From 25e7debe9f5ef751379a47a21d9363f4ca7ac4e2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 23 May 2024 19:57:22 -0400 Subject: [PATCH 01/42] Start implementing Configurate config system --- bootstrap/spigot/build.gradle.kts | 2 + .../platform/spigot/GeyserSpigotPlugin.java | 3 + .../standalone/GeyserStandaloneBootstrap.java | 11 +- core/build.gradle.kts | 3 + .../java/org/geysermc/geyser/Constants.java | 2 + .../configuration/ConfigLoaderTemp.java | 87 +++++ .../geyser/configuration/GeyserConfig.java | 328 ++++++++++++++++++ .../configuration/GeyserPluginConfig.java | 92 +++++ .../configuration/GeyserRemoteConfig.java | 67 ++++ gradle/libs.versions.toml | 5 + 10 files changed, 594 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 8143d96a187..e7503b964d7 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -28,6 +28,8 @@ platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLo platformRelocate("org.objectweb.asm") platformRelocate("me.lucko.commodore") platformRelocate("org.yaml") // Broken as of 1.20 +platformRelocate("org.spongepowered") +platformRelocate("io.leangen.geantyref") // These dependencies are already present on the platform provided(libs.viaversion) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index e33de5f9b2c..293b0ce3f45 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -52,7 +52,9 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.network.GameProtocol; @@ -485,6 +487,7 @@ private boolean loadConfig() { File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); + ConfigLoaderTemp.load(GeyserPluginConfig.class); } catch (IOException ex) { getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 039004867d7..a2b701665dc 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -43,8 +43,10 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; +import org.geysermc.geyser.configuration.GeyserRemoteConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -59,12 +61,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; public class GeyserStandaloneBootstrap implements GeyserBootstrap { @@ -201,6 +198,8 @@ public void onGeyserEnable() { (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); + ConfigLoaderTemp.load(GeyserRemoteConfig.class); + handleArgsConfigOptions(); if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 42d4e13c5ac..c8bc432dfcf 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,6 +15,9 @@ dependencies { // Jackson JSON and YAML serialization api(libs.bundles.jackson) + annotationProcessor(libs.configurate.`interface`.ap) + api(libs.configurate.`interface`) + implementation(libs.configurate.yaml) api(libs.guava) // Fastutil Maps diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java index 5de8e6e6b5e..ab17084a334 100644 --- a/core/src/main/java/org/geysermc/geyser/Constants.java +++ b/core/src/main/java/org/geysermc/geyser/Constants.java @@ -45,6 +45,8 @@ public final class Constants { public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/"; + public static final int CONFIG_VERSION = 5; + static { URI wsUri = null; try { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java new file mode 100644 index 00000000000..161b1aff024 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ConfigLoaderTemp { + private static final String HEADER = """ + -------------------------------- + Geyser Configuration File + + A bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition. + + GitHub: https://github.com/GeyserMC/Geyser + Discord: https://discord.gg/geysermc + Wiki: https://wiki.geysermc.org/ + + NOTICE: See https://wiki.geysermc.org/geyser/setup/ for the setup guide. Many video tutorials are outdated. + In most cases, especially with server hosting providers, further hosting-specific configuration is required. + --------------------------------"""; + + public static T load(Class configClass) throws IOException { + var loader = YamlConfigurationLoader.builder() + .file(new File("newconfig.yml")) + .defaultOptions(InterfaceDefaultOptions.get() + .header(HEADER)) + .build(); + ConfigurationNode node = loader.load(); + // temp fix for node.virtual() being broken + var virtual = !Files.exists(Path.of("newconfig.yml")); + + // TODO needed or else Configurate breaks + var migrations = ConfigurationTransformation.versionedBuilder() + // Pre-Configurate + .addVersion(5, ConfigurationTransformation.builder() + .addAction(NodePath.path("legacyPingPassthrough"), (path, value) -> { + // Invert value + value.set(Boolean.FALSE.equals(value.get(boolean.class))); + return new Object[]{"integratedPingPassthrough"}; + }) + .addAction(NodePath.path("remote"), (path, value) -> + new Object[]{"java"}) + .build()) + .build(); + + migrations.apply(node); + + T config = node.get(configClass); + System.out.println(config); + + loader.save(node); + + return config; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java new file mode 100644 index 00000000000..03068bb2527 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.Constants; +import org.geysermc.geyser.api.network.AuthType; +import org.geysermc.geyser.api.network.BedrockListener; +import org.geysermc.geyser.api.network.RemoteServer; +import org.geysermc.geyser.network.GameProtocol; +import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; +import org.spongepowered.configurate.interfaces.meta.range.NumericRange; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; + +import java.nio.file.Path; +import java.util.List; +import java.util.UUID; + +public interface GeyserConfig { + BedrockConfig bedrock(); + + JavaConfig java(); + + Path floodgateKeyPath(); + + @Comment(""" + For online mode authentication type only. + Stores a list of Bedrock players that should have their Java Edition account saved after login. + This saves a token that can be reused to authenticate the player later. This does not save emails or passwords, + but you should still be cautious when adding to this list and giving others access to this Geyser instance's files. + Removing a name from this list will delete its cached login information on the next Geyser startup. + The file that tokens will be saved in is in the same folder as this config, named "saved-refresh-tokens.json".""") + default List savedUserLogins() { + return List.of("ThisExampleUsernameShouldBeLongEnoughToNeverBeAnXboxUsername", + "ThisOtherExampleUsernameShouldAlsoBeLongEnough"); + } + + @Comment(""" + Specify how many seconds to wait while user authorizes Geyser to access their Microsoft account. + User is allowed to disconnect from the server during this period.""") + @DefaultNumeric(128) + int pendingAuthenticationTimeout(); + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + @Comment(""" + Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands. + Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients.""") + @DefaultBoolean(true) + boolean commandSuggestions(); + + @Comment("Relay the MOTD from the Java server to Bedrock players.") + @DefaultBoolean(true) + boolean passthroughMotd(); + + @Comment("Relay the player count and max players from the Java server to Bedrock players.") + @DefaultBoolean(true) + boolean passthroughPlayerCounts(); + + @Comment("How often to ping the Java server to refresh MOTD and player count, in seconds.") + @DefaultNumeric(3) + int pingPassthroughInterval(); + + @Comment(""" + Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate + ping, it may also cause players to time out more easily.""") + boolean forwardPlayerPing(); + + @Comment(""" + Maximum amount of players that can connect. + This is only visual, and is only applied if passthrough-motd is disabled.""") + @DefaultNumeric(100) + int maxPlayers(); + + @Comment("If debug messages should be sent through console") + boolean debugMode(); + + @Comment(""" + Allow a fake cooldown indicator to be sent. Bedrock players otherwise do not see a cooldown as they still use 1.8 combat. + Please note: if the cooldown is enabled, some users may see a black box during the cooldown sequence, like below: + https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png + This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 + This setting can be set to "title", "actionbar" or "false\"""") + @DefaultString("title") + String showCooldown(); + + @Comment("Controls if coordinates are shown to players.") + @DefaultBoolean(true) + boolean showCoordinates(); + + @Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.") + boolean disableBedrockScaffolding(); + + //@DefaultString("disabled") + EmoteOffhandWorkaroundOption emoteOffhandWorkaround(); + + String defaultLocale(); + + @Comment(""" + Specify how many days images will be cached to disk to save downloading them from the internet. + A value of 0 is disabled. (Default: 0)""") + int cacheImages(); + + @Comment("Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices.") + @DefaultBoolean(true) + boolean allowCustomSkulls(); + + @Comment(""" + The maximum number of custom skulls to be displayed per player. Increasing this may decrease performance on weaker devices. + Setting this to -1 will cause all custom skulls to be displayed regardless of distance or number.""") + @DefaultNumeric(128) + int maxVisibleCustomSkulls(); + + @Comment("The radius in blocks around the player in which custom skulls are displayed.") + @DefaultNumeric(32) + int customSkullRenderDistance(); + + @Comment(""" + Whether to add any items and blocks which normally does not exist in Bedrock Edition. + This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching. + If this is disabled, furnace minecart items will be mapped to hopper minecart items. + Geyser's block, item, and skull mappings systems will also be disabled. + This option requires a restart of Geyser in order to change its setting.""") + @DefaultBoolean(true) + boolean addNonBedrockItems(); + + @Comment(""" + Bedrock prevents building and displaying blocks above Y127 in the Nether. + This config option works around that by changing the Nether dimension ID to the End ID. + The main downside to this is that the entire Nether will have the same red fog rather than having different fog for each biome.""") + boolean aboveBedrockNetherBuilding(); + + @Comment(""" + Force clients to load all resource packs if there are any. + If set to false, it allows the user to connect to the server even if they don't + want to download the resource packs.""") + @DefaultBoolean(true) + boolean forceResourcePacks(); + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + @Comment(""" + Allows Xbox achievements to be unlocked. + THIS DISABLES ALL COMMANDS FROM SUCCESSFULLY RUNNING FOR BEDROCK IN-GAME, as otherwise Bedrock thinks you are cheating.""") + boolean xboxAchievementsEnabled(); + + @Comment("Whether player IP addresses will be logged by the server.") + @DefaultBoolean(true) + boolean logPlayerIpAddresses(); + + @Comment(""" + Whether to alert the console and operators that a new Geyser version is available that supports a Bedrock version + that this Geyser version does not support. It's recommended to keep this option enabled, as many Bedrock platforms + auto-update.""") + @DefaultBoolean(true) + boolean notifyOnNewBedrockUpdate(); + + @Comment(""" + Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, + or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.""") + @DefaultString("minecraft:barrier") + String unusableSpaceBlock(); + + @Comment(""" + bStats is a stat tracker that is entirely anonymous and tracks only basic information + about Geyser, such as how many people are online, how many servers are using Geyser, + what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. + https://bstats.org/plugin/server-implementation/GeyserMC""") + MetricsInfo metrics(); + + @ConfigSerializable + interface BedrockConfig extends BedrockListener { + @Override + @Comment(""" + The IP address that will listen for connections. + Generally, you should only uncomment and change this if you want to limit what IPs can connect to your server.""") + @NonNull + @DefaultString("0.0.0.0") + String address(); + + @Override + @Comment("The port that will listen for connections") + @DefaultNumeric(19132) + int port(); + + @Override + @Comment(""" + The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. + DO NOT uncomment and change this unless Geyser runs on a different internal port than the one that is used to connect.""") + @DefaultNumeric(19132) + int broadcastPort(); + + void address(String address); + + void port(int port); + + void broadcastPort(int broadcastPort); + + @Override + @DefaultString("Geyser") + String primaryMotd(); + + @Override + @DefaultString("Another Geyser server.") // TODO migrate or change name + String secondaryMotd(); + + @Comment(""" + How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but + the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable.""") + @DefaultNumeric(6) + @NumericRange(from = -1, to = 9) + int compressionLevel(); + + @DefaultBoolean + boolean enableProxyProtocol(); + + @Comment(""" + A list of allowed PROXY protocol speaking proxy IP addresses/subnets. Only effective when "enable-proxy-protocol" is enabled, and + should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.). + Keeping this list empty means there is no IP address whitelist. + IP addresses, subnets, and links to plain text files are supported.""") + List proxyProtocolWhitelistedIPs(); + +// /** +// * @return Unmodifiable list of {@link CIDRMatcher}s from {@link #proxyProtocolWhitelistedIPs()} +// */ +// @Exclude +// List whitelistedIPsMatchers(); + } + + @ConfigSerializable + interface JavaConfig extends RemoteServer { + + void address(String address); + + void port(int port); + + @Comment(""" + Whether to enable PROXY protocol or not while connecting to the server. + This is useful only when: + 1) Your server supports PROXY protocol (it probably doesn't) + 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config. + IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT!""") + boolean useProxyProtocol(); + + boolean forwardHostname(); + + @Override + @Exclude + default String minecraftVersion() { + return GameProtocol.getJavaMinecraftVersion(); + } + + @Override + @Exclude + default int protocolVersion() { + return GameProtocol.getJavaProtocolVersion(); + } + + @Override + @Exclude + default boolean resolveSrv() { + return false; + } + + void authType(AuthType authType); + } + + @ConfigSerializable + interface MetricsInfo { + + @DefaultBoolean(true) + boolean enabled(); + + default UUID uniqueId() { //TODO rename? + return UUID.randomUUID(); + } + } + + @Comment(""" + Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle + a lot of scoreboard packets per second can cause serious lag. + This option allows you to specify after how many Scoreboard packets per seconds + the Scoreboard updates will be limited to four updates per second.""") + @DefaultNumeric(20) + int scoreboardPacketThreshold(); + + @Comment(""" + Allow connections from ProxyPass and Waterdog. + See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP.""") + // if u have offline mode enabled pls be safe + boolean enableProxyConnections(); + + @Comment(""" + The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation. + 1400 is the default.""") + @DefaultNumeric(1400) + int mtu(); + + default int version() { + return Constants.CONFIG_VERSION; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java new file mode 100644 index 00000000000..b7c0fd9b6ea --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.geysermc.geyser.GeyserImpl; +import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.spongepowered.configurate.interfaces.meta.Hidden; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; + +@ConfigSerializable +public interface GeyserPluginConfig extends GeyserConfig { + @Override + IntegratedBedrockConfig bedrock(); + + @Override + IntegratedJavaConfig java(); + + @ConfigSerializable + interface IntegratedBedrockConfig extends BedrockConfig { + @Comment(""" + Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. + This option makes the Bedrock port the same as the Java port every time you start the server.""") + @DefaultBoolean + boolean cloneRemotePort(); + } + + @ConfigSerializable + interface IntegratedJavaConfig extends JavaConfig { + @Override + @Exclude + default String address() { + return GeyserImpl.getInstance().getBootstrap().getServerBindAddress(); + } + + @Override + @Exclude + default int port() { + return GeyserImpl.getInstance().getBootstrap().getServerPort(); + } + + @Override + @Exclude + default boolean forwardHostname() { + return true; // No need to worry about suspicious behavior flagging the server. + } + } + + @Comment(""" + Use server API methods to determine the Java server's MOTD and ping passthrough. + There is no need to disable this unless your MOTD or player count does not appear properly.""") + @DefaultBoolean(true) + boolean integratedPingPassthrough(); + + @Comment(""" + How often to ping the Java server to refresh MOTD and player count, in seconds. + Only relevant if integrated-ping-passthrough is disabled.""") + @Override + int pingPassthroughInterval(); + + @Hidden + @DefaultBoolean(true) + boolean useDirectConnection(); + + @Hidden + @DefaultBoolean(true) + boolean disableCompression(); +} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java new file mode 100644 index 00000000000..603a675af8c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; + +/** + * Used for any instance where the Java server is detached from Geyser. + */ +@ConfigSerializable +public interface GeyserRemoteConfig extends GeyserConfig { + + @Override + RemoteConfig java(); + + @ConfigSerializable + interface RemoteConfig extends JavaConfig { + @Override + @Comment("The IP address of the Java Edition server.") + @DefaultString("127.0.0.1") + String address(); + + @Override + @Comment("The port of the Java Edition server.") + @DefaultNumeric(25565) + int port(); + + @Override + @Exclude + default int protocolVersion() { + return JavaConfig.super.protocolVersion(); + } + + @Override + @Comment(""" + Forward the hostname that the Bedrock client used to connect over to the Java server + This is designed to be used for forced hosts on proxies""") + boolean forwardHostname(); + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dee170705b5..962b4a35547 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] base-api = "1.0.0-SNAPSHOT" cumulus = "1.1.2" +configurate = "4.2.0-GeyserMC-SNAPSHOT" erosion = "1.0-20230406.174837-8" events = "1.1-SNAPSHOT" jackson = "2.17.0" @@ -58,6 +59,10 @@ jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-an jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" } +configurate-interface-ap = { module = "org.spongepowered:configurate-extra-interface-ap", version.ref = "configurate" } +configurate-interface = { module = "org.spongepowered:configurate-extra-interface", version.ref = "configurate" } +configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" } + fastutil-int-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-int-maps", version.ref = "fastutil" } fastutil-int-long-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-long-maps", version.ref = "fastutil" } fastutil-int-byte-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-byte-maps", version.ref = "fastutil" } From a135d3c4da45989fe655effb81209d7b38c67b30 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 26 May 2024 22:20:53 -0400 Subject: [PATCH 02/42] More development --- .../geyser/configuration/GeyserConfig.java | 10 ++++++++++ .../configuration/GeyserPluginConfig.java | 19 +++++++++++++++++++ .../configuration/GeyserRemoteConfig.java | 12 +++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 03068bb2527..b50d172e0eb 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -260,6 +260,15 @@ interface JavaConfig extends RemoteServer { void port(int port); + @Override + @Comment(""" + What type of authentication Bedrock players will be checked against when logging into the Java server. + Can be floodgate (see https://wiki.geysermc.org/floodgate/), online, or offline.""") + @NonNull + default AuthType authType() { + return AuthType.ONLINE; + } + @Comment(""" Whether to enable PROXY protocol or not while connecting to the server. This is useful only when: @@ -322,6 +331,7 @@ default UUID uniqueId() { //TODO rename? @DefaultNumeric(1400) int mtu(); + @Comment("Do not change!") default int version() { return Constants.CONFIG_VERSION; } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index b7c0fd9b6ea..0676f0e8cbb 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -57,12 +57,31 @@ default String address() { return GeyserImpl.getInstance().getBootstrap().getServerBindAddress(); } + @Override + default void address(String address) { + throw new IllegalStateException(); + } + @Override @Exclude default int port() { return GeyserImpl.getInstance().getBootstrap().getServerPort(); } + @Override + default void port(int port) { + throw new IllegalStateException(); + } + +// @Nonnull +// @Comment(""" +// What type of authentication Bedrock players will be checked against when logging into the Java server. +// Floodgate allows Bedrock players to join without needing a Java account. It's not recommended to change this.""") +// @Override +// default AuthType authType() { +// return AuthType.FLOODGATE; +// } + @Override @Exclude default boolean forwardHostname() { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index 603a675af8c..e44fdedd0a7 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -25,7 +25,8 @@ package org.geysermc.geyser.configuration; -import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.network.AuthType; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; import org.spongepowered.configurate.objectmapping.ConfigSerializable; @@ -36,6 +37,8 @@ */ @ConfigSerializable public interface GeyserRemoteConfig extends GeyserConfig { +// @Override // For config placement +// BedrockConfig bedrock(); @Override RemoteConfig java(); @@ -52,10 +55,9 @@ interface RemoteConfig extends JavaConfig { @DefaultNumeric(25565) int port(); - @Override - @Exclude - default int protocolVersion() { - return JavaConfig.super.protocolVersion(); + @Override // For config placement + default @NonNull AuthType authType() { + return JavaConfig.super.authType(); } @Override From 25a755fd03dcb532f31a1dde1a109017d1dcac8e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 27 May 2024 19:48:35 -0400 Subject: [PATCH 03/42] Start migrating to Gson only --- bootstrap/standalone/build.gradle.kts | 2 + .../java/org/geysermc/geyser/GeyserImpl.java | 20 ++++++---- .../defaults/ConnectionTestCommand.java | 27 +++++++------- .../configuration/ConfigLoaderTemp.java | 3 ++ .../org/geysermc/geyser/dump/DumpInfo.java | 7 ++-- .../loader/BiomeIdentifierRegistryLoader.java | 12 +++--- .../registry/loader/EffectRegistryLoader.java | 25 ++++--------- .../loader/EnchantmentRegistryLoader.java | 33 +++++++++-------- .../loader/ParticleTypesRegistryLoader.java | 23 +++++------- .../loader/SoundEventsRegistryLoader.java | 37 +++++++++---------- .../registry/loader/SoundRegistryLoader.java | 28 +++++++------- .../geysermc/geyser/skin/SkinProvider.java | 22 +++++------ .../geyser/util/VersionCheckUtils.java | 10 ++--- .../org/geysermc/geyser/util/WebUtils.java | 9 ++++- gradle/libs.versions.toml | 4 +- 15 files changed, 135 insertions(+), 127 deletions(-) diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts index eaf8951086f..61460ce0f6a 100644 --- a/bootstrap/standalone/build.gradle.kts +++ b/bootstrap/standalone/build.gradle.kts @@ -14,6 +14,8 @@ dependencies { implementation(libs.bundles.jline) implementation(libs.bundles.log4j) + + implementation(libs.gson.runtime) } application { diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index d5635acf9d4..8afe2d9e672 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -26,9 +26,11 @@ package org.geysermc.geyser; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import io.netty.channel.epoll.Epoll; import io.netty.util.NettyRuntime; import io.netty.util.concurrent.DefaultThreadFactory; @@ -87,8 +89,10 @@ import org.geysermc.mcprotocollib.network.tcp.TcpSession; import java.io.File; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.lang.reflect.Type; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -112,6 +116,8 @@ public class GeyserImpl implements GeyserApi { .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); + public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final String NAME = "Geyser"; public static final String GIT_VERSION = "${gitVersion}"; public static final String VERSION = "${version}"; @@ -527,11 +533,11 @@ private void startInstance() { File tokensFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile(); if (tokensFile.exists()) { - TypeReference> type = new TypeReference<>() { }; + Type type = new TypeToken>() { }.getType(); Map refreshTokenFile = null; - try { - refreshTokenFile = JSON_MAPPER.readValue(tokensFile, type); + try (FileReader reader = new FileReader(tokensFile)) { + refreshTokenFile = GSON.fromJson(reader, type); } catch (IOException e) { logger.error("Cannot load saved user tokens!", e); } @@ -824,11 +830,9 @@ private void scheduleRefreshTokensWrite() { scheduledThread.execute(() -> { // Ensure all writes are handled on the same thread File savedTokens = getBootstrap().getSavedUserLoginsFolder().resolve(Constants.SAVED_REFRESH_TOKEN_FILE).toFile(); - TypeReference> type = new TypeReference<>() { }; + Type type = new TypeToken>() { }.getType(); try (FileWriter writer = new FileWriter(savedTokens)) { - JSON_MAPPER.writerFor(type) - .withDefaultPrettyPrinter() - .writeValue(writer, savedRefreshTokens); + GSON.toJson(savedRefreshTokens, type, writer); } catch (IOException e) { getLogger().error("Unable to write saved refresh tokens!", e); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index 981c9759511..84da7323942 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -25,7 +25,8 @@ package org.geysermc.geyser.command.defaults; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; @@ -179,7 +180,7 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, CONNECTION_TEST_MOTD = connectionTestMotd; sender.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait..."); - JsonNode output; + JsonObject output; try { String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8); output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + hostname + "&port=" + port); @@ -187,18 +188,18 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, CONNECTION_TEST_MOTD = null; } - if (output.get("success").asBoolean()) { - JsonNode cache = output.get("cache"); + if (output.get("success").getAsBoolean()) { + JsonObject cache = output.getAsJsonObject("cache"); String when; - if (cache.get("fromCache").asBoolean()) { - when = cache.get("secondsSince").asInt() + " seconds ago"; + if (cache.get("fromCache").isJsonPrimitive()) { + when = cache.get("secondsSince").getAsBoolean() + " seconds ago"; } else { when = "now"; } - JsonNode ping = output.get("ping"); - JsonNode pong = ping.get("pong"); - String remoteMotd = pong.get("motd").asText(); + JsonObject ping = output.getAsJsonObject("ping"); + JsonObject pong = ping.getAsJsonObject("pong"); + String remoteMotd = pong.get("motd").getAsString(); if (!connectionTestMotd.equals(remoteMotd)) { sender.sendMessage("The MOTD did not match when we pinged the server (we got '" + remoteMotd + "'). " + "Did you supply the correct IP and port of your server?"); @@ -206,7 +207,7 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, return; } - if (ping.get("tcpFirst").asBoolean()) { + if (ping.get("tcpFirst").getAsBoolean()) { sender.sendMessage("Your server hardware likely has some sort of firewall preventing people from joining easily. See https://geysermc.link/ovh-firewall for more information."); sendLinks(sender); return; @@ -218,9 +219,9 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, } sender.sendMessage("Your server is likely unreachable from outside the network!"); - JsonNode message = output.get("message"); - if (message != null && !message.asText().isEmpty()) { - sender.sendMessage("Got the error message: " + message.asText()); + JsonElement message = output.get("message"); + if (message != null && !message.getAsString().isEmpty()) { + sender.sendMessage("Got the error message: " + message.getAsString()); } sendLinks(sender); } catch (Exception e) { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java index 161b1aff024..98c6d27a69e 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java @@ -52,6 +52,9 @@ public class ConfigLoaderTemp { --------------------------------"""; public static T load(Class configClass) throws IOException { + if (true) { + return null; // For now + } var loader = YamlConfigurationLoader.builder() .file(new File("newconfig.yml")) .defaultOptions(InterfaceDefaultOptions.get() diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 6989dc10a7b..bd8d85440fc 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -27,10 +27,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; import com.google.common.io.Files; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; @@ -249,9 +250,9 @@ public LogsInfo() { Map fields = new HashMap<>(); fields.put("content", FileUtils.readAllLines(GeyserImpl.getInstance().getBootstrap().getLogsPath()).collect(Collectors.joining("\n"))); - JsonNode logData = GeyserImpl.JSON_MAPPER.readTree(WebUtils.postForm("https://api.mclo.gs/1/log", fields)); + JsonObject logData = new JsonParser().parse(WebUtils.postForm("https://api.mclo.gs/1/log", fields)).getAsJsonObject(); - this.link = logData.get("url").textValue(); + this.link = logData.get("url").getAsString(); } catch (IOException ignored) { } } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java index 3205004e438..eb091f273de 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java @@ -25,14 +25,16 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.type.TypeReference; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.geyser.GeyserImpl; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; import java.util.Map; public class BiomeIdentifierRegistryLoader implements RegistryLoader> { @@ -44,11 +46,11 @@ public Object2IntMap load(String input) { // The server sends the corresponding Java network IDs, so we don't need to worry about that now. // Reference variable for Jackson to read off of - TypeReference> biomeEntriesType = new TypeReference<>() { }; + Type biomeEntriesType = new TypeToken>() { }.getType(); Map biomeEntries; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/biomes.json")) { - biomeEntries = GeyserImpl.JSON_MAPPER.readValue(stream, biomeEntriesType); + biomeEntries = GeyserImpl.GSON.fromJson(new InputStreamReader(stream), biomeEntriesType); } catch (IOException e) { throw new AssertionError("Unable to load Bedrock runtime biomes", e); } @@ -66,7 +68,7 @@ private static class BiomeEntry { /** * The Bedrock network ID for this biome. */ - @JsonProperty("bedrock_id") + @SerializedName("bedrock_id") private int bedrockId; } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/EffectRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/EffectRegistryLoader.java index aa95c1d563c..e6d8df39bd0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/EffectRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/EffectRegistryLoader.java @@ -25,12 +25,12 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.geysermc.geyser.GeyserImpl; import java.io.InputStream; -import java.util.Map; -import java.util.WeakHashMap; +import java.io.InputStreamReader; /** * An abstract registry loader for loading effects from a resource path. @@ -38,21 +38,12 @@ * @param the value */ public abstract class EffectRegistryLoader implements RegistryLoader { - private static final Map loadedFiles = new WeakHashMap<>(); - public void loadFile(String input) { - if (!loadedFiles.containsKey(input)) { - JsonNode effects; - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) { - effects = GeyserImpl.JSON_MAPPER.readTree(stream); - } catch (Exception e) { - throw new AssertionError("Unable to load registrations for " + input, e); - } - loadedFiles.put(input, effects); + public JsonObject loadFile(String input) { + try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) { + return new JsonParser().parse(new InputStreamReader(stream)).getAsJsonObject(); + } catch (Exception e) { + throw new AssertionError("Unable to load registrations for " + input, e); } } - - public JsonNode get(String input) { - return loadedFiles.get(input); - } } \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java index 8a0fb1f401b..c1a50de9617 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/EnchantmentRegistryLoader.java @@ -25,7 +25,10 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import org.geysermc.geyser.GeyserImpl; @@ -35,41 +38,39 @@ import org.geysermc.geyser.registry.type.EnchantmentData; import java.io.InputStream; +import java.io.InputStreamReader; import java.util.EnumMap; import java.util.EnumSet; -import java.util.Iterator; import java.util.Map; public class EnchantmentRegistryLoader implements RegistryLoader> { @Override public Map load(String input) { - JsonNode enchantmentsNode; + JsonObject enchantmentsJson; try (InputStream enchantmentsStream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) { - enchantmentsNode = GeyserImpl.JSON_MAPPER.readTree(enchantmentsStream); + enchantmentsJson = new JsonParser().parse(new InputStreamReader(enchantmentsStream)).getAsJsonObject(); } catch (Exception e) { throw new AssertionError("Unable to load enchantment data", e); } Map enchantments = new EnumMap<>(JavaEnchantment.class); - Iterator> it = enchantmentsNode.fields(); - while (it.hasNext()) { - Map.Entry entry = it.next(); + for (Map.Entry entry : enchantmentsJson.entrySet()) { JavaEnchantment key = JavaEnchantment.getByJavaIdentifier(entry.getKey()); - JsonNode node = entry.getValue(); - int rarityMultiplier = node.get("anvil_cost").asInt(); - int maxLevel = node.get("max_level").asInt(); + JsonObject node = entry.getValue().getAsJsonObject(); + int rarityMultiplier = node.get("anvil_cost").getAsInt(); + int maxLevel = node.get("max_level").getAsInt(); EnumSet incompatibleEnchantments = EnumSet.noneOf(JavaEnchantment.class); - JsonNode incompatibleEnchantmentsNode = node.get("incompatible_enchantments"); - if (incompatibleEnchantmentsNode != null) { - for (JsonNode incompatibleNode : incompatibleEnchantmentsNode) { - incompatibleEnchantments.add(JavaEnchantment.getByJavaIdentifier(incompatibleNode.textValue())); + JsonArray incompatibleEnchantmentsJson = node.getAsJsonArray("incompatible_enchantments"); + if (incompatibleEnchantmentsJson != null) { + for (JsonElement incompatibleNode : incompatibleEnchantmentsJson) { + incompatibleEnchantments.add(JavaEnchantment.getByJavaIdentifier(incompatibleNode.getAsString())); } } IntSet validItems = new IntOpenHashSet(); - for (JsonNode itemNode : node.get("valid_items")) { - String javaIdentifier = itemNode.textValue(); + for (JsonElement itemNode : node.getAsJsonArray("valid_items")) { + String javaIdentifier = itemNode.getAsString(); Item item = Registries.JAVA_ITEM_IDENTIFIERS.get(javaIdentifier); if (item != null) { validItems.add(item.javaId()); diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ParticleTypesRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ParticleTypesRegistryLoader.java index a09d6d6d3a1..42e34a2d59b 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ParticleTypesRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ParticleTypesRegistryLoader.java @@ -25,15 +25,15 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.databind.JsonNode; -import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.LevelEventType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.registry.type.ParticleMapping; +import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; -import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -44,16 +44,13 @@ public class ParticleTypesRegistryLoader extends EffectRegistryLoader load(String input) { - this.loadFile(input); - - Iterator> particlesIterator = this.get(input).fields(); + JsonObject particlesJson = this.loadFile(input); Map particles = new Object2ObjectOpenHashMap<>(); try { - while (particlesIterator.hasNext()) { - Map.Entry entry = particlesIterator.next(); + for (Map.Entry entry : particlesJson.entrySet()) { String key = entry.getKey().toUpperCase(Locale.ROOT); - JsonNode bedrockId = entry.getValue().get("bedrockId"); - JsonNode eventType = entry.getValue().get("eventType"); + JsonElement bedrockId = entry.getValue().getAsJsonObject().get("bedrockId"); + JsonElement eventType = entry.getValue().getAsJsonObject().get("eventType"); if (eventType == null && bedrockId == null) { GeyserImpl.getInstance().getLogger().debug("Skipping particle mapping " + key + " because no Bedrock equivalent exists."); continue; @@ -63,16 +60,16 @@ public Map load(String input) { if (eventType != null) { try { // Check if we have a particle type mapping - type = org.cloudburstmc.protocol.bedrock.data.ParticleType.valueOf(eventType.asText().toUpperCase(Locale.ROOT)); + type = org.cloudburstmc.protocol.bedrock.data.ParticleType.valueOf(eventType.getAsString().toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException ex) { // No particle type; try level event - type = LevelEvent.valueOf(eventType.asText().toUpperCase(Locale.ROOT)); + type = LevelEvent.valueOf(eventType.getAsString().toUpperCase(Locale.ROOT)); } } particles.put(ParticleType.valueOf(key), new ParticleMapping( type, - bedrockId == null ? null : bedrockId.asText()) + bedrockId == null ? null : bedrockId.getAsString()) ); } } catch (Exception e) { diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundEventsRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundEventsRegistryLoader.java index 4e3fbe40a51..441a74f48aa 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundEventsRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundEventsRegistryLoader.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.databind.JsonNode; -import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.cloudburstmc.protocol.bedrock.data.LevelEventType; import org.geysermc.geyser.GeyserImpl; @@ -34,8 +34,8 @@ import org.geysermc.geyser.translator.level.event.PlaySoundEventTranslator; import org.geysermc.geyser.translator.level.event.SoundEventEventTranslator; import org.geysermc.geyser.translator.level.event.SoundLevelEventTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent; -import java.util.Iterator; import java.util.Map; /** @@ -47,37 +47,36 @@ public class SoundEventsRegistryLoader extends EffectRegistryLoader load(String input) { this.loadFile(input); - Iterator> effectsIterator = this.get(input).fields(); + JsonObject effectsJson = this.loadFile(input); Map soundEffects = new Object2ObjectOpenHashMap<>(); - while (effectsIterator.hasNext()) { - Map.Entry entry = effectsIterator.next(); - JsonNode node = entry.getValue(); + for (Map.Entry entry : effectsJson.entrySet()) { + JsonObject node = entry.getValue().getAsJsonObject(); try { - String type = node.get("type").asText(); + String type = node.get("type").getAsString(); LevelEvent javaEffect = null; LevelEventTranslator transformer = null; switch (type) { case "soundLevel" -> { javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey()); - LevelEventType levelEventType = org.cloudburstmc.protocol.bedrock.data.LevelEvent.valueOf(node.get("name").asText()); - int data = node.has("data") ? node.get("data").intValue() : 0; + LevelEventType levelEventType = org.cloudburstmc.protocol.bedrock.data.LevelEvent.valueOf(node.get("name").getAsString()); + int data = node.has("data") ? node.get("data").getAsInt() : 0; transformer = new SoundLevelEventTranslator(levelEventType, data); } case "soundEvent" -> { javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey()); - org.cloudburstmc.protocol.bedrock.data.SoundEvent soundEvent = org.cloudburstmc.protocol.bedrock.data.SoundEvent.valueOf(node.get("name").asText()); - String identifier = node.has("identifier") ? node.get("identifier").asText() : ""; - int extraData = node.has("extraData") ? node.get("extraData").intValue() : -1; + org.cloudburstmc.protocol.bedrock.data.SoundEvent soundEvent = org.cloudburstmc.protocol.bedrock.data.SoundEvent.valueOf(node.get("name").getAsString()); + String identifier = node.has("identifier") ? node.get("identifier").getAsString() : ""; + int extraData = node.has("extraData") ? node.get("extraData").getAsInt() : -1; transformer = new SoundEventEventTranslator(soundEvent, identifier, extraData); } case "playSound" -> { javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey()); - String name = node.get("name").asText(); - float volume = node.has("volume") ? node.get("volume").floatValue() : 1.0f; - boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").booleanValue(); - float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").floatValue() : 1.0f; - float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").floatValue() : 0.0f; - boolean relative = !node.has("relative") || node.get("relative").booleanValue(); + String name = node.get("name").getAsString(); + float volume = node.has("volume") ? node.get("volume").getAsFloat() : 1.0f; + boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").getAsBoolean(); + float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").getAsFloat() : 1.0f; + float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").getAsFloat() : 0.0f; + boolean relative = !node.has("relative") || node.get("relative").getAsBoolean(); transformer = new PlaySoundEventTranslator(name, volume, pitchSub, pitchMul, pitchAdd, relative); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundRegistryLoader.java index 318cc08d704..1212a9a1888 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/SoundRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/SoundRegistryLoader.java @@ -25,14 +25,16 @@ package org.geysermc.geyser.registry.loader; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.registry.type.SoundMapping; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; /** @@ -42,26 +44,24 @@ public class SoundRegistryLoader implements RegistryLoader load(String input) { - JsonNode soundsTree; + JsonObject soundsJson; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) { - soundsTree = GeyserImpl.JSON_MAPPER.readTree(stream); + soundsJson = new JsonParser().parse(new InputStreamReader(stream)).getAsJsonObject(); } catch (IOException e) { throw new AssertionError("Unable to load sound mappings", e); } Map soundMappings = new HashMap<>(); - Iterator> soundsIterator = soundsTree.fields(); - while(soundsIterator.hasNext()) { - Map.Entry next = soundsIterator.next(); - JsonNode brMap = next.getValue(); - String javaSound = next.getKey(); + for (Map.Entry entry : soundsJson.entrySet()) { + JsonObject brMap = entry.getValue().getAsJsonObject(); + String javaSound = entry.getKey(); soundMappings.put(javaSound, new SoundMapping( javaSound, - brMap.has("bedrock_mapping") && brMap.get("bedrock_mapping").isTextual() ? brMap.get("bedrock_mapping").asText() : null, - brMap.has("playsound_mapping") && brMap.get("playsound_mapping").isTextual() ? brMap.get("playsound_mapping").asText() : null, - brMap.has("extra_data") && brMap.get("extra_data").isInt() ? brMap.get("extra_data").asInt() : -1, - brMap.has("identifier") && brMap.get("identifier").isTextual() ? brMap.get("identifier").asText() : null, - brMap.has("level_event") && brMap.get("level_event").isBoolean() && brMap.get("level_event").asBoolean() + brMap.has("bedrock_mapping") ? brMap.get("bedrock_mapping").getAsString() : null, + brMap.has("playsound_mapping") ? brMap.get("playsound_mapping").getAsString() : null, + brMap.has("extra_data") ? brMap.get("extra_data").getAsInt() : -1, + brMap.has("identifier") ? brMap.get("identifier").getAsString() : null, + brMap.has("level_event") && brMap.get("level_event").getAsBoolean() ) ); } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 5b16bc3a367..16a939304d5 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -25,13 +25,11 @@ package org.geysermc.geyser.skin; -import com.fasterxml.jackson.databind.JsonNode; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.bytes.ByteArrays; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; @@ -56,7 +54,9 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; import java.util.concurrent.*; import java.util.function.Predicate; @@ -486,13 +486,13 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E public static CompletableFuture<@Nullable String> requestTexturesFromUUID(String uuid) { return CompletableFuture.supplyAsync(() -> { try { - JsonNode node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); - JsonNode properties = node.get("properties"); + JsonObject node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); + JsonObject properties = node.getAsJsonObject("properties"); if (properties == null) { GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuid); return null; } - return node.get("properties").get(0).get("value").asText(); + return node.getAsJsonArray("properties").get(0).getAsJsonObject().get("value").getAsString(); } catch (Exception e) { GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { @@ -513,13 +513,13 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E return CompletableFuture.supplyAsync(() -> { try { // Offline skin, or no present UUID - JsonNode node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); - JsonNode id = node.get("id"); + JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); + JsonElement id = node.get("id"); if (id == null) { GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username); return null; } - return id.asText(); + return id.getAsString(); } catch (Exception e) { if (GeyserImpl.getInstance().getConfig().isDebugMode()) { e.printStackTrace(); diff --git a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java index cf90a6bcdc0..91e7fcbdc01 100644 --- a/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/VersionCheckUtils.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.event.ClickEvent; @@ -92,9 +92,9 @@ public static void checkForOutdatedJava(GeyserLogger logger) { public static void checkForGeyserUpdate(Supplier recipient) { CompletableFuture.runAsync(() -> { try { - JsonNode json = WebUtils.getJson("https://api.geysermc.org/v2/versions/geyser"); - JsonNode bedrock = json.get("bedrock").get("protocol"); - int protocolVersion = bedrock.get("id").asInt(); + JsonObject json = WebUtils.getJson("https://api.geysermc.org/v2/versions/geyser"); + JsonObject bedrock = json.getAsJsonObject("bedrock").getAsJsonObject("protocol"); + int protocolVersion = bedrock.get("id").getAsInt(); if (GameProtocol.getBedrockCodec(protocolVersion) != null) { LATEST_BEDROCK_RELEASE = OptionalInt.empty(); // We support the latest version! No need to print a message. @@ -102,7 +102,7 @@ public static void checkForGeyserUpdate(Supplier recipient) } LATEST_BEDROCK_RELEASE = OptionalInt.of(protocolVersion); - final String newBedrockVersion = bedrock.get("name").asText(); + final String newBedrockVersion = bedrock.get("name").getAsString(); // Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join. GeyserCommandSource sender = recipient.get(); diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 1b7f2d9d966..0e4c7154a7f 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -26,6 +26,9 @@ package org.geysermc.geyser.util; import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonReader; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; @@ -71,12 +74,14 @@ public static String getBody(String reqURL) { * @param reqURL URL to fetch * @return the response as JSON */ - public static JsonNode getJson(String reqURL) throws IOException { + public static JsonObject getJson(String reqURL) throws IOException { HttpURLConnection con = (HttpURLConnection) new URL(reqURL).openConnection(); con.setRequestProperty("User-Agent", getUserAgent()); con.setConnectTimeout(10000); con.setReadTimeout(10000); - return GeyserImpl.JSON_MAPPER.readTree(con.getInputStream()); + try (JsonReader reader = GeyserImpl.GSON.newJsonReader(new InputStreamReader(con.getInputStream()))) { + return new JsonParser().parse(reader).getAsJsonObject(); + } } /** diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 962b4a35547..3a03f57c737 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,8 @@ fastutil = "8.5.2" netty = "4.1.107.Final" netty-io-uring = "0.0.25.Final-SNAPSHOT" guava = "29.0-jre" -gson = "2.3.1" # Provided by Spigot 1.8.8 +gson = "2.3.1" # Provided by Spigot 1.8.8 TODO bump to 2.8.1 or similar (Spigot 1.16.5 version) after Merge +gson-runtime = "2.10.1" websocket = "1.5.1" protocol = "3.0.0.Beta1-20240411.165033-129" protocol-connection = "3.0.0.Beta1-20240411.165033-128" @@ -111,6 +112,7 @@ checker-qual = { group = "org.checkerframework", name = "checker-qual", version. commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } +gson-runtime = { group = "com.google.code.gson", name = "gson", version.ref = "gson-runtime" } junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } mcprotocollib = { group = "com.github.geysermc", name = "mcprotocollib", version.ref = "mcprotocollib" } From cb4c5ad62dbed0d5c81fe549fd95a1e3224cffc9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 15 Jun 2024 12:56:00 -0400 Subject: [PATCH 04/42] Progress --- .../org/geysermc/geyser/configuration/ConfigLoaderTemp.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java index 98c6d27a69e..6d51a28477e 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java @@ -52,13 +52,9 @@ public class ConfigLoaderTemp { --------------------------------"""; public static T load(Class configClass) throws IOException { - if (true) { - return null; // For now - } var loader = YamlConfigurationLoader.builder() .file(new File("newconfig.yml")) - .defaultOptions(InterfaceDefaultOptions.get() - .header(HEADER)) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options.header(HEADER))) .build(); ConfigurationNode node = loader.load(); // temp fix for node.virtual() being broken From 8d24891c9757250358fdf7c63ce09399655498a4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:16:14 -0400 Subject: [PATCH 05/42] Update usage of WebUtils --- .../geysermc/geyser/command/defaults/VersionCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index c6852d57743..1c4874e1cee 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.command.defaults; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; @@ -86,8 +86,8 @@ public void execute(GeyserSession session, GeyserCommandSource sender, String[] sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", sender.locale())); try { int buildNumber = this.geyser.buildNumber(); - JsonNode response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest"); - int latestBuildNumber = response.get("build").asInt(); + JsonObject response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest"); + int latestBuildNumber = response.get("build").getAsInt(); if (latestBuildNumber == buildNumber) { sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", sender.locale())); From 2d33cfd14947d33fc12f15eeb05a4da295371313 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 01:04:19 -0400 Subject: [PATCH 06/42] Most things now use Gson for JSON --- .../standalone/GeyserStandaloneLogger.java | 8 +- .../viaproxy/GeyserViaProxyLogger.java | 8 + .../org/geysermc/geyser/GeyserLogger.java | 5 +- .../geyser/configuration/GeyserConfig.java | 1 + .../pack/GeyserResourcePackManifest.java | 28 +- .../ping/GeyserLegacyPingPassthrough.java | 23 +- .../loader/BiomeIdentifierRegistryLoader.java | 4 +- .../registry/loader/ResourcePackLoader.java | 7 +- .../mappings/MappingsConfigReader.java | 20 +- .../mappings/versions/MappingsReader.java | 35 +-- .../mappings/versions/MappingsReader_v1.java | 285 +++++++++--------- .../populator/BlockRegistryPopulator.java | 42 ++- .../CreativeItemRegistryPopulator.java | 37 +-- .../populator/ItemRegistryPopulator.java | 36 ++- .../registry/type/GeyserMappingItem.java | 18 +- .../org/geysermc/geyser/skin/SkinManager.java | 3 +- .../geysermc/geyser/text/MinecraftLocale.java | 15 +- .../org/geysermc/geyser/util/AssetUtils.java | 93 +++--- .../org/geysermc/geyser/util/FileUtils.java | 9 +- .../org/geysermc/geyser/util/JsonUtils.java | 53 ++++ 20 files changed, 433 insertions(+), 297 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/util/JsonUtils.java diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index 3a34920ce8f..c19fa1bc46b 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -29,6 +29,7 @@ import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; @@ -84,7 +85,12 @@ public void info(String message) { @Override public void debug(String message) { - log.debug(ChatColor.GRAY + message); + log.debug(ChatColor.GRAY + "{}", message); + } + + @Override + public void debug(@Nullable Object object) { + log.debug("{}", object); } @Override diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java index 10f414b51dc..550ee91061d 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java @@ -26,6 +26,7 @@ import net.raphimc.viaproxy.cli.ConsoleFormatter; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; @@ -75,6 +76,13 @@ public void debug(String message) { } } + @Override + public void debug(@Nullable Object object) { + if (this.debug) { + this.logger.debug(ConsoleFormatter.convert(String.valueOf(object))); + } + } + @Override public void setDebug(boolean debug) { this.debug = debug; diff --git a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java index aa79e3630f7..f7baf976756 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java @@ -99,7 +99,10 @@ default void info(Component message) { * @param object the object to log */ default void debug(@Nullable Object object) { - debug(String.valueOf(object)); + if (isDebug()) { + // Don't create String object by default + info(String.valueOf(object)); + } } /** diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index b50d172e0eb..059d1c548c9 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.UUID; +@ConfigSerializable public interface GeyserConfig { BedrockConfig bedrock(); diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java index 25a0f0ee044..cfd8709af59 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java @@ -25,11 +25,10 @@ package org.geysermc.geyser.pack; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.pack.ResourcePackManifest; @@ -37,15 +36,14 @@ import java.util.Collection; import java.util.UUID; -public record GeyserResourcePackManifest(@JsonProperty("format_version") int formatVersion, Header header, Collection modules, Collection dependencies) implements ResourcePackManifest { +public record GeyserResourcePackManifest(@SerializedName("format_version") int formatVersion, Header header, Collection modules, Collection dependencies) implements ResourcePackManifest { - public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { } + public record Header(UUID uuid, Version version, String name, String description, @SerializedName("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { } public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { } public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { } - @JsonDeserialize(using = Version.VersionDeserializer.class) public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version { @Override @@ -53,11 +51,17 @@ public record Version(int major, int minor, int patch) implements ResourcePackMa return major + "." + minor + "." + patch; } - public static class VersionDeserializer extends JsonDeserializer { + public static class VersionDeserializer extends TypeAdapter { @Override - public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - int[] version = ctxt.readValue(p, int[].class); - return new Version(version[0], version[1], version[2]); + public void write(JsonWriter jsonWriter, Version version) throws IOException { + } + + @Override + public Version read(JsonReader jsonReader) throws IOException { + jsonReader.beginArray(); + Version version = new Version(jsonReader.nextInt(), jsonReader.nextInt(), jsonReader.nextInt()); + jsonReader.endArray(); + return version; } } } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index 27b40534838..ed08801d995 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -25,8 +25,7 @@ package org.geysermc.geyser.ping; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; +import com.google.gson.JsonSyntaxException; import io.netty.handler.codec.haproxy.HAProxyCommand; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.NetUtil; @@ -34,9 +33,19 @@ import org.cloudburstmc.nbt.util.VarInts; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.GameProtocol; - -import java.io.*; -import java.net.*; +import org.geysermc.geyser.util.JsonUtils; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.ConnectException; +import java.net.Inet4Address; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runnable { @@ -130,11 +139,11 @@ public void run() { } } - this.pingInfo = GeyserImpl.JSON_MAPPER.readValue(buffer, GeyserPingInfo.class); + this.pingInfo = JsonUtils.fromJson(buffer, GeyserPingInfo.class); } catch (SocketTimeoutException | ConnectException ex) { this.pingInfo = null; this.geyser.getLogger().debug("Connection timeout for ping passthrough."); - } catch (JsonParseException | JsonMappingException ex) { + } catch (JsonSyntaxException ex) { this.geyser.getLogger().error("Failed to parse json when pinging server!", ex); } catch (EOFException e) { this.pingInfo = null; diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java index eb091f273de..78d7296057f 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java @@ -30,10 +30,10 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.util.JsonUtils; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Type; import java.util.Map; @@ -50,7 +50,7 @@ public Object2IntMap load(String input) { Map biomeEntries; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/biomes.json")) { - biomeEntries = GeyserImpl.GSON.fromJson(new InputStreamReader(stream), biomeEntriesType); + biomeEntries = JsonUtils.fromJson(stream, biomeEntriesType); } catch (IOException e) { throw new AssertionError("Unable to load Bedrock runtime biomes", e); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java index 800a3d22c6e..537c41ed9dc 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.registry.loader; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; import org.geysermc.geyser.api.pack.ResourcePack; @@ -55,6 +57,9 @@ * Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}. */ public class ResourcePackLoader implements RegistryLoader> { + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(GeyserResourcePackManifest.Version.class, new GeyserResourcePackManifest.Version.VersionDeserializer()) + .create(); static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}"); @@ -135,7 +140,7 @@ public static GeyserResourcePack readPack(Path path) throws IllegalArgumentExcep } if (name.contains("manifest.json")) { try { - GeyserResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), GeyserResourcePackManifest.class); + GeyserResourcePackManifest manifest = FileUtils.loadJson(GSON, zip.getInputStream(x), GeyserResourcePackManifest.class); if (manifest.header().uuid() != null) { manifestReference.set(manifest); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/MappingsConfigReader.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/MappingsConfigReader.java index d09e0b5a1e1..6fd23c73852 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/MappingsConfigReader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/MappingsConfigReader.java @@ -25,7 +25,8 @@ package org.geysermc.geyser.registry.mappings; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.checkerframework.checker.nullness.qual.Nullable; @@ -35,6 +36,7 @@ import org.geysermc.geyser.registry.mappings.versions.MappingsReader; import org.geysermc.geyser.registry.mappings.versions.MappingsReader_v1; +import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -95,10 +97,10 @@ public void loadBlockMappingsFromJson(BiConsumer con } } - public @Nullable JsonNode getMappingsRoot(Path file) { - JsonNode mappingsRoot; - try { - mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile()); + public @Nullable JsonObject getMappingsRoot(Path file) { + JsonObject mappingsRoot; + try (FileReader reader = new FileReader(file.toFile())) { + mappingsRoot = (JsonObject) new JsonParser().parse(reader); } catch (IOException e) { GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e); return null; @@ -112,8 +114,8 @@ public void loadBlockMappingsFromJson(BiConsumer con return mappingsRoot; } - public int getFormatVersion(JsonNode mappingsRoot, Path file) { - int formatVersion = mappingsRoot.get("format_version").asInt(); + public int getFormatVersion(JsonObject mappingsRoot, Path file) { + int formatVersion = mappingsRoot.get("format_version").getAsInt(); if (!this.mappingReaders.containsKey(formatVersion)) { GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion); return -1; @@ -122,7 +124,7 @@ public int getFormatVersion(JsonNode mappingsRoot, Path file) { } public void readItemMappingsFromJson(Path file, BiConsumer consumer) { - JsonNode mappingsRoot = getMappingsRoot(file); + JsonObject mappingsRoot = getMappingsRoot(file); if (mappingsRoot == null) { return; @@ -138,7 +140,7 @@ public void readItemMappingsFromJson(Path file, BiConsumer consumer) { - JsonNode mappingsRoot = getMappingsRoot(file); + JsonObject mappingsRoot = getMappingsRoot(file); if (mappingsRoot == null) { return; diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java index b2bdd5a0130..43d9a23d999 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.registry.mappings.versions; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; @@ -36,14 +36,14 @@ import java.util.function.BiConsumer; public abstract class MappingsReader { - public abstract void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); - public abstract void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); + public abstract void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer); + public abstract void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer); - public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException; - public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException; + public abstract CustomItemData readItemMappingEntry(JsonObject node) throws InvalidCustomMappingsFileException; + public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException; - protected @Nullable CustomRenderOffsets fromJsonNode(JsonNode node) { - if (node == null || !node.isObject()) { + protected @Nullable CustomRenderOffsets fromJsonObject(JsonObject node) { + if (node == null) { return null; } @@ -53,9 +53,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonNode node, String hand) { - JsonNode tmpNode = node.get(hand); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonObject node, String hand) { + if (!(node.get(hand) instanceof JsonObject tmpNode)) { return null; } @@ -65,9 +64,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonNode node, String perspective) { - JsonNode tmpNode = node.get(perspective); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonObject node, String perspective) { + if (!(node.get(perspective) instanceof JsonObject tmpNode)) { return null; } @@ -78,9 +76,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) { - JsonNode tmpNode = node.get(offsetType); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonObject node, String offsetType) { + if (!(node.get(offsetType) instanceof JsonObject tmpNode)) { return null; } @@ -89,9 +86,9 @@ public abstract class MappingsReader { } return new CustomRenderOffsets.OffsetXYZ( - tmpNode.get("x").floatValue(), - tmpNode.get("y").floatValue(), - tmpNode.get("z").floatValue() + tmpNode.get("x").getAsFloat(), + tmpNode.get("y").getAsFloat(), + tmpNode.get("z").getAsFloat() ); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java index b5e25a4ba5c..bbde78d0377 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java @@ -25,8 +25,10 @@ package org.geysermc.geyser.registry.mappings.versions; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.checkerframework.checker.nullness.qual.Nullable; @@ -34,9 +36,14 @@ import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockPermutation; import org.geysermc.geyser.api.block.custom.CustomBlockState; -import org.geysermc.geyser.api.block.custom.component.*; +import org.geysermc.geyser.api.block.custom.component.BoxComponent; +import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; +import org.geysermc.geyser.api.block.custom.component.GeometryComponent; +import org.geysermc.geyser.api.block.custom.component.MaterialInstance; +import org.geysermc.geyser.api.block.custom.component.PlacementConditions; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face; +import org.geysermc.geyser.api.block.custom.component.TransformationComponent; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.util.CreativeCategory; @@ -57,7 +64,13 @@ import org.geysermc.geyser.util.MinecraftKey; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; @@ -68,7 +81,7 @@ */ public class MappingsReader_v1 extends MappingsReader { @Override - public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + public void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer) { this.readItemMappingsV1(file, mappingsRoot, consumer); } @@ -76,24 +89,24 @@ public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + public void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer) { this.readBlockMappingsV1(file, mappingsRoot, consumer); } - public void readItemMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer consumer) { - JsonNode itemsNode = mappingsRoot.get("items"); + public void readItemMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer consumer) { + JsonObject itemsNode = mappingsRoot.getAsJsonObject("items"); - if (itemsNode != null && itemsNode.isObject()) { - itemsNode.fields().forEachRemaining(entry -> { - if (entry.getValue().isArray()) { - entry.getValue().forEach(data -> { + if (itemsNode != null) { + itemsNode.entrySet().forEach(entry -> { + if (entry.getValue() instanceof JsonArray array) { + array.forEach(data -> { try { - CustomItemData customItemData = this.readItemMappingEntry(data); + CustomItemData customItemData = this.readItemMappingEntry((JsonObject) data); consumer.accept(entry.getKey(), customItemData); } catch (InvalidCustomMappingsFileException e) { GeyserImpl.getInstance().getLogger().error("Error in registering items for custom mapping file: " + file.toString(), e); @@ -108,19 +121,17 @@ public void readItemMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer consumer) { - JsonNode blocksNode = mappingsRoot.get("blocks"); - - if (blocksNode != null && blocksNode.isObject()) { - blocksNode.fields().forEachRemaining(entry -> { - if (entry.getValue().isObject()) { + public void readBlockMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer consumer) { + if (mappingsRoot.get("blocks") instanceof JsonObject blocksNode) { + blocksNode.entrySet().forEach(entry -> { + if (entry.getValue() instanceof JsonObject jsonObject) { try { String identifier = MinecraftKey.key(entry.getKey()).asString(); - CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, entry.getValue()); + CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, jsonObject); consumer.accept(identifier, customBlockMapping); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error("Error in registering blocks for custom mapping file: " + file.toString()); @@ -131,85 +142,85 @@ public void readBlockMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer tagsSet = new ObjectOpenHashSet<>(); - tags.forEach(tag -> tagsSet.add(tag.asText())); + tags.forEach(tag -> tagsSet.add(tag.getAsString())); customItemData.tags(tagsSet); } @@ -220,26 +231,26 @@ public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMa * Read a block mapping entry from a JSON node and Java identifier * * @param identifier The Java identifier of the block - * @param node The {@link JsonNode} containing the block mapping entry + * @param node The {@link JsonObject} containing the block mapping entry * @return The {@link CustomBlockMapping} record to be read by {@link org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator} * @throws InvalidCustomMappingsFileException If the JSON node is invalid */ @Override - public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException { - if (node == null || !node.isObject()) { + public CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException { + if (node == null) { throw new InvalidCustomMappingsFileException("Invalid block mappings entry:" + node); } - String name = node.get("name").asText(); + String name = node.get("name").getAsString(); if (name == null || name.isEmpty()) { throw new InvalidCustomMappingsFileException("A block entry has no name"); } - boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").asBoolean(); + boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").getAsBoolean(); CreativeCategory creativeCategory = CreativeCategory.NONE; if (node.has("creative_category")) { - String categoryName = node.get("creative_category").asText(); + String categoryName = node.get("creative_category").getAsString(); try { creativeCategory = CreativeCategory.valueOf(categoryName.toUpperCase()); } catch (IllegalArgumentException e) { @@ -249,11 +260,11 @@ public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node String creativeGroup = ""; if (node.has("creative_group")) { - creativeGroup = node.get("creative_group").asText(); + creativeGroup = node.get("creative_group").getAsString(); } // If this is true, we will only register the states the user has specified rather than all the possible block states - boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean(); + boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").getAsBoolean(); // Create the data for the overall block CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder() @@ -273,12 +284,9 @@ public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node Map componentsMap = new LinkedHashMap<>(); - JsonNode stateOverrides = node.get("state_overrides"); - if (stateOverrides != null && stateOverrides.isObject()) { + if (node.get("state_overrides") instanceof JsonObject stateOverrides) { // Load components for specific Java block states - Iterator> fields = stateOverrides.fields(); - while (fields.hasNext()) { - Map.Entry overrideEntry = fields.next(); + for (Map.Entry overrideEntry : stateOverrides.entrySet()) { String state = identifier + "[" + overrideEntry.getKey() + "]"; if (!BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().containsKey(state)) { throw new InvalidCustomMappingsFileException("Unknown Java block state: " + state + " for state_overrides."); @@ -358,12 +366,12 @@ private CustomBlockMapping createCustomBlockMapping(CustomBlockData.Builder cust /** * Creates a {@link CustomBlockComponents} object for the passed state override or base block node, Java block state identifier, and custom block name * - * @param node the state override or base block {@link JsonNode} + * @param element the state override or base block {@link JsonObject} * @param stateKey the Java block state identifier * @param name the name of the custom block * @return the {@link CustomBlockComponents} object */ - private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode node, String stateKey, String name) { + private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonElement element, String stateKey, String name) { // This is needed to find the correct selection box for the given block int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1); BoxComponent boxComponent = createBoxComponent(id); @@ -372,7 +380,7 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode .collisionBox(boxComponent) .selectionBox(boxComponent); - if (node == null) { + if (!(element instanceof JsonObject node)) { // No other components were defined return new CustomBlockComponentsMapping(builder.build(), extendedBoxComponent); } @@ -394,28 +402,28 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode // We set this to max value by default so that we may dictate the correct destroy time ourselves float destructibleByMining = Float.MAX_VALUE; if (node.has("destructible_by_mining")) { - destructibleByMining = node.get("destructible_by_mining").floatValue(); + destructibleByMining = node.get("destructible_by_mining").getAsFloat(); } builder.destructibleByMining(destructibleByMining); if (node.has("geometry")) { - if (node.get("geometry").isTextual()) { + if (node.get("geometry").isJsonPrimitive()) { builder.geometry(new GeyserGeometryComponent.Builder() - .identifier(node.get("geometry").asText()) + .identifier(node.get("geometry").getAsString()) .build()); } else { - JsonNode geometry = node.get("geometry"); + JsonObject geometry = node.getAsJsonObject("geometry"); GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder(); if (geometry.has("identifier")) { - geometryBuilder.identifier(geometry.get("identifier").asText()); + geometryBuilder.identifier(geometry.get("identifier").getAsString()); } if (geometry.has("bone_visibility")) { - JsonNode boneVisibility = geometry.get("bone_visibility"); - if (boneVisibility.isObject()) { + if (geometry.get("bone_visibility") instanceof JsonObject boneVisibility) { Map boneVisibilityMap = new Object2ObjectOpenHashMap<>(); - boneVisibility.fields().forEachRemaining(entry -> { + boneVisibility.entrySet().forEach(entry -> { String key = entry.getKey(); - String value = entry.getValue().isBoolean() ? (entry.getValue().asBoolean() ? "1" : "0") : entry.getValue().asText(); + String value = entry.getValue() instanceof JsonPrimitive primitive && primitive.isBoolean() + ? (entry.getValue().getAsBoolean() ? "1" : "0") : entry.getValue().getAsString(); boneVisibilityMap.put(key, value); }); geometryBuilder.boneVisibility(boneVisibilityMap); @@ -427,30 +435,30 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode String displayName = name; if (node.has("display_name")) { - displayName = node.get("display_name").asText(); + displayName = node.get("display_name").getAsString(); } builder.displayName(displayName); if (node.has("friction")) { - builder.friction(node.get("friction").floatValue()); + builder.friction(node.get("friction").getAsFloat()); } if (node.has("light_emission")) { - builder.lightEmission(node.get("light_emission").asInt()); + builder.lightEmission(node.get("light_emission").getAsInt()); } if (node.has("light_dampening")) { - builder.lightDampening(node.get("light_dampening").asInt()); + builder.lightDampening(node.get("light_dampening").getAsInt()); } boolean placeAir = true; if (node.has("place_air")) { - placeAir = node.get("place_air").asBoolean(); + placeAir = node.get("place_air").getAsBoolean(); } builder.placeAir(placeAir); if (node.has("transformation")) { - JsonNode transformation = node.get("transformation"); + JsonObject transformation = node.getAsJsonObject("transformation"); int rotationX = 0; int rotationY = 0; @@ -463,22 +471,22 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode float transformZ = 0; if (transformation.has("rotation")) { - JsonNode rotation = transformation.get("rotation"); - rotationX = rotation.get(0).asInt(); - rotationY = rotation.get(1).asInt(); - rotationZ = rotation.get(2).asInt(); + JsonArray rotation = transformation.getAsJsonArray("rotation"); + rotationX = rotation.get(0).getAsInt(); + rotationY = rotation.get(1).getAsInt(); + rotationZ = rotation.get(2).getAsInt(); } if (transformation.has("scale")) { - JsonNode scale = transformation.get("scale"); - scaleX = scale.get(0).floatValue(); - scaleY = scale.get(1).floatValue(); - scaleZ = scale.get(2).floatValue(); + JsonArray scale = transformation.getAsJsonArray("scale"); + scaleX = scale.get(0).getAsFloat(); + scaleY = scale.get(1).getAsFloat(); + scaleZ = scale.get(2).getAsFloat(); } if (transformation.has("translation")) { - JsonNode translation = transformation.get("translation"); - transformX = translation.get(0).floatValue(); - transformY = translation.get(1).floatValue(); - transformZ = translation.get(2).floatValue(); + JsonArray translation = transformation.getAsJsonArray("translation"); + transformX = translation.get(0).getAsFloat(); + transformY = translation.get(1).getAsFloat(); + transformZ = translation.get(2).getAsFloat(); } builder.transformation(new TransformationComponent(rotationX, rotationY, rotationZ, scaleX, scaleY, scaleZ, transformX, transformY, transformZ)); } @@ -490,12 +498,10 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode } if (node.has("material_instances")) { - JsonNode materialInstances = node.get("material_instances"); - if (materialInstances.isObject()) { - materialInstances.fields().forEachRemaining(entry -> { + if (node.get("material_instances") instanceof JsonObject materialInstances) { + materialInstances.entrySet().forEach(entry -> { String key = entry.getKey(); - JsonNode value = entry.getValue(); - if (value.isObject()) { + if (entry.getValue() instanceof JsonObject value) { MaterialInstance materialInstance = createMaterialInstanceComponent(value); builder.materialInstance(key, materialInstance); } @@ -503,16 +509,10 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode } } - if (node.has("placement_filter")) { - JsonNode placementFilter = node.get("placement_filter"); - if (placementFilter.isObject()) { - if (placementFilter.has("conditions")) { - JsonNode conditions = placementFilter.get("conditions"); - if (conditions.isArray()) { - List filter = createPlacementFilterComponent(conditions); - builder.placementFilter(filter); - } - } + if (node.get("placement_filter") instanceof JsonObject placementFilter) { + if (placementFilter.get("conditions") instanceof JsonArray conditions) { + List filter = createPlacementFilterComponent(conditions); + builder.placementFilter(filter); } } @@ -521,9 +521,9 @@ private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode // Ideally we could programmatically extract the tags here https://wiki.bedrock.dev/blocks/block-tags.html // This would let us automatically apply the correct vanilla tags to blocks // However, its worth noting that vanilla tools do not currently honor these tags anyway - if (node.get("tags") instanceof ArrayNode tags) { + if (node.get("tags") instanceof JsonArray tags) { Set tagsSet = new ObjectOpenHashSet<>(); - tags.forEach(tag -> tagsSet.add(tag.asText())); + tags.forEach(tag -> tagsSet.add(tag.getAsString())); builder.tags(tagsSet); } @@ -613,21 +613,21 @@ private BoxComponent createBoxComponent(int javaId) { /** * Creates a {@link BoxComponent} from a JSON Node * - * @param node the JSON node + * @param element the JSON node * @return the {@link BoxComponent} */ - private @Nullable BoxComponent createBoxComponent(JsonNode node) { - if (node != null && node.isObject()) { + private @Nullable BoxComponent createBoxComponent(JsonElement element) { + if (element instanceof JsonObject node) { if (node.has("origin") && node.has("size")) { - JsonNode origin = node.get("origin"); - float originX = origin.get(0).floatValue(); - float originY = origin.get(1).floatValue(); - float originZ = origin.get(2).floatValue(); + JsonArray origin = node.getAsJsonArray("origin"); + float originX = origin.get(0).getAsFloat(); + float originY = origin.get(1).getAsFloat(); + float originZ = origin.get(2).getAsFloat(); - JsonNode size = node.get("size"); - float sizeX = size.get(0).floatValue(); - float sizeY = size.get(1).floatValue(); - float sizeZ = size.get(2).floatValue(); + JsonArray size = node.getAsJsonArray("size"); + float sizeX = size.get(0).getAsFloat(); + float sizeY = size.get(1).getAsFloat(); + float sizeZ = size.get(2).getAsFloat(); return new BoxComponent(originX, originY, originZ, sizeX, sizeY, sizeZ); } @@ -642,26 +642,26 @@ private BoxComponent createBoxComponent(int javaId) { * @param node the material instance node * @return the {@link MaterialInstance} */ - private MaterialInstance createMaterialInstanceComponent(JsonNode node) { + private MaterialInstance createMaterialInstanceComponent(JsonObject node) { // Set default values, and use what the user provides if they have provided something String texture = null; if (node.has("texture")) { - texture = node.get("texture").asText(); + texture = node.get("texture").getAsString(); } String renderMethod = "opaque"; if (node.has("render_method")) { - renderMethod = node.get("render_method").asText(); + renderMethod = node.get("render_method").getAsString(); } boolean faceDimming = true; if (node.has("face_dimming")) { - faceDimming = node.get("face_dimming").asBoolean(); + faceDimming = node.get("face_dimming").getAsBoolean(); } boolean ambientOcclusion = true; if (node.has("ambient_occlusion")) { - ambientOcclusion = node.get("ambient_occlusion").asBoolean(); + ambientOcclusion = node.get("ambient_occlusion").getAsBoolean(); } return new GeyserMaterialInstance.Builder() @@ -678,32 +678,33 @@ private MaterialInstance createMaterialInstanceComponent(JsonNode node) { * @param node the conditions node * @return the list of {@link PlacementConditions} */ - private List createPlacementFilterComponent(JsonNode node) { + private List createPlacementFilterComponent(JsonArray node) { List conditions = new ArrayList<>(); // The structure of the placement filter component is the most complex of the current components // Each condition effectively separated into two arrays: one of allowed faces, and one of blocks/block Molang queries - node.forEach(condition -> { + node.forEach(json -> { + if (!(json instanceof JsonObject condition)) { + return; + } Set faces = EnumSet.noneOf(Face.class); if (condition.has("allowed_faces")) { - JsonNode allowedFaces = condition.get("allowed_faces"); - if (allowedFaces.isArray()) { - allowedFaces.forEach(face -> faces.add(Face.valueOf(face.asText().toUpperCase()))); + if (condition.get("allowed_faces") instanceof JsonArray allowedFaces) { + allowedFaces.forEach(face -> faces.add(Face.valueOf(face.getAsString().toUpperCase()))); } } LinkedHashMap blockFilters = new LinkedHashMap<>(); if (condition.has("block_filter")) { - JsonNode blockFilter = condition.get("block_filter"); - if (blockFilter.isArray()) { + if (condition.get("block_filter") instanceof JsonArray blockFilter) { blockFilter.forEach(filter -> { - if (filter.isObject()) { - if (filter.has("tags")) { - JsonNode tags = filter.get("tags"); - blockFilters.put(tags.asText(), BlockFilterType.TAG); + if (filter instanceof JsonObject jsonObject) { + if (jsonObject.has("tags")) { + JsonElement tags = jsonObject.get("tags"); + blockFilters.put(tags.getAsString(), BlockFilterType.TAG); } - } else if (filter.isTextual()) { - blockFilters.put(filter.asText(), BlockFilterType.BLOCK); + } else if (filter instanceof JsonPrimitive primitive && primitive.isString()) { + blockFilters.put(filter.getAsString(), BlockFilterType.BLOCK); } }); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index d7dc989dab8..4c7fa8f8abe 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -25,17 +25,26 @@ package org.geysermc.geyser.registry.populator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Interner; import com.google.common.collect.Interners; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.*; -import org.cloudburstmc.nbt.*; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.cloudburstmc.nbt.NBTInputStream; +import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.nbt.NbtType; +import org.cloudburstmc.nbt.NbtUtils; import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData; @@ -56,12 +65,21 @@ import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import java.io.DataInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.stream.Stream; import java.util.zip.GZIPInputStream; @@ -461,23 +479,23 @@ public ItemStack pickItem(BlockState state) { BLOCKS_NBT = blocksNbt; - JsonNode blockInteractionsJson; + JsonObject blockInteractionsJson; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) { - blockInteractionsJson = GeyserImpl.JSON_MAPPER.readTree(stream); + blockInteractionsJson = JsonUtils.fromJson(stream); } catch (Exception e) { throw new AssertionError("Unable to load Java block interaction mappings", e); } - BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes"))); - BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build"))); + BlockRegistries.INTERACTIVE.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("always_consumes"))); + BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("requires_may_build"))); BlockRegistries.BLOCK_STATES.freeze(); } - private static BitSet toBlockStateSet(ArrayNode node) { + private static BitSet toBlockStateSet(JsonArray node) { BitSet blockStateSet = new BitSet(node.size()); - for (JsonNode javaIdentifier : node) { - blockStateSet.set(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.textValue())); + for (JsonElement javaIdentifier : node) { + blockStateSet.set(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.getAsString())); } return blockStateSet; } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java index c536d739c03..701775223e2 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java @@ -25,7 +25,9 @@ package org.geysermc.geyser.registry.populator; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; @@ -37,6 +39,7 @@ import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; +import org.geysermc.geyser.util.JsonUtils; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -61,16 +64,16 @@ static void populate(ItemRegistryPopulator.PaletteVersion palette, Map definitions) { + private static ItemData.@Nullable Builder createItemData(JsonObject itemNode, BlockMappings blockMappings, Map definitions) { int count = 1; int damage = 0; int bedrockBlockRuntimeId; NbtMap tag = null; - String identifier = itemNode.get("id").textValue(); + String identifier = itemNode.get("id").getAsString(); for (BiPredicate predicate : JAVA_ONLY_ITEM_FILTER) { if (predicate.test(identifier, damage)) { return null; } } - JsonNode damageNode = itemNode.get("damage"); + JsonElement damageNode = itemNode.get("damage"); if (damageNode != null) { - damage = damageNode.asInt(); + damage = damageNode.getAsInt(); } - JsonNode countNode = itemNode.get("count"); + JsonElement countNode = itemNode.get("count"); if (countNode != null) { - count = countNode.asInt(); + count = countNode.getAsInt(); } GeyserBedrockBlock blockDefinition = null; - JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId"); - JsonNode blockStateNode; + JsonElement blockRuntimeIdNode = itemNode.get("blockRuntimeId"); + JsonElement blockStateNode; if (blockRuntimeIdNode != null) { - bedrockBlockRuntimeId = blockRuntimeIdNode.asInt(); + bedrockBlockRuntimeId = blockRuntimeIdNode.getAsInt(); if (bedrockBlockRuntimeId == 0 && !identifier.equals("minecraft:blue_candle")) { // FIXME bedrockBlockRuntimeId = -1; } blockDefinition = bedrockBlockRuntimeId == -1 ? null : blockMappings.getDefinition(bedrockBlockRuntimeId); } else if ((blockStateNode = itemNode.get("block_state_b64")) != null) { - byte[] bytes = Base64.getDecoder().decode(blockStateNode.asText()); + byte[] bytes = Base64.getDecoder().decode(blockStateNode.getAsString()); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); try { NbtMap stateTag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); @@ -132,9 +135,9 @@ static void populate(ItemRegistryPopulator.PaletteVersion palette, Map> mappingItemsType = new TypeReference<>() { }; + Type mappingItemsType = new TypeToken>() { }.getType(); Map items; try (InputStream stream = bootstrap.getResourceOrThrow("mappings/items.json")) { // Load item mappings from Java Edition to Bedrock Edition - items = GeyserImpl.JSON_MAPPER.readValue(stream, mappingItemsType); + items = JsonUtils.fromJson(stream, mappingItemsType); } catch (Exception e) { throw new AssertionError("Unable to load Java runtime item IDs", e); } @@ -126,11 +146,11 @@ public static void populate() { /* Load item palette */ for (PaletteVersion palette : paletteVersions) { - TypeReference> paletteEntriesType = new TypeReference<>() {}; + Type paletteEntriesType = new TypeToken>() { }.getType(); List itemEntries; try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/runtime_item_states.%s.json", palette.version()))) { - itemEntries = GeyserImpl.JSON_MAPPER.readValue(stream, paletteEntriesType); + itemEntries = JsonUtils.fromJson(stream, paletteEntriesType); } catch (Exception e) { throw new AssertionError("Unable to load Bedrock runtime item IDs", e); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java index ab8c52bf6f4..50198e0fca9 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.registry.type; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -43,14 +43,14 @@ @NoArgsConstructor @AllArgsConstructor public class GeyserMappingItem { - @JsonProperty("bedrock_identifier") String bedrockIdentifier; - @JsonProperty("bedrock_data") int bedrockData; + @SerializedName("bedrock_identifier") String bedrockIdentifier; + @SerializedName("bedrock_data") int bedrockData; Integer firstBlockRuntimeId; Integer lastBlockRuntimeId; - @JsonProperty("tool_type") String toolType; - @JsonProperty("tool_tier") String toolTier; - @JsonProperty("armor_type") String armorType; - @JsonProperty("protection_value") int protectionValue; - @JsonProperty("is_edible") boolean edible = false; - @JsonProperty("is_entity_placer") boolean entityPlacer = false; + @SerializedName("tool_type") String toolType; + @SerializedName("tool_tier") String toolTier; + @SerializedName("armor_type") String armorType; + @SerializedName("protection_value") int protectionValue; + @SerializedName("is_edible") boolean edible = false; + @SerializedName("is_entity_placer") boolean entityPlacer = false; } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 4c3db7504c9..88a9c6d01eb 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -282,6 +282,7 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { } public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException { + // TODO use GameProfile method. JsonNode skinObject; try { skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); @@ -334,4 +335,4 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { private static final String DEFAULT_FLOODGATE_STEVE = "https://textures.minecraft.net/texture/31f477eb1a7beee631c2ca64d06f8f68fa93a3386d04452ab27f43acdf1b60cb"; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 94d8b254fc7..3f3e8ff6fb1 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -25,11 +25,13 @@ package org.geysermc.geyser.text; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.FileUtils; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.WebUtils; import java.io.FileNotFoundException; @@ -39,7 +41,6 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.HashMap; -import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -189,14 +190,12 @@ public static Map parseLangFile(Path localeFile, String locale) // Read the localefile try (InputStream localeStream = Files.newInputStream(localeFile, StandardOpenOption.READ)) { // Parse the file as json - JsonNode localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream); + JsonObject localeObj = JsonUtils.fromJson(localeStream); // Parse all the locale fields - Iterator> localeIterator = localeObj.fields(); Map langMap = new HashMap<>(); - while (localeIterator.hasNext()) { - Map.Entry entry = localeIterator.next(); - langMap.put(entry.getKey(), entry.getValue().asText()); + for (Map.Entry entry : localeObj.entrySet()) { + langMap.put(entry.getKey(), entry.getValue().getAsString()); } return langMap; } catch (FileNotFoundException e){ @@ -266,4 +265,4 @@ private static String byteArrayToHexString(byte[] b) { } return result.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java index 6bdae6dfeda..0c07f4237ec 100644 --- a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java @@ -25,18 +25,28 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.zip.ZipFile; @@ -83,7 +93,7 @@ public static CompletableFuture generateAssetCache() { return CompletableFuture.supplyAsync(() -> { try { // Get the version manifest from Mojang - VersionManifest versionManifest = GeyserImpl.JSON_MAPPER.readValue( + VersionManifest versionManifest = GeyserImpl.GSON.fromJson( WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); // Get the url for the latest version of the games manifest @@ -101,26 +111,24 @@ public static CompletableFuture generateAssetCache() { } // Get the individual version manifest - VersionInfo versionInfo = GeyserImpl.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); + VersionInfo versionInfo = GeyserImpl.GSON.fromJson(WebUtils.getBody(latestInfoURL), VersionInfo.class); // Get the client jar for use when downloading the en_us locale - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); + GeyserImpl.getInstance().getLogger().debug(versionInfo.getDownloads()); // Was previously a Jackson call for writeValueToString CLIENT_JAR_INFO = versionInfo.getDownloads().get("client"); - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(CLIENT_JAR_INFO)); + GeyserImpl.getInstance().getLogger().debug(CLIENT_JAR_INFO); // Was previously a Jackson call for writeValueToString // Get the assets list - JsonNode assets = GeyserImpl.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); + JsonObject assets = ((JsonObject) new JsonParser().parse(WebUtils.getBody(versionInfo.getAssetIndex().getUrl()))).getAsJsonObject("objects"); // Put each asset into an array for use later - Iterator> assetIterator = assets.fields(); - while (assetIterator.hasNext()) { - Map.Entry entry = assetIterator.next(); + for (Map.Entry entry : assets.entrySet()) { if (!entry.getKey().startsWith("minecraft/lang/")) { // No need to cache non-language assets as we don't use them continue; } - Asset asset = GeyserImpl.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); + Asset asset = GeyserImpl.GSON.fromJson(entry.getValue(), Asset.class); ASSET_MAP.put(entry.getKey(), asset); } @@ -221,106 +229,99 @@ public interface InputStreamConsumer { /* Classes that map to JSON files served by Mojang */ - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionManifest { - @JsonProperty("latest") + @SerializedName("latest") private LatestVersion latestVersion; - @JsonProperty("versions") + @SerializedName("versions") private List versions; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class LatestVersion { - @JsonProperty("release") + @SerializedName("release") private String release; - @JsonProperty("snapshot") + @SerializedName("snapshot") private String snapshot; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class Version { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("type") + @SerializedName("type") private String type; - @JsonProperty("url") + @SerializedName("url") private String url; - @JsonProperty("time") + @SerializedName("time") private String time; - @JsonProperty("releaseTime") + @SerializedName("releaseTime") private String releaseTime; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionInfo { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("type") + @SerializedName("type") private String type; - @JsonProperty("time") + @SerializedName("time") private String time; - @JsonProperty("releaseTime") + @SerializedName("releaseTime") private String releaseTime; - @JsonProperty("assetIndex") + @SerializedName("assetIndex") private AssetIndex assetIndex; - @JsonProperty("downloads") + @SerializedName("downloads") private Map downloads; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionDownload { - @JsonProperty("sha1") + @SerializedName("sha1") private String sha1; - @JsonProperty("size") + @SerializedName("size") private int size; - @JsonProperty("url") + @SerializedName("url") private String url; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class AssetIndex { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("sha1") + @SerializedName("sha1") private String sha1; - @JsonProperty("size") + @SerializedName("size") private int size; - @JsonProperty("totalSize") + @SerializedName("totalSize") private int totalSize; - @JsonProperty("url") + @SerializedName("url") private String url; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter public static class Asset { - @JsonProperty("hash") + @SerializedName("hash") private String hash; - @JsonProperty("size") + @SerializedName("size") private int size; } diff --git a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java index c8423c3be11..4a7b7fda6c6 100644 --- a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.gson.Gson; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; @@ -63,9 +64,13 @@ public static T loadConfig(File src, Class valueType) throws IOException return objectMapper.readValue(src, valueType); } - public static T loadJson(InputStream src, Class valueType) throws IOException { + public static T loadJson(InputStream src, Class valueType) { + return loadJson(GeyserImpl.GSON, src, valueType); + } + + public static T loadJson(Gson gson, InputStream src, Class valueType) { // Read specifically with UTF-8 to allow any non-UTF-encoded JSON to read - return GeyserImpl.JSON_MAPPER.readValue(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); + return gson.fromJson(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java new file mode 100644 index 00000000000..9ae74b1774b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 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 com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.geysermc.geyser.GeyserImpl; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +public final class JsonUtils { + + public static T fromJson(byte[] bytes, Class type) { + return GeyserImpl.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), type); + } + + public static JsonObject fromJson(InputStream stream) { + return (JsonObject) new JsonParser().parse(new InputStreamReader(stream)); + } + + public static T fromJson(InputStream stream, Type type) { + return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type); + } + + private JsonUtils() { + } +} From 0dadbc2ce26791cb9ad1ace6377073370f8dfb03 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:17:21 -0400 Subject: [PATCH 07/42] Allow tests to succeed by using new Gson version --- core/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index af4076c3026..41a2b1ff0c0 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { // Test testImplementation(libs.junit) + testImplementation(libs.gson.runtime) // Annotation Processors compileOnly(projects.ap) @@ -159,4 +160,4 @@ tasks.register("downloadBedrockData") { suffixedFiles = listOf("block_palette.nbt", "creative_items.json", "runtime_item_states.json") destinationDir = "$projectDir/src/main/resources/bedrock" -} \ No newline at end of file +} From 7eb5b539b5deda97c0fde996936e8acc309a569c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:39:50 -0400 Subject: [PATCH 08/42] Use slightly cleaner version for Version deserializer --- .../pack/GeyserResourcePackManifest.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java index cfd8709af59..2954a445494 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java @@ -25,14 +25,16 @@ package org.geysermc.geyser.pack; -import com.google.gson.TypeAdapter; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.pack.ResourcePackManifest; -import java.io.IOException; +import java.lang.reflect.Type; import java.util.Collection; import java.util.UUID; @@ -51,17 +53,11 @@ public record Version(int major, int minor, int patch) implements ResourcePackMa return major + "." + minor + "." + patch; } - public static class VersionDeserializer extends TypeAdapter { + public static class VersionDeserializer implements JsonDeserializer { @Override - public void write(JsonWriter jsonWriter, Version version) throws IOException { - } - - @Override - public Version read(JsonReader jsonReader) throws IOException { - jsonReader.beginArray(); - Version version = new Version(jsonReader.nextInt(), jsonReader.nextInt(), jsonReader.nextInt()); - jsonReader.endArray(); - return version; + public Version deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray array = json.getAsJsonArray(); + return new Version(array.get(0).getAsInt(), array.get(1).getAsInt(), array.get(2).getAsInt()); } } } From 360350d44eeaa019ad95920feace75dd7cec45d0 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:49:42 -0400 Subject: [PATCH 09/42] Work around older Gson versions without record support --- bootstrap/spigot/build.gradle.kts | 3 +++ core/build.gradle.kts | 6 ++++- .../java/org/geysermc/geyser/GeyserImpl.java | 27 +++++++++++++++---- .../pack/GeyserResourcePackManifest.java | 2 ++ .../registry/loader/ResourcePackLoader.java | 7 +---- .../org/geysermc/geyser/util/FileUtils.java | 14 +++++----- .../org/geysermc/geyser/util/JsonUtils.java | 19 +++++++++++++ gradle/libs.versions.toml | 1 + 8 files changed, 60 insertions(+), 19 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index fa24f3566fa..1f637d8f848 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -25,6 +25,8 @@ dependencies { compileOnly(libs.paper.mojangapi) compileOnlyApi(libs.viaversion) + + implementation(libs.gson.record.factory) // For 1.16.5/1.17.1 } platformRelocate("it.unimi.dsi.fastutil") @@ -36,6 +38,7 @@ platformRelocate("me.lucko.commodore") platformRelocate("org.yaml") // Broken as of 1.20 platformRelocate("org.spongepowered") platformRelocate("io.leangen.geantyref") +platformRelocate("marcono1234.gson") // These dependencies are already present on the platform provided(libs.viaversion) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 41a2b1ff0c0..1e5a9d67593 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -20,6 +20,10 @@ dependencies { implementation(libs.configurate.yaml) api(libs.guava) + compileOnly(libs.gson.record.factory) { + isTransitive = false + } + // Fastutil Maps implementation(libs.bundles.fastutil) @@ -61,7 +65,7 @@ dependencies { // Test testImplementation(libs.junit) - testImplementation(libs.gson.runtime) + testImplementation(libs.gson.runtime) // Record support // Annotation Processors compileOnly(projects.ap) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index bbe6a34aa68..c8fc94ffb26 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import io.netty.channel.epoll.Epoll; import io.netty.util.NettyRuntime; @@ -57,7 +56,11 @@ import org.geysermc.geyser.api.command.CommandSource; import org.geysermc.geyser.api.event.EventBus; import org.geysermc.geyser.api.event.EventRegistrar; -import org.geysermc.geyser.api.event.lifecycle.*; +import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPostReloadEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserPreReloadEvent; +import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; @@ -87,7 +90,14 @@ import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.text.MessageTranslator; -import org.geysermc.geyser.util.*; +import org.geysermc.geyser.util.AssetUtils; +import org.geysermc.geyser.util.CooldownUtils; +import org.geysermc.geyser.util.DimensionUtils; +import org.geysermc.geyser.util.JsonUtils; +import org.geysermc.geyser.util.Metrics; +import org.geysermc.geyser.util.NewsHandler; +import org.geysermc.geyser.util.VersionCheckUtils; +import org.geysermc.geyser.util.WebUtils; import org.geysermc.mcprotocollib.network.tcp.TcpSession; import java.io.File; @@ -101,7 +111,14 @@ import java.nio.file.Path; import java.security.Key; import java.text.DecimalFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; @@ -118,7 +135,7 @@ public class GeyserImpl implements GeyserApi { .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); - public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final Gson GSON = JsonUtils.createGson(); public static final String NAME = "Geyser"; public static final String GIT_VERSION = BuildData.GIT_VERSION; diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java index 2954a445494..582744c8399 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java @@ -30,6 +30,7 @@ import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.pack.ResourcePackManifest; @@ -46,6 +47,7 @@ public record Module(UUID uuid, Version version, String type, String description public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { } + @JsonAdapter(value = Version.VersionDeserializer.class) public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version { @Override diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java index 537c41ed9dc..800a3d22c6e 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.registry.loader; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; import org.geysermc.geyser.api.pack.ResourcePack; @@ -57,9 +55,6 @@ * Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}. */ public class ResourcePackLoader implements RegistryLoader> { - private static final Gson GSON = new GsonBuilder() - .registerTypeAdapter(GeyserResourcePackManifest.Version.class, new GeyserResourcePackManifest.Version.VersionDeserializer()) - .create(); static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}"); @@ -140,7 +135,7 @@ public static GeyserResourcePack readPack(Path path) throws IllegalArgumentExcep } if (name.contains("manifest.json")) { try { - GeyserResourcePackManifest manifest = FileUtils.loadJson(GSON, zip.getInputStream(x), GeyserResourcePackManifest.class); + GeyserResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), GeyserResourcePackManifest.class); if (manifest.header().uuid() != null) { manifestReference.set(manifest); } diff --git a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java index 4a7b7fda6c6..9933a713cb6 100644 --- a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java @@ -30,11 +30,15 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.google.gson.Gson; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.annotation.Annotation; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -65,12 +69,8 @@ public static T loadConfig(File src, Class valueType) throws IOException } public static T loadJson(InputStream src, Class valueType) { - return loadJson(GeyserImpl.GSON, src, valueType); - } - - public static T loadJson(Gson gson, InputStream src, Class valueType) { // Read specifically with UTF-8 to allow any non-UTF-encoded JSON to read - return gson.fromJson(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); + return GeyserImpl.GSON.fromJson(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java index 9ae74b1774b..a1f11e8607c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java @@ -25,9 +25,13 @@ package org.geysermc.geyser.util; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import marcono1234.gson.recordadapter.RecordTypeAdapterFactory; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.pack.GeyserResourcePackManifest; import java.io.InputStream; import java.io.InputStreamReader; @@ -48,6 +52,21 @@ public static T fromJson(InputStream stream, Type type) { return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type); } + public static Gson createGson() { + GsonBuilder builder = new GsonBuilder().setPrettyPrinting(); + try { + new Gson().fromJson("{\"version\":[1,0,0],\"uuid\":\"eebb4ea8-a701-11eb-95ba-047d7bb283ba\"}", GeyserResourcePackManifest.Dependency.class); + } catch (Throwable e) { + // 1.16.5 and 1.17.1 (at minimum) have an outdated Gson version that doesn't support records. + // Remove this workaround when all platforms support Gson 2.10+ + // (Explicitly allow missing component values - the dependencies module for resource packs, for example, can be missing) + builder.registerTypeAdapterFactory(RecordTypeAdapterFactory.builder().allowMissingComponentValues().create()) + // Since this is a record, the above will take precedence unless we explicitly declare it. + .registerTypeAdapter(GeyserResourcePackManifest.Version.class, new GeyserResourcePackManifest.Version.VersionDeserializer()); + } + return builder.create(); + } + private JsonUtils() { } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f57cf79decc..f5ceb160590 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -113,6 +113,7 @@ commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } gson-runtime = { group = "com.google.code.gson", name = "gson", version.ref = "gson-runtime" } +gson-record-factory = { group = "com.github.Marcono1234", name = "gson-record-type-adapter-factory", version = "0.3.0" } junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" } From add02cadd7e43641ce9912a2a56902059ce348be Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:49:26 -0400 Subject: [PATCH 10/42] GeyserCustomSkullConfiguration uses Configurate --- bootstrap/spigot/build.gradle.kts | 5 ++++- .../configuration/GeyserCustomSkullConfiguration.java | 9 ++------- .../populator/CustomSkullRegistryPopulator.java | 2 +- .../main/java/org/geysermc/geyser/util/FileUtils.java | 10 ++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 1f637d8f848..e4161164e78 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -26,7 +26,10 @@ dependencies { compileOnlyApi(libs.viaversion) - implementation(libs.gson.record.factory) // For 1.16.5/1.17.1 + // For 1.16.5/1.17.1 + implementation(libs.gson.record.factory) { + isTransitive = false + } } platformRelocate("it.unimi.dsi.fastutil") diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java index 1af3578a348..46bbb2db1ba 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java @@ -25,26 +25,21 @@ package org.geysermc.geyser.configuration; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; import java.util.Collections; import java.util.List; import java.util.Objects; -@JsonIgnoreProperties(ignoreUnknown = true) +@ConfigSerializable @SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final public class GeyserCustomSkullConfiguration { - @JsonProperty("player-usernames") private List playerUsernames; - @JsonProperty("player-uuids") private List playerUUIDs; - @JsonProperty("player-profiles") private List playerProfiles; - @JsonProperty("skin-hashes") private List skinHashes; public List getPlayerUsernames() { diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java index ec72433964c..8408346907c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java @@ -64,7 +64,7 @@ public static void populate() { GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml"); File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap); - skullConfig = FileUtils.loadConfig(skullConfigFile, GeyserCustomSkullConfiguration.class); + skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class); } catch (IOException e) { GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); return; diff --git a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java index 9933a713cb6..0bf389d3fdf 100644 --- a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java @@ -32,6 +32,8 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.BufferedReader; import java.io.File; @@ -68,6 +70,14 @@ public static T loadConfig(File src, Class valueType) throws IOException return objectMapper.readValue(src, valueType); } + public static T loadConfigNew(File src, Class valueType) throws IOException { + YamlConfigurationLoader loader = YamlConfigurationLoader.builder() + .file(src) + .build(); + ConfigurationNode node = loader.load(); + return node.get(valueType); + } + public static T loadJson(InputStream src, Class valueType) { // Read specifically with UTF-8 to allow any non-UTF-encoded JSON to read return GeyserImpl.GSON.fromJson(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); From db9b9513527d6a5f6f1e9223442090664e2f891e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:35:54 -0400 Subject: [PATCH 11/42] Fix regression in properties get --- .../configuration/GeyserCustomSkullConfiguration.java | 10 ++++++++++ .../populator/CustomSkullRegistryPopulator.java | 1 + .../java/org/geysermc/geyser/skin/SkinProvider.java | 6 ++++-- .../main/java/org/geysermc/geyser/util/AssetUtils.java | 9 +++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java index 46bbb2db1ba..48490fad07d 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserCustomSkullConfiguration.java @@ -57,4 +57,14 @@ public List getPlayerProfiles() { public List getPlayerSkinHashes() { return Objects.requireNonNullElse(skinHashes, Collections.emptyList()); } + + @Override + public String toString() { + return "GeyserCustomSkullConfiguration{" + + "playerUsernames=" + playerUsernames + + ", playerUUIDs=" + playerUUIDs + + ", playerProfiles=" + playerProfiles + + ", skinHashes=" + skinHashes + + '}'; + } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java index 8408346907c..8494edc2bd7 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java @@ -65,6 +65,7 @@ public static void populate() { Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml"); File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap); skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class); + System.out.println(skullConfig); } catch (IOException e) { GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); return; diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 16a939304d5..82c4a75f7ab 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -27,6 +27,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.bytes.ByteArrays; @@ -487,12 +488,12 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E return CompletableFuture.supplyAsync(() -> { try { JsonObject node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid); - JsonObject properties = node.getAsJsonObject("properties"); + JsonArray properties = node.getAsJsonArray("properties"); if (properties == null) { GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuid); return null; } - return node.getAsJsonArray("properties").get(0).getAsJsonObject().get("value").getAsString(); + return properties.get(0).getAsJsonObject().get("value").getAsString(); } catch (Exception e) { GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid); if (GeyserImpl.getInstance().getConfig().isDebugMode()) { @@ -514,6 +515,7 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E try { // Offline skin, or no present UUID JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); + System.out.println(node); JsonElement id = node.get("id"); if (id == null) { GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username); diff --git a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java index 0c07f4237ec..81df9919db5 100644 --- a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java @@ -296,6 +296,15 @@ static class VersionDownload { @SerializedName("url") private String url; + + @Override + public String toString() { + return "VersionDownload{" + + "sha1='" + sha1 + '\'' + + ", size=" + size + + ", url='" + url + '\'' + + '}'; + } } @Getter From 29f8e294ada243874193799ca4e69ca600a018e7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:16:08 -0400 Subject: [PATCH 12/42] New config used in core --- .../bungeecord/GeyserBungeeInjector.java | 4 +- .../GeyserBungeeUpdateListener.java | 2 +- .../platform/mod/GeyserModInjector.java | 4 +- .../mixin/client/IntegratedServerMixin.java | 1 - .../platform/spigot/GeyserSpigotInjector.java | 8 +- .../platform/spigot/GeyserSpigotPlugin.java | 2 +- .../spigot/GeyserSpigotUpdateListener.java | 2 +- .../command/GeyserPaperCommandListener.java | 2 +- .../standalone/GeyserStandaloneBootstrap.java | 175 ++----- .../velocity/GeyserVelocityInjector.java | 13 +- .../GeyserVelocityUpdateListener.java | 2 +- core/build.gradle.kts | 2 + .../java/org/geysermc/geyser/Constants.java | 4 +- .../org/geysermc/geyser/GeyserBootstrap.java | 17 +- .../java/org/geysermc/geyser/GeyserImpl.java | 183 +++---- .../geyser/GeyserPluginBootstrap.java | 36 ++ .../defaults/ConnectionTestCommand.java | 24 +- .../geyser/command/defaults/DumpCommand.java | 33 +- .../configuration/ConfigLoaderTemp.java | 91 +++- .../geyser/configuration/GeyserConfig.java | 65 ++- .../configuration/GeyserPluginConfig.java | 6 + .../configuration/GeyserRemoteConfig.java | 20 +- .../LowercaseEnumSerializer.java | 55 +++ .../org/geysermc/geyser/dump/DumpInfo.java | 23 +- .../geyser/entity/EntityDefinition.java | 2 +- .../geysermc/geyser/entity/type/Entity.java | 2 +- .../geyser/inventory/click/ClickPlan.java | 2 +- .../geyser/network/UpstreamPacketHandler.java | 10 +- .../geyser/network/netty/GeyserInjector.java | 10 +- .../geyser/network/netty/GeyserServer.java | 87 ++-- .../geyser/pack/SkullResourcePackManager.java | 4 +- .../ping/GeyserLegacyPingPassthrough.java | 10 +- .../registry/PacketTranslatorRegistry.java | 2 +- .../CustomBlockRegistryPopulator.java | 2 +- .../CustomSkullRegistryPopulator.java | 3 +- .../populator/ItemRegistryPopulator.java | 2 +- .../geyser/scoreboard/ScoreboardUpdater.java | 8 +- .../geyser/session/GeyserSession.java | 110 ++++- .../session/auth/BedrockClientData.java | 89 ++-- .../session/cache/PreferencesCache.java | 11 +- .../geyser/session/cache/SkullCache.java | 6 +- .../geyser/skin/FloodgateSkinUploader.java | 67 ++- .../org/geysermc/geyser/skin/SkinManager.java | 37 +- .../geysermc/geyser/skin/SkinProvider.java | 18 +- .../geysermc/geyser/text/GeyserLocale.java | 4 +- .../inventory/InventoryTranslator.java | 8 +- .../entity/SkullBlockEntityTranslator.java | 2 +- .../bedrock/BedrockEmoteListTranslator.java | 5 - ...BedrockInventoryTransactionTranslator.java | 4 +- .../BedrockNetworkStackLatencyTranslator.java | 2 +- ...SetLocalPlayerAsInitializedTranslator.java | 2 +- .../entity/player/BedrockEmoteTranslator.java | 10 - .../protocol/java/JavaCommandsTranslator.java | 2 +- .../java/JavaKeepAliveTranslator.java | 2 +- .../java/JavaUpdateRecipesTranslator.java | 2 +- .../entity/JavaSetEntityDataTranslator.java | 2 +- .../player/JavaPlayerPositionTranslator.java | 2 +- .../translator/text/MessageTranslator.java | 2 +- .../geysermc/geyser/util/CooldownUtils.java | 16 +- .../geysermc/geyser/util/InventoryUtils.java | 4 +- .../org/geysermc/geyser/util/JsonUtils.java | 4 + .../geyser/util/LoginEncryptionUtils.java | 14 +- .../org/geysermc/geyser/util/Metrics.java | 447 ------------------ .../geysermc/geyser/util/SettingsUtils.java | 4 +- .../org/geysermc/geyser/util/WebUtils.java | 2 +- gradle/libs.versions.toml | 3 + 66 files changed, 756 insertions(+), 1043 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java delete mode 100644 core/src/main/java/org/geysermc/geyser/util/Metrics.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 7c60ba95d1a..fbd3bbd6e88 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -108,7 +108,7 @@ protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Excepti listenerInfo.isPingPassthrough(), listenerInfo.getQueryPort(), listenerInfo.isQueryEnabled(), - bootstrap.getGeyserConfig().getRemote().isUseProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end + bootstrap.config().java().useProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end ); // The field that stores all listeners in BungeeCord @@ -142,7 +142,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { } initChannel.invoke(channelInitializer, ch); - if (bootstrap.getGeyserConfig().isDisableCompression()) { + if (bootstrap.config().asPluginConfig().orElseThrow().useDirectConnection()) { ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler", new GeyserBungeeCompressionDisabler()); } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java index c68839b2095..8a1244ac643 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeUpdateListener.java @@ -38,7 +38,7 @@ public final class GeyserBungeeUpdateListener implements Listener { @EventHandler public void onPlayerJoin(final PostLoginEvent event) { - if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { + if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) { final ProxiedPlayer player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player)); diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java index 624eccb3f24..89a92d789f1 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java @@ -96,7 +96,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.getGeyserConfig().isDisableCompression()) { + if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression()) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler()); } } @@ -125,7 +125,7 @@ private ChannelInitializer getChildHandler(GeyserBootstrap bootstrap, C childHandler = (ChannelInitializer) childHandlerField.get(handler); break; } catch (Exception e) { - if (bootstrap.getGeyserConfig().isDebugMode()) { + if (bootstrap.config().debugMode()) { bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!"); e.printStackTrace(); } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java index ece2f730a1a..7a4abd50ea0 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java @@ -56,7 +56,6 @@ private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, Cal // If the LAN is opened, starts Geyser. GeyserModBootstrap instance = GeyserModBootstrap.getInstance(); instance.setServer((MinecraftServer) (Object) this); - instance.getGeyserConfig().getRemote().setPort(port); instance.onGeyserEnable(); // Ensure player locale has been loaded, in case it's different from Java system language GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index 5dcfbd0f8b3..d1d4b50ea0b 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -122,7 +122,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { + if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler()); } } @@ -157,7 +157,7 @@ private ChannelInitializer getChildHandler(GeyserBootstrap bootstrap, C } break; } catch (Exception e) { - if (bootstrap.getGeyserConfig().isDebugMode()) { + if (bootstrap.config().debugMode()) { bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!"); e.printStackTrace(); } @@ -176,8 +176,8 @@ private ChannelInitializer getChildHandler(GeyserBootstrap bootstrap, C */ private void workAroundWeirdBug(GeyserBootstrap bootstrap) { MinecraftProtocol protocol = new MinecraftProtocol(); - LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(), - bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress, + LocalSession session = new LocalSession(bootstrap.config().java().address(), + bootstrap.config().java().port(), this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper()); session.connect(); session.disconnect(""); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 8e853b6298e..60be30725ed 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -487,7 +487,7 @@ private boolean loadConfig() { File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); - ConfigLoaderTemp.load(GeyserPluginConfig.class); + ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java index 5e3c4def8e1..d244adddb9c 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotUpdateListener.java @@ -38,7 +38,7 @@ public final class GeyserSpigotUpdateListener implements Listener { @EventHandler public void onPlayerJoin(final PlayerJoinEvent event) { - if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { + if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player)); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java index dcec045abb8..f1f8cb91591 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/GeyserPaperCommandListener.java @@ -76,7 +76,7 @@ private boolean isProbablyJavaPlayer(Player player) { return false; } - if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) { + if (GeyserImpl.getInstance().config().asPluginConfig().orElseThrow().useDirectConnection()) { InetSocketAddress address = player.getAddress(); if (address != null) { return address.getPort() != 0; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 98b67a32b6e..0037e376d69 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -25,11 +25,6 @@ package org.geysermc.geyser.platform.standalone; -import com.fasterxml.jackson.databind.BeanDescription; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.introspect.AnnotatedField; -import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import io.netty.util.ResourceLeakDetector; import lombok.Getter; import net.minecrell.terminalconsole.TerminalConsoleAppender; @@ -44,16 +39,17 @@ import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.ConfigLoaderTemp; -import org.geysermc.geyser.configuration.GeyserConfiguration; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.configuration.GeyserRemoteConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.LoopbackUtil; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.NodePath; +import org.spongepowered.configurate.serialize.SerializationException; import java.io.File; import java.io.IOException; @@ -61,13 +57,13 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; -import java.util.*; -import java.util.stream.Collectors; +import java.util.HashMap; +import java.util.Map; public class GeyserStandaloneBootstrap implements GeyserBootstrap { private GeyserCommandManager geyserCommandManager; - private GeyserStandaloneConfiguration geyserConfig; + private GeyserConfig geyserConfig; private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger(); private IGeyserPingPassthrough geyserPingPassthrough; private GeyserStandaloneGUI gui; @@ -78,15 +74,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { private GeyserImpl geyser; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private static final Map argsConfigKeys = new HashMap<>(); + private static final Map argsConfigKeys = new HashMap<>(); public static void main(String[] args) { if (System.getProperty("io.netty.leakDetection.level") == null) { ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance } + System.setProperty("bstats.relocatecheck", "false"); + GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); // Set defaults boolean useGuiOpts = bootstrap.useGui; @@ -94,8 +90,6 @@ public static void main(String[] args) { GeyserLocale.init(bootstrap); - List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class); - for (int i = 0; i < args.length; i++) { // By default, standalone Geyser will check if it should open the GUI based on if the GUI is null // Optionally, you can force the use of a GUI or no GUI by specifying args @@ -127,34 +121,8 @@ public static void main(String[] args) { // Split the argument by an = String[] argParts = arg.substring(2).split("="); if (argParts.length == 2) { - // Split the config key by . to allow for nested options - String[] configKeyParts = argParts[0].split("\\."); - - // Loop the possible config options to check the passed key is valid - boolean found = false; - for (BeanPropertyDefinition property : availableProperties) { - if (configKeyParts[0].equals(property.getName())) { - if (configKeyParts.length > 1) { - // Loop sub-section options to check the passed key is valid - for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) { - if (configKeyParts[1].equals(subProperty.getName())) { - found = true; - break; - } - } - } else { - found = true; - } - - break; - } - } - - // Add the found key to the stored list for later usage - if (found) { - argsConfigKeys.put(argParts[0], argParts[1]); - break; - } + argsConfigKeys.put(NodePath.of(argParts[0].split("\\.")), argParts[1]); + break; } } System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg)); @@ -192,18 +160,7 @@ public void onGeyserInitialize() { @Override public void onGeyserEnable() { try { - File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); - - ConfigLoaderTemp.load(GeyserRemoteConfig.class); - - handleArgsConfigOptions(); - - if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) { - geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug - geyserConfig.getRemote().setAddress("127.0.0.1"); - } + geyserConfig = ConfigLoaderTemp.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions); } catch (IOException ex) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); if (gui == null) { @@ -213,11 +170,10 @@ public void onGeyserEnable() { return; } } - geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + geyserLogger.setDebug(geyserConfig.debugMode()); // Allow libraries like Protocol to have their debug information passthrough - log4jLogger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); + log4jLogger.get().setLevel(geyserConfig.debugMode() ? Level.DEBUG : Level.INFO); geyser = GeyserImpl.load(PlatformType.STANDALONE, this); @@ -266,8 +222,8 @@ public void onGeyserShutdown() { } @Override - public GeyserConfiguration getGeyserConfig() { - return geyserConfig; + public GeyserConfig config() { + return this.geyserConfig; } @Override @@ -318,100 +274,47 @@ public boolean testFloodgatePluginPresent() { return false; } - /** - * Get the {@link BeanPropertyDefinition}s for the given class - * - * @param clazz The class to get the definitions for - * @return A list of {@link BeanPropertyDefinition} for the given class - */ - public static List getPOJOForClass(Class clazz) { - JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz); - - // Introspect the given type - BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType); - - // Find properties - List properties = beanDescription.findProperties(); - - // Get the ignored properties - Set ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector() - .findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig() ,beanDescription.getClassInfo()).getIgnored(); - - // Filter properties removing the ignored ones - return properties.stream() - .filter(property -> !ignoredProperties.contains(property.getName())) - .collect(Collectors.toList()); + @Override + public Path getFloodgateKeyPath() { + return Path.of(geyserConfig.floodgateKeyFile()); } /** * Set a POJO property value on an object * - * @param property The {@link BeanPropertyDefinition} to set - * @param parentObject The object to alter * @param value The new value of the property */ - @SuppressWarnings({"unchecked", "rawtypes"}) // Required for enum usage - private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) { + private static void setConfigOption(CommentedConfigurationNode node, Object value) throws SerializationException { Object parsedValue = value; // Change the values type if needed - if (int.class.equals(property.getRawPrimaryType())) { + Class clazz = node.raw().getClass(); + if (Integer.class == clazz) { parsedValue = Integer.valueOf((String) parsedValue); - } else if (boolean.class.equals(property.getRawPrimaryType())) { + } else if (Boolean.class == clazz) { parsedValue = Boolean.valueOf((String) parsedValue); - } else if (Enum.class.isAssignableFrom(property.getRawPrimaryType())) { - parsedValue = Enum.valueOf((Class) property.getRawPrimaryType(), ((String) parsedValue).toUpperCase(Locale.ROOT)); } - // Force the value to be set - AnnotatedField field = property.getField(); - field.fixAccess(true); - field.setValue(parentObject, parsedValue); + node.set(parsedValue); } /** - * Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments + * Update the loaded config with any values passed in the command line arguments */ - private void handleArgsConfigOptions() { - // Get the available properties from the class - List availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class); - - for (Map.Entry configKey : argsConfigKeys.entrySet()) { - String[] configKeyParts = configKey.getKey().split("\\."); - - // Loop over the properties looking for any matches against the stored one from the argument - for (BeanPropertyDefinition property : availableProperties) { - if (configKeyParts[0].equals(property.getName())) { - if (configKeyParts.length > 1) { - // Loop through the sub property if the first part matches - for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) { - if (configKeyParts[1].equals(subProperty.getName())) { - geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue())); - - // Set the sub property value on the config - try { - Object subConfig = property.getGetter().callOn(geyserConfig); - setConfigOption(subProperty, subConfig, configKey.getValue()); - } catch (Exception e) { - geyserLogger.error("Failed to set config option: " + property.getFullName()); - } - - break; - } - } - } else { - geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue())); - - // Set the property value on the config - try { - setConfigOption(property, geyserConfig, configKey.getValue()); - } catch (Exception e) { - geyserLogger.error("Failed to set config option: " + property.getFullName()); - } - } + private void handleArgsConfigOptions(CommentedConfigurationNode node) { + for (Map.Entry configKey : argsConfigKeys.entrySet()) { + NodePath path = configKey.getKey(); + CommentedConfigurationNode subNode = node.node(path); + if (subNode.virtual()) { + geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", path)); + continue; + } - break; - } + try { + setConfigOption(subNode, configKey.getValue()); + geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue())); + } catch (SerializationException e) { + geyserLogger.error("Failed to set config option: " + path); } } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java index 68a9eb40b89..caeaf15b579 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java @@ -27,10 +27,15 @@ import com.velocitypowered.api.proxy.ProxyServer; import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.local.LocalAddress; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; @@ -47,7 +52,7 @@ public GeyserVelocityInjector(ProxyServer proxy) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { Field cm = proxy.getClass().getDeclaredField("cm"); cm.setAccessible(true); Object connectionManager = cm.get(proxy); @@ -80,7 +85,7 @@ protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Excepti protected void initChannel(@NonNull Channel ch) throws Exception { initChannel.invoke(channelInitializer, ch); - if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { + if (bootstrap.config().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler", new GeyserVelocityCompressionDisabler()); } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java index 31e5846128b..d7049b5cc5c 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityUpdateListener.java @@ -37,7 +37,7 @@ public final class GeyserVelocityUpdateListener { @Subscribe public void onPlayerJoin(PostLoginEvent event) { - if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { + if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) { final Player player = event.getPlayer(); if (player.hasPermission(Constants.UPDATE_PERMISSION)) { VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player)); diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1e5a9d67593..7d5349f12f0 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -73,6 +73,8 @@ dependencies { annotationProcessor(projects.ap) api(libs.events) + + api(libs.bstats) } tasks.processResources { diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java index 40aa8ada16e..94ff93c670c 100644 --- a/core/src/main/java/org/geysermc/geyser/Constants.java +++ b/core/src/main/java/org/geysermc/geyser/Constants.java @@ -47,6 +47,8 @@ public final class Constants { public static final int CONFIG_VERSION = 5; + public static final int BSTATS_ID = 5273; + static { URI wsUri = null; try { @@ -56,4 +58,4 @@ public final class Constants { } GLOBAL_API_WS_URI = wsUri; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index a9414d9d001..96a0078bdc8 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.command.GeyserCommandManager; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.GeyserWorldManager; @@ -72,7 +73,16 @@ public interface GeyserBootstrap { * * @return The current GeyserConfiguration */ - GeyserConfiguration getGeyserConfig(); + default GeyserConfiguration getGeyserConfig() { + throw new UnsupportedOperationException(); + } + + /** + * Returns the current GeyserConfig + * + * @return The current GeyserConfig + */ + GeyserConfig config(); /** * Returns the current GeyserLogger @@ -189,4 +199,9 @@ default Path getLogsPath() { * Tests if Floodgate is installed, loads the Floodgate key if so, and returns the result of Floodgate installed. */ boolean testFloodgatePluginPresent(); + + /** + * TEMPORARY - will be removed after The Merge:tm:. + */ + Path getFloodgateKeyPath(); } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index c8fc94ffb26..8015dfd1a7b 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -25,9 +25,6 @@ package org.geysermc.geyser; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import io.netty.channel.epoll.Epoll; @@ -39,6 +36,11 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import org.bstats.MetricsBase; +import org.bstats.charts.AdvancedPie; +import org.bstats.charts.DrilldownPie; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -67,7 +69,7 @@ import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.erosion.UnixSocketClientListener; import org.geysermc.geyser.event.GeyserEventBus; @@ -91,10 +93,8 @@ import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.AssetUtils; -import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.JsonUtils; -import org.geysermc.geyser.util.Metrics; import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.VersionCheckUtils; import org.geysermc.geyser.util.WebUtils; @@ -128,13 +128,6 @@ @Getter public class GeyserImpl implements GeyserApi { - public static final ObjectMapper JSON_MAPPER = new ObjectMapper() - .enable(JsonParser.Feature.IGNORE_UNDEFINED) - .enable(JsonParser.Feature.ALLOW_COMMENTS) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) - .enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); - public static final Gson GSON = JsonUtils.createGson(); public static final String NAME = "Geyser"; @@ -180,7 +173,7 @@ public class GeyserImpl implements GeyserApi { private final EventBus eventBus; private final GeyserExtensionManager extensionManager; - private Metrics metrics; + private MetricsBase metrics; private PendingMicrosoftAuthentication pendingMicrosoftAuthentication; @Getter(AccessLevel.NONE) @@ -269,7 +262,7 @@ public void initialize() { startInstance(); - GeyserConfiguration config = bootstrap.getGeyserConfig(); + GeyserConfig config = bootstrap.config(); double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)); @@ -277,11 +270,11 @@ public void initialize() { logger.info(message); if (platformType == PlatformType.STANDALONE) { - if (config.getRemote().authType() != AuthType.FLOODGATE) { + if (config.java().authType() != AuthType.FLOODGATE) { // If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); } - } else if (config.getRemote().authType() == AuthType.FLOODGATE) { + } else if (config.java().authType() == AuthType.FLOODGATE) { VersionCheckUtils.checkForOutdatedFloodgate(logger); } @@ -296,7 +289,7 @@ private void startInstance() { GeyserLocale.finalizeDefaultLocale(this); } GeyserLogger logger = bootstrap.getGeyserLogger(); - GeyserConfiguration config = bootstrap.getGeyserConfig(); + GeyserConfig config = bootstrap.config(); ScoreboardUpdater.init(); @@ -314,31 +307,28 @@ private void startInstance() { 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()); + String serverAddress = bootstrap.getServerBindAddress(); + if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) { + config.java().address(serverAddress); + } else { + // Set the remote address to localhost since that is where we are always connecting + try { + config.java().address(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.debug("Unknown host when trying to find localhost."); + if (config.debugMode()) { + ex.printStackTrace(); } + config.java().address(InetAddress.getLoopbackAddress().getHostAddress()); } - if (javaPort != -1) { - config.getRemote().setPort(javaPort); - } + } + if (javaPort != -1 && config.asPluginConfig().isEmpty()) { + config.java().port(javaPort); } boolean forceMatchServerPort = "server".equals(pluginUdpPort); - if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) { - config.getBedrock().setPort(javaPort); + if ((config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false) || forceMatchServerPort) && javaPort != -1) { + config.bedrock().port(javaPort); if (forceMatchServerPort) { if (geyserUdpPort.isEmpty()) { logger.info("Port set from system generic property to match Java server."); @@ -352,15 +342,15 @@ private void startInstance() { if ("server".equals(pluginUdpAddress)) { String address = bootstrap.getServerBindAddress(); if (!address.isEmpty()) { - config.getBedrock().setAddress(address); + config.bedrock().address(address); } } else if (!pluginUdpAddress.isEmpty()) { - config.getBedrock().setAddress(pluginUdpAddress); + config.bedrock().address(pluginUdpAddress); } if (!portPropertyApplied && !pluginUdpPort.isEmpty()) { int port = Integer.parseInt(pluginUdpPort); - config.getBedrock().setPort(port); + config.bedrock().port(port); if (geyserUdpPort.isEmpty()) { logger.info("Port set from generic system property: " + port); } else { @@ -378,42 +368,44 @@ private void startInstance() { } } catch (NumberFormatException e) { logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); - parsedPort = config.getBedrock().port(); + parsedPort = config.bedrock().port(); } - config.getBedrock().setBroadcastPort(parsedPort); + config.bedrock().broadcastPort(parsedPort); logger.info("Broadcast port set from system property: " + parsedPort); } if (platformType != PlatformType.VIAPROXY) { boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); - if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { + if (config.java().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) { + } else if (floodgatePresent) { // Floodgate installed means that the user wants Floodgate authentication logger.debug("Auto-setting to Floodgate authentication."); - config.getRemote().setAuthType(AuthType.FLOODGATE); + config.java().authType(AuthType.FLOODGATE); } } } - 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 (!remoteAddress.matches(IP_REGEX) && !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 + "\""); + if (config.asPluginConfig().isEmpty()) { + String remoteAddress = config.java().address(); + // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. + if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { + String[] record = WebUtils.findSrvRecord(this, remoteAddress); + if (record != null) { + int remotePort = Integer.parseInt(record[2]); + config.java().address(remoteAddress = record[3]); + config.java().port(remotePort); + logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); + } } } // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; - pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); + pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout()); this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); @@ -425,8 +417,7 @@ private void startInstance() { logger.debug("Epoll is not available; Erosion's Unix socket handling will not work."); } - CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); - DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether + DimensionUtils.changeBedrockNetherId(config.aboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); if (bedrockThreadCount == null) { @@ -436,14 +427,14 @@ private void startInstance() { if (shouldStartListener) { this.geyserServer = new GeyserServer(this, bedrockThreadCount); - this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())) + this.geyserServer.bind(new InetSocketAddress(config.bedrock().address(), config.bedrock().port())) .whenComplete((avoid, throwable) -> { if (throwable == null) { - logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(), - String.valueOf(config.getBedrock().port()))); + logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.bedrock().address(), + String.valueOf(config.bedrock().port()))); } else { - String address = config.getBedrock().address(); - int port = config.getBedrock().port(); + String address = config.bedrock().address(); + int port = config.bedrock().port(); logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(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)); @@ -453,9 +444,9 @@ private void startInstance() { }).join(); } - if (config.getRemote().authType() == AuthType.FLOODGATE) { + if (config.java().authType() == AuthType.FLOODGATE) { try { - Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); + Key key = new AesKeyProducer().produceFrom(bootstrap.getFloodgateKeyPath()); cipher = new AesCipher(new Base64Topping()); cipher.init(key); logger.debug("Loaded Floodgate key!"); @@ -467,15 +458,39 @@ private void startInstance() { } } - 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)); + if (config.metrics().enabled()) { + metrics = new MetricsBase( + "server-implementation", + config.metrics().uuid().toString(), + Constants.BSTATS_ID, + true, // Already checked above. + builder -> { + // OS specific data + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + builder.appendField("osName", osName); + builder.appendField("osArch", osArch); + builder.appendField("osVersion", osVersion); + builder.appendField("coreCount", coreCount); + }, + builder -> {}, + null, + () -> true, + logger::error, + logger::info, + config.debugMode(), + config.debugMode(), + config.debugMode()); + metrics.addCustomChart(new SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can - metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); - metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::platformName)); - metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); - metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); - metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { + metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT))); + metrics.addCustomChart(new SimplePie("platform", platformType::platformName)); + metrics.addCustomChart(new SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); + metrics.addCustomChart(new SimplePie("version", () -> GeyserImpl.VERSION)); + metrics.addCustomChart(new AdvancedPie("playerPlatform", () -> { Map valueMap = new HashMap<>(); for (GeyserSession session : sessionManager.getAllSessions()) { if (session == null) continue; @@ -489,7 +504,7 @@ private void startInstance() { } return valueMap; })); - metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { + metrics.addCustomChart(new AdvancedPie("playerVersion", () -> { Map valueMap = new HashMap<>(); for (GeyserSession session : sessionManager.getAllSessions()) { if (session == null) continue; @@ -511,7 +526,7 @@ private void startInstance() { platformMap.put(platformType.platformName(), 1); versionMap.put(minecraftVersion, platformMap); - metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> { + metrics.addCustomChart(new DrilldownPie("minecraftServerVersion", () -> { // By the end, we should return, for example: // 1.16.5 => (Spigot, 1) return versionMap; @@ -520,7 +535,7 @@ private void startInstance() { // 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", () -> { + metrics.addCustomChart(new DrilldownPie("javaVersion", () -> { Map> map = new HashMap<>(); String javaVersion = System.getProperty("java.version"); Map entry = new HashMap<>(); @@ -555,7 +570,7 @@ private void startInstance() { metrics = null; } - if (config.getRemote().authType() == AuthType.ONLINE) { + if (config.java().authType() == AuthType.ONLINE) { // May be written/read to on multiple threads from each GeyserSession as well as writing the config savedRefreshTokens = new ConcurrentHashMap<>(); @@ -570,7 +585,7 @@ private void startInstance() { logger.error("Cannot load saved user tokens!", e); } if (refreshTokenFile != null) { - List validUsers = config.getSavedUserLogins(); + List validUsers = config.savedUserLogins(); boolean doWrite = false; for (Map.Entry entry : refreshTokenFile.entrySet()) { String user = entry.getKey(); @@ -598,7 +613,7 @@ private void startInstance() { this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); } - if (config.isNotifyOnNewBedrockUpdate()) { + if (config.notifyOnNewBedrockUpdate()) { VersionCheckUtils.checkForGeyserUpdate(this::getLogger); } } @@ -750,13 +765,13 @@ public EventBus eventBus() { @NonNull public RemoteServer defaultRemoteServer() { - return getConfig().getRemote(); + return config().java(); } @Override @NonNull public BedrockListener bedrockListener() { - return getConfig().getBedrock(); + return config().bedrock(); } @Override @@ -830,8 +845,8 @@ public GeyserLogger getLogger() { return bootstrap.getGeyserLogger(); } - public GeyserConfiguration getConfig() { - return bootstrap.getGeyserConfig(); + public GeyserConfig config() { + return bootstrap.config(); } public WorldManager getWorldManager() { @@ -844,7 +859,7 @@ public String refreshTokenFor(@NonNull String bedrockName) { } public void saveRefreshToken(@NonNull String bedrockName, @NonNull String refreshToken) { - if (!getConfig().getSavedUserLogins().contains(bedrockName)) { + if (!config().savedUserLogins().contains(bedrockName)) { // Do not save this login return; } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java new file mode 100644 index 00000000000..ed05ec1f31e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 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; + +import org.geysermc.geyser.configuration.GeyserPluginConfig; + +/** + * Used in any instance where Geyser is directly attached to a server instance of some sort. + */ +public interface GeyserPluginBootstrap extends GeyserBootstrap { + @Override + GeyserPluginConfig config(); +} diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index 84da7323942..2a4a2b1afc0 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -32,7 +32,7 @@ import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandSource; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoopbackUtil; @@ -86,7 +86,7 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, return; } } else { - port = geyser.getConfig().getBedrock().broadcastPort(); + port = geyser.config().bedrock().broadcastPort(); } String ip = fullAddress[0]; @@ -114,41 +114,41 @@ public void execute(@Nullable GeyserSession session, GeyserCommandSource sender, return; } - GeyserConfiguration config = geyser.getConfig(); + GeyserConfig config = geyser.config(); // Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing - if (config.getBedrock().broadcastPort() == config.getBedrock().port()) { - if (port != config.getBedrock().port()) { + if (config.bedrock().broadcastPort() == config.bedrock().port()) { + if (port != config.bedrock().port()) { if (fullAddress.length == 2) { sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration (" - + config.getBedrock().port() + ")"); + + config.bedrock().port() + ")"); sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config."); - if (config.getBedrock().isCloneRemotePort()) { + if (config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false)) { sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead."); } } else { sender.sendMessage("You did not specify the port to check (add it with \":\"), " + "and the default port 19132 does not match the port in your Geyser configuration (" - + config.getBedrock().port() + ")!"); + + config.bedrock().port() + ")!"); sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`."); } } } else { - if (config.getBedrock().broadcastPort() != port) { + if (config.bedrock().broadcastPort() != port) { sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration (" - + config.getBedrock().broadcastPort() + "). "); + + config.bedrock().broadcastPort() + "). "); sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on."); sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config."); } } // Issue: is the `bedrock` `address` in the config different? - if (!config.getBedrock().address().equals("0.0.0.0")) { + if (!config.bedrock().address().equals("0.0.0.0")) { sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional."); } // Issue: did someone turn on enable-proxy-protocol, and they didn't mean it? - if (config.getBedrock().isEnableProxyProtocol()) { + if (config.bedrock().enableProxyProtocol()) { sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " + "Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled."); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java index b3fee375f65..8bcadcc7331 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java @@ -25,13 +25,14 @@ package org.geysermc.geyser.command.defaults; -import com.fasterxml.jackson.core.util.DefaultIndenter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.dump.DumpInfo; @@ -49,7 +50,6 @@ public class DumpCommand extends GeyserCommand { private final GeyserImpl geyser; - private static final ObjectMapper MAPPER = new ObjectMapper(); private static final String DUMP_URL = "https://dump.geysermc.org/"; public DumpCommand(GeyserImpl geyser, String name, String description, String permission) { @@ -81,18 +81,13 @@ public void execute(GeyserSession session, GeyserCommandSource sender, String[] AsteriskSerializer.showSensitive = showSensitive; + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale())); String dumpData; try { - if (offlineDump) { - DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); - // Make arrays easier to read - prettyPrinter.indentArraysWith(new DefaultIndenter(" ", "\n")); - dumpData = MAPPER.writer(prettyPrinter).writeValueAsString(new DumpInfo(addLog)); - } else { - dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog)); - } - } catch (IOException e) { + dumpData = gson.toJson(new DumpInfo(addLog)); + } catch (JsonSyntaxException e) { sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); return; @@ -118,10 +113,10 @@ public void execute(GeyserSession session, GeyserCommandSource sender, String[] sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale())); String response; - JsonNode responseNode; + JsonObject responseNode; try { response = WebUtils.post(DUMP_URL + "documents", dumpData); - responseNode = MAPPER.readTree(response); + responseNode = (JsonObject) new JsonParser().parse(response); } catch (IOException e) { sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); @@ -129,11 +124,11 @@ public void execute(GeyserSession session, GeyserCommandSource sender, String[] } if (!responseNode.has("key")) { - sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); + sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").getAsString() : response)); return; } - uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); + uploadedDumpUrl = DUMP_URL + responseNode.get("key").getAsString(); } sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java index 6d51a28477e..59a68ba0e43 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java @@ -25,18 +25,21 @@ package org.geysermc.geyser.configuration; -import org.spongepowered.configurate.ConfigurationNode; -import org.spongepowered.configurate.NodePath; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; import org.spongepowered.configurate.transformation.ConfigurationTransformation; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.function.Consumer; -public class ConfigLoaderTemp { +import static org.spongepowered.configurate.NodePath.path; +import static org.spongepowered.configurate.transformation.TransformAction.remove; +import static org.spongepowered.configurate.transformation.TransformAction.rename; + +public final class ConfigLoaderTemp { private static final String HEADER = """ -------------------------------- Geyser Configuration File @@ -51,35 +54,81 @@ public class ConfigLoaderTemp { In most cases, especially with server hosting providers, further hosting-specific configuration is required. --------------------------------"""; - public static T load(Class configClass) throws IOException { + public static T load(File file, Class configClass) throws IOException { + return load(file, configClass, null); + } + + public static T load(File file, Class configClass, @Nullable Consumer transformer) throws IOException { var loader = YamlConfigurationLoader.builder() - .file(new File("newconfig.yml")) - .defaultOptions(options -> InterfaceDefaultOptions.addTo(options.header(HEADER))) + .file(file) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options + .header(HEADER) + .serializers(builder -> builder.register(new LowercaseEnumSerializer())))) .build(); - ConfigurationNode node = loader.load(); + var node = loader.load(); // temp fix for node.virtual() being broken - var virtual = !Files.exists(Path.of("newconfig.yml")); + boolean virtual = file.exists(); - // TODO needed or else Configurate breaks + // Note for Tim? Needed or else Configurate breaks. var migrations = ConfigurationTransformation.versionedBuilder() + .versionKey("config-version") // Pre-Configurate .addVersion(5, ConfigurationTransformation.builder() - .addAction(NodePath.path("legacyPingPassthrough"), (path, value) -> { - // Invert value - value.set(Boolean.FALSE.equals(value.get(boolean.class))); - return new Object[]{"integratedPingPassthrough"}; - }) - .addAction(NodePath.path("remote"), (path, value) -> - new Object[]{"java"}) - .build()) + .addAction(path("legacy-ping-passthrough"), configClass == GeyserRemoteConfig.class ? remove() : (path, value) -> { + // Invert value + value.set(!value.getBoolean()); + return new Object[]{"integrated-ping-passthrough"}; + }) + .addAction(path("remote"), rename("java")) + .addAction(path("floodgate-key-file"), (path, value) -> { + // Elimate any legacy config values + if ("public-key.pem".equals(value.getString())) { + value.set("key.pem"); + } + return null; + }) + .addAction(path("default-locale"), (path, value) -> { + if (value.getString() == null) { + value.set("system"); + } + return null; + }) + .addAction(path("show-cooldown"), (path, value) -> { + String s = value.getString(); + if (s != null) { + switch (s) { + case "true" -> value.set("title"); + case "false" -> value.set("disabled"); + } + } + return null; + }) + .addAction(path("bedrock", "motd1"), rename("primary-motd")) + .addAction(path("bedrock", "motd2"), rename("secondary-motd")) + // Legacy config values + .addAction(path("emote-offhand-workaround"), remove()) + .addAction(path("allow-third-party-capes"), remove()) + .addAction(path("allow-third-party-ears"), remove()) + .addAction(path("general-thread-pool"), remove()) + .addAction(path("cache-chunks"), remove()) + .build()) .build(); + int currentVersion = migrations.version(node); migrations.apply(node); + int newVersion = migrations.version(node); T config = node.get(configClass); - System.out.println(config); - loader.save(node); + if (virtual || currentVersion != newVersion) { + loader.save(node); + } + + if (transformer != null) { + // Do not let the transformer change the node. + transformer.accept(node); + config = node.get(configClass); + } return config; } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 059d1c548c9..6828200eb1c 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -31,6 +31,7 @@ import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.util.CooldownUtils; import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; @@ -39,8 +40,9 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; -import java.nio.file.Path; +import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.UUID; @ConfigSerializable @@ -49,7 +51,9 @@ public interface GeyserConfig { JavaConfig java(); - Path floodgateKeyPath(); + // Cannot be type File yet because we want to hide it in plugin instances. + @DefaultString("key.pem") + String floodgateKeyFile(); @Comment(""" For online mode authentication type only. @@ -108,8 +112,9 @@ default List savedUserLogins() { https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 This setting can be set to "title", "actionbar" or "false\"""") - @DefaultString("title") - String showCooldown(); + default CooldownUtils.CooldownType showCooldown() { + return CooldownUtils.CooldownType.TITLE; + } @Comment("Controls if coordinates are shown to players.") @DefaultBoolean(true) @@ -118,9 +123,9 @@ default List savedUserLogins() { @Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.") boolean disableBedrockScaffolding(); - //@DefaultString("disabled") - EmoteOffhandWorkaroundOption emoteOffhandWorkaround(); - + @Comment("The default locale if we don't have the one the client requested. If set to \"system\", the system's language will be used.") + @NonNull + @DefaultString("system") String defaultLocale(); @Comment(""" @@ -207,13 +212,15 @@ interface BedrockConfig extends BedrockListener { @Override @Comment("The port that will listen for connections") @DefaultNumeric(19132) + @NumericRange(from = 0, to = 65535) int port(); @Override @Comment(""" The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. - DO NOT uncomment and change this unless Geyser runs on a different internal port than the one that is used to connect.""") + DO NOT change this unless Geyser runs on a different internal port than the one that is used to connect.""") @DefaultNumeric(19132) + @NumericRange(from = 0, to = 65535) int broadcastPort(); void address(String address); @@ -223,13 +230,21 @@ interface BedrockConfig extends BedrockListener { void broadcastPort(int broadcastPort); @Override + @Comment(""" + The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true. + If either of these are empty, the respective string will default to "Geyser\"""") @DefaultString("Geyser") String primaryMotd(); @Override - @DefaultString("Another Geyser server.") // TODO migrate or change name + @DefaultString("Another Geyser server.") String secondaryMotd(); + @Override + @Comment("The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu.") + @DefaultString("Geyser") + String serverName(); + @Comment(""" How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable.""") @@ -237,6 +252,9 @@ interface BedrockConfig extends BedrockListener { @NumericRange(from = -1, to = 9) int compressionLevel(); + @Comment(""" + Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy + in front of your Geyser instance.""") @DefaultBoolean boolean enableProxyProtocol(); @@ -245,13 +263,9 @@ interface BedrockConfig extends BedrockListener { should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.). Keeping this list empty means there is no IP address whitelist. IP addresses, subnets, and links to plain text files are supported.""") - List proxyProtocolWhitelistedIPs(); - -// /** -// * @return Unmodifiable list of {@link CIDRMatcher}s from {@link #proxyProtocolWhitelistedIPs()} -// */ -// @Exclude -// List whitelistedIPsMatchers(); + default List proxyProtocolWhitelistedIps() { + return Collections.emptyList(); + } } @ConfigSerializable @@ -270,6 +284,8 @@ default AuthType authType() { return AuthType.ONLINE; } + void authType(AuthType authType); + @Comment(""" Whether to enable PROXY protocol or not while connecting to the server. This is useful only when: @@ -297,17 +313,16 @@ default int protocolVersion() { default boolean resolveSrv() { return false; } - - void authType(AuthType authType); } @ConfigSerializable interface MetricsInfo { - + @Comment("If metrics should be enabled") @DefaultBoolean(true) boolean enabled(); - default UUID uniqueId() { //TODO rename? + @Comment("UUID of server. Don't change!") + default UUID uuid() { //TODO rename? return UUID.randomUUID(); } } @@ -333,7 +348,15 @@ default UUID uniqueId() { //TODO rename? int mtu(); @Comment("Do not change!") - default int version() { + default int configVersion() { return Constants.CONFIG_VERSION; } + + @Exclude + default Optional asPluginConfig() { + if (this instanceof GeyserPluginConfig config) { + return Optional.of(config); + } + return Optional.empty(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index 0676f0e8cbb..c2f9b97a0c5 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -32,6 +32,8 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; +import java.io.File; + @ConfigSerializable public interface GeyserPluginConfig extends GeyserConfig { @Override @@ -89,6 +91,10 @@ default boolean forwardHostname() { } } + @Override + @Hidden + String floodgateKeyFile(); + @Comment(""" Use server API methods to determine the Java server's MOTD and ping passthrough. There is no need to disable this unless your MOTD or player count does not appear properly.""") diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index e44fdedd0a7..fa3e53aec80 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -25,10 +25,9 @@ package org.geysermc.geyser.configuration; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.network.AuthType; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; +import org.spongepowered.configurate.interfaces.meta.range.NumericRange; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; @@ -37,12 +36,17 @@ */ @ConfigSerializable public interface GeyserRemoteConfig extends GeyserConfig { -// @Override // For config placement -// BedrockConfig bedrock(); - @Override RemoteConfig java(); + @Override + @Comment(""" + Floodgate uses encryption to ensure use from authorized sources. + This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) + You can ignore this when not using Floodgate. + If you're using a plugin version of Floodgate on the same server, the key will automatically be picked up from Floodgate.""") + String floodgateKeyFile(); + @ConfigSerializable interface RemoteConfig extends JavaConfig { @Override @@ -53,13 +57,9 @@ interface RemoteConfig extends JavaConfig { @Override @Comment("The port of the Java Edition server.") @DefaultNumeric(25565) + @NumericRange(from = 0, to = 65535) int port(); - @Override // For config placement - default @NonNull AuthType authType() { - return JavaConfig.super.authType(); - } - @Override @Comment(""" Forward the hostname that the Bedrock client used to connect over to the Java server diff --git a/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java b/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java new file mode 100644 index 00000000000..5414ab2e20d --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/LowercaseEnumSerializer.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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.configuration; + +import io.leangen.geantyref.TypeToken; +import org.spongepowered.configurate.serialize.ScalarSerializer; +import org.spongepowered.configurate.serialize.Scalars; +import org.spongepowered.configurate.serialize.SerializationException; + +import java.lang.reflect.Type; +import java.util.Locale; +import java.util.function.Predicate; + +/** + * Ensures enum values are written to lowercase. {@link Scalars#ENUM} will read enum values + * in any case. + */ +final class LowercaseEnumSerializer extends ScalarSerializer> { + LowercaseEnumSerializer() { + super(new TypeToken>() {}); + } + + @Override + public Enum deserialize(Type type, Object obj) throws SerializationException { + return Scalars.ENUM.deserialize(type, obj); + } + + @Override + protected Object serialize(Enum item, Predicate> typeSupported) { + return item.name().toLowerCase(Locale.ROOT); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index bd8d85440fc..a6186026ce5 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -25,13 +25,12 @@ package org.geysermc.geyser.dump; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; import com.google.common.io.Files; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; @@ -41,7 +40,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.AsteriskSerializer; @@ -57,12 +56,16 @@ import java.net.Socket; import java.net.UnknownHostException; import java.nio.file.Paths; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.stream.Collectors; @Getter public class DumpInfo { - @JsonIgnore private static final long MEGABYTE = 1024L * 1024L; private final DumpInfo.VersionInfo versionInfo; @@ -71,7 +74,7 @@ public class DumpInfo { private final Locale systemLocale; private final String systemEncoding; private final GitInfo gitInfo; - private final GeyserConfiguration config; + private final GeyserConfig config; private final Floodgate floodgate; private final Object2IntMap userPlatforms; private final int connectionAttempts; @@ -92,7 +95,7 @@ public DumpInfo(boolean addLog) { this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); - this.config = GeyserImpl.getInstance().getConfig(); + this.config = GeyserImpl.getInstance().config(); this.floodgate = new Floodgate(); String md5Hash = "unknown"; @@ -108,7 +111,7 @@ public DumpInfo(boolean addLog) { //noinspection UnstableApiUsage sha256Hash = byteSource.hash(Hashing.sha256()).toString(); } catch (Exception e) { - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } } @@ -280,7 +283,7 @@ public FlagsInfo() { public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List authors) { } - public record GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash, - @JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) { + public record GitInfo(String buildNumber, @SerializedName("git.commit.id.abbrev") String commitHashAbbrev, @SerializedName("git.commit.id") String commitHash, + @SerializedName("git.branch") String branchName, @SerializedName("git.remote.origin.url") String originUrl) { } } diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java index f9b65a545ff..85458abb6c6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinition.java @@ -71,7 +71,7 @@ public void translateMetadata(T entity, EntityMetadata bind(InetSocketAddress address) { @@ -170,12 +174,12 @@ private void modifyHandlers(ChannelFuture future) { .addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this)); // Add proxy handler - boolean isProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol(); + boolean isProxyProtocol = this.geyser.config().bedrock().enableProxyProtocol(); if (isProxyProtocol) { channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler()); } - boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty(); + boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.config().bedrock().proxyProtocolWhitelistedIps().isEmpty(); if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) { // We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter channel.pipeline().remove(RakServerRateLimiter.NAME); @@ -205,7 +209,7 @@ public void shutdown() { } private ServerBootstrap createBootstrap() { - if (this.geyser.getConfig().isDebugMode()) { + if (this.geyser.config().debugMode()) { this.geyser.getLogger().debug("EventLoop type: " + TRANSPORT.datagramChannel()); if (TRANSPORT.datagramChannel() == NioDatagramChannel.class) { if (System.getProperties().contains("disableNativeEventLoop")) { @@ -220,7 +224,7 @@ private ServerBootstrap createBootstrap() { GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser); playerGroup = serverInitializer.getEventLoopGroup(); - this.geyser.getLogger().debug("Setting MTU to " + this.geyser.getConfig().getMtu()); + this.geyser.getLogger().debug("Setting MTU to " + this.geyser.config().mtu()); int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT); this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit); @@ -235,7 +239,7 @@ private ServerBootstrap createBootstrap() { .channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel())) .group(group, childGroup) .option(RakChannelOption.RAK_HANDLE_PING, true) - .option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu()) + .option(RakChannelOption.RAK_MAX_MTU, this.geyser.config().mtu()) .option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) .option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit) .option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie) @@ -243,10 +247,10 @@ private ServerBootstrap createBootstrap() { } public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) { - List allowedProxyIPs = geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs(); - if (geyser.getConfig().getBedrock().isEnableProxyProtocol() && !allowedProxyIPs.isEmpty()) { + List allowedProxyIPs = geyser.config().bedrock().proxyProtocolWhitelistedIps(); + if (geyser.config().bedrock().enableProxyProtocol() && !allowedProxyIPs.isEmpty()) { boolean isWhitelistedIP = false; - for (CIDRMatcher matcher : geyser.getConfig().getBedrock().getWhitelistedIPsMatchers()) { + for (CIDRMatcher matcher : getWhitelistedIPsMatchers()) { if (matcher.matches(inetSocketAddress.getAddress())) { isWhitelistedIP = true; break; @@ -260,8 +264,8 @@ public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) { } String ip; - if (geyser.getConfig().isLogPlayerIpAddresses()) { - if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) { + if (geyser.config().logPlayerIpAddresses()) { + if (geyser.config().bedrock().enableProxyProtocol()) { ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString(); } else { ip = inetSocketAddress.toString(); @@ -287,10 +291,10 @@ public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) { } public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) { - if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) { + if (geyser.config().debugMode() && PRINT_DEBUG_PINGS) { String ip; - if (geyser.getConfig().isLogPlayerIpAddresses()) { - if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) { + if (geyser.config().logPlayerIpAddresses()) { + if (geyser.config().bedrock().enableProxyProtocol()) { ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString(); } else { ip = inetSocketAddress.toString(); @@ -301,10 +305,10 @@ public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip)); } - GeyserConfiguration config = geyser.getConfig(); + GeyserConfig config = geyser.config(); GeyserPingInfo pingInfo = null; - if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) { + if (config.passthroughMotd() || config.passthroughPlayerCounts()) { IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough(); if (pingPassthrough != null) { pingInfo = pingPassthrough.getPingInformation(inetSocketAddress); @@ -321,25 +325,25 @@ public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) .ipv6Port(this.broadcastPort) .serverId(channel.config().getOption(RakChannelOption.RAK_GUID)); - if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { + if (config.passthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); - String mainMotd = (motd.length > 0) ? motd[0] : config.getBedrock().primaryMotd(); // First line of the motd. - String subMotd = (motd.length > 1) ? motd[1] : config.getBedrock().secondaryMotd(); // Second line of the motd if present, otherwise default. + String mainMotd = (motd.length > 0) ? motd[0] : config.bedrock().primaryMotd(); // First line of the motd. + String subMotd = (motd.length > 1) ? motd[1] : config.bedrock().secondaryMotd(); // Second line of the motd if present, otherwise default. pong.motd(mainMotd.trim()); pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. } else { - pong.motd(config.getBedrock().primaryMotd()); - pong.subMotd(config.getBedrock().secondaryMotd()); + pong.motd(config.bedrock().primaryMotd()); + pong.subMotd(config.bedrock().secondaryMotd()); } // Placed here to prevent overriding values set in the ping event. - if (config.isPassthroughPlayerCounts() && pingInfo != null) { + if (config.passthroughPlayerCounts() && pingInfo != null) { pong.playerCount(pingInfo.getPlayers().getOnline()); pong.maximumPlayerCount(pingInfo.getPlayers().getMax()); } else { pong.playerCount(geyser.getSessionManager().getSessions().size()); - pong.maximumPlayerCount(config.getMaxPlayers()); + pong.maximumPlayerCount(config.maxPlayers()); } this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress)); @@ -390,6 +394,35 @@ public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) return pong; } + private List whitelistedIPsMatchers = null; + + /** + * @return Unmodifiable list of {@link CIDRMatcher}s from {@link GeyserConfig.BedrockConfig#proxyProtocolWhitelistedIps()} + */ + public List getWhitelistedIPsMatchers() { + // Effective Java, Third Edition; Item 83: Use lazy initialization judiciously + List matchers = this.whitelistedIPsMatchers; + if (matchers == null) { + synchronized (this) { + // Check if proxyProtocolWhitelistedIPs contains URLs we need to fetch and parse by line + List whitelistedCIDRs = new ArrayList<>(); + for (String ip: geyser.config().bedrock().proxyProtocolWhitelistedIps()) { + if (!ip.startsWith("http")) { + whitelistedCIDRs.add(ip); + continue; + } + + WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add); + } + + this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream() + .map(CIDRMatcher::new) + .collect(Collectors.toList()); + } + } + return Collections.unmodifiableList(matchers); + } + /** * @return the throwable from the given supplier, or the throwable caught while calling the supplier. */ diff --git a/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java index 59651d1393a..03cdc60b27f 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java +++ b/core/src/main/java/org/geysermc/geyser/pack/SkullResourcePackManager.java @@ -78,7 +78,7 @@ public class SkullResourcePackManager { Path packPath = cachePath.resolve("player_skulls.mcpack"); File packFile = packPath.toFile(); - if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { + if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().config().addNonBedrockItems()) { packFile.delete(); // No need to keep resource pack return null; } @@ -161,7 +161,7 @@ public static void cleanSkullSkinCache() { } } catch (IOException e) { GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache."); - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index ed08801d995..5ceeb1651a5 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -65,10 +65,10 @@ public GeyserLegacyPingPassthrough(GeyserImpl geyser) { * @return GeyserPingPassthrough, or null if not initialized */ public static @Nullable IGeyserPingPassthrough init(GeyserImpl geyser) { - if (geyser.getConfig().isPassthroughMotd() || geyser.getConfig().isPassthroughPlayerCounts()) { + if (geyser.config().passthroughMotd() || geyser.config().passthroughPlayerCounts()) { GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(geyser); // Ensure delay is not zero - int interval = (geyser.getConfig().getPingPassthroughInterval() == 0) ? 1 : geyser.getConfig().getPingPassthroughInterval(); + int interval = (geyser.config().pingPassthroughInterval() == 0) ? 1 : geyser.config().pingPassthroughInterval(); geyser.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s)."); geyser.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS); return pingPassthrough; @@ -84,8 +84,8 @@ public GeyserPingInfo getPingInformation(InetSocketAddress inetSocketAddress) { @Override public void run() { try (Socket socket = new Socket()) { - String address = geyser.getConfig().getRemote().address(); - int port = geyser.getConfig().getRemote().port(); + String address = geyser.config().java().address(); + int port = geyser.config().java().port(); InetSocketAddress endpoint = new InetSocketAddress(address, port); socket.connect(endpoint, 5000); @@ -102,7 +102,7 @@ public void run() { byte[] buffer; try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) { - if (geyser.getConfig().getRemote().isUseProxyProtocol()) { + if (geyser.config().java().useProxyProtocol()) { // HAProxy support // Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78 dataOutputStream.write(HAPROXY_BINARY_PREFIX); diff --git a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java index 9a5b43816fd..283a8b8695c 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java @@ -70,7 +70,7 @@ public

boolean translate(Class clazz, P packet, Geyse } return true; } else { - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { if (!IGNORED_PACKETS.contains(clazz)) { GeyserImpl.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet)); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java index a43df3f52cf..734f1ccb9a8 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomBlockRegistryPopulator.java @@ -90,7 +90,7 @@ public enum Stage { * @param stage the stage to populate */ public static void populate(Stage stage) { - if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { + if (!GeyserImpl.getInstance().config().addNonBedrockItems()) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java index 8494edc2bd7..e44550d71a2 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java @@ -55,7 +55,7 @@ public static void populate() { SkullResourcePackManager.SKULL_SKINS.clear(); // Remove skins after reloading BlockRegistries.CUSTOM_SKULLS.set(Object2ObjectMaps.emptyMap()); - if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { + if (!GeyserImpl.getInstance().config().addNonBedrockItems()) { return; } @@ -65,7 +65,6 @@ public static void populate() { Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml"); File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap); skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class); - System.out.println(skullConfig); } catch (IOException e) { GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); return; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index 32d420efaa3..551770b8a1d 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -129,7 +129,7 @@ public static void populate() { throw new AssertionError("Unable to load Bedrock item components", e); } - boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); + boolean customItemsAllowed = GeyserImpl.getInstance().config().addNonBedrockItems(); // List values here is important compared to HashSet - we need to preserve the order of what's given to us // (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java index 395eb957660..6c9f8a6957e 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java @@ -28,7 +28,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.text.GeyserLocale; @@ -46,9 +46,9 @@ public final class ScoreboardUpdater extends Thread { private static final boolean DEBUG_ENABLED; static { - GeyserConfiguration config = GeyserImpl.getInstance().getConfig(); - FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); - DEBUG_ENABLED = config.isDebugMode(); + GeyserConfig config = GeyserImpl.getInstance().config(); + FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); + DEBUG_ENABLED = config.debugMode(); } private final GeyserImpl geyser = GeyserImpl.getInstance(); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 7f015a05eff..2d5f2cbdceb 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -46,17 +46,52 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; -import org.cloudburstmc.math.vector.*; +import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.math.vector.Vector2i; +import org.cloudburstmc.math.vector.Vector3d; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.data.*; +import org.cloudburstmc.protocol.bedrock.data.Ability; +import org.cloudburstmc.protocol.bedrock.data.AbilityLayer; +import org.cloudburstmc.protocol.bedrock.data.AuthoritativeMovementMode; +import org.cloudburstmc.protocol.bedrock.data.ChatRestrictionLevel; +import org.cloudburstmc.protocol.bedrock.data.ExperimentData; +import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; +import org.cloudburstmc.protocol.bedrock.data.GameRuleData; +import org.cloudburstmc.protocol.bedrock.data.GameType; +import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; +import org.cloudburstmc.protocol.bedrock.data.SoundEvent; +import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; -import org.cloudburstmc.protocol.bedrock.packet.*; +import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket; +import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; +import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket; +import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket; +import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; +import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket; +import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; +import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; +import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; +import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet; +import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; +import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; +import org.cloudburstmc.protocol.bedrock.packet.SyncEntityPropertyPacket; +import org.cloudburstmc.protocol.bedrock.packet.TextPacket; +import org.cloudburstmc.protocol.bedrock.packet.TransferPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAbilitiesPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAdventureSettingsPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateSoftEnumPacket; import org.cloudburstmc.protocol.common.DefinitionRegistry; import org.cloudburstmc.protocol.common.util.OptionalBoolean; import org.geysermc.api.util.BedrockPlatform; @@ -80,7 +115,6 @@ import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandSource; -import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.GeyserEntityData; @@ -107,7 +141,22 @@ import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.BedrockClientData; -import org.geysermc.geyser.session.cache.*; +import org.geysermc.geyser.session.cache.AdvancementsCache; +import org.geysermc.geyser.session.cache.BookEditCache; +import org.geysermc.geyser.session.cache.ChunkCache; +import org.geysermc.geyser.session.cache.EntityCache; +import org.geysermc.geyser.session.cache.EntityEffectCache; +import org.geysermc.geyser.session.cache.FormCache; +import org.geysermc.geyser.session.cache.LodestoneCache; +import org.geysermc.geyser.session.cache.PistonCache; +import org.geysermc.geyser.session.cache.PreferencesCache; +import org.geysermc.geyser.session.cache.RegistryCache; +import org.geysermc.geyser.session.cache.SkullCache; +import org.geysermc.geyser.session.cache.StructureBlockCache; +import org.geysermc.geyser.session.cache.TagCache; +import org.geysermc.geyser.session.cache.TeleportCache; +import org.geysermc.geyser.session.cache.WorldBorder; +import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.MinecraftLocale; @@ -119,7 +168,11 @@ import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.Session; -import org.geysermc.mcprotocollib.network.event.session.*; +import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; +import org.geysermc.mcprotocollib.network.event.session.PacketErrorEvent; +import org.geysermc.mcprotocollib.network.event.session.PacketSendingEvent; +import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.network.tcp.TcpClientSession; import org.geysermc.mcprotocollib.network.tcp.TcpSession; @@ -154,7 +207,16 @@ import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; @@ -600,12 +662,8 @@ public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSessio this.spawned = false; this.loggedIn = false; - if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) { - this.emotes = new HashSet<>(); - geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes())); - } else { - this.emotes = null; - } + this.emotes = new HashSet<>(); + geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes())); this.remoteServer = geyser.defaultRemoteServer(); } @@ -618,7 +676,7 @@ public void connect() { sentSpawnPacket = true; syncEntityProperties(); - if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { + if (GeyserImpl.getInstance().config().addNonBedrockItems()) { ItemComponentPacket componentPacket = new ItemComponentPacket(); componentPacket.getItems().addAll(itemMappings.getComponentItemData()); upstream.sendPacket(componentPacket); @@ -870,11 +928,11 @@ private void connectDownstream() { // Disable automatic creation of a new TcpClientSession when transferring - we don't use that functionality. this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false); - if (geyser.getConfig().getRemote().isUseProxyProtocol()) { + if (geyser.config().java().useProxyProtocol()) { downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true); downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress()); } - if (geyser.getConfig().isForwardPlayerPing()) { + if (geyser.config().forwardPlayerPing()) { // Let Geyser handle sending the keep alive downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false); } @@ -943,7 +1001,7 @@ public void packetSending(PacketSendingEvent event) { ClientIntentionPacket intentionPacket = event.getPacket(); String address; - if (geyser.getConfig().getRemote().isForwardHost()) { + if (geyser.config().java().forwardHostname()) { address = clientData.getServerAddress().split(":")[0]; } else { address = intentionPacket.getHostname(); @@ -1035,7 +1093,7 @@ public void disconnected(DisconnectedEvent event) { } else { GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause); } - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { cause.printStackTrace(); } } @@ -1058,7 +1116,7 @@ public void packetReceived(Session session, Packet packet) { @Override public void packetError(PacketErrorEvent event) { geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage())); - if (geyser.getConfig().isDebugMode()) + if (geyser.config().debugMode()) event.getCause().printStackTrace(); event.setSuppress(true); } @@ -1090,7 +1148,7 @@ public void disconnect(String reason) { } else { // Downstream's disconnect will fire an event that prints a log message // Otherwise, we print a message here - String address = geyser.getConfig().isLogPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : ""; + String address = geyser.config().logPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : ""; geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, reason)); } @@ -1482,7 +1540,7 @@ private void startGame() { startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setDifficulty(1); startGamePacket.setDefaultSpawn(Vector3i.ZERO); - startGamePacket.setAchievementsDisabled(!geyser.getConfig().isXboxAchievementsEnabled()); + startGamePacket.setAchievementsDisabled(!geyser.config().xboxAchievementsEnabled()); startGamePacket.setCurrentTick(-1); startGamePacket.setEduEditionOffers(0); startGamePacket.setEduFeaturesEnabled(false); @@ -1492,7 +1550,7 @@ private void startGame() { startGamePacket.setBroadcastingToLan(true); startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); - startGamePacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled()); + startGamePacket.setCommandsEnabled(!geyser.config().xboxAchievementsEnabled()); startGamePacket.setTexturePacksRequired(false); startGamePacket.setBonusChestEnabled(false); startGamePacket.setStartingWithMap(false); @@ -1510,7 +1568,7 @@ private void startGame() { startGamePacket.setEducationProductionId(""); startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty()); - String serverName = geyser.getConfig().getBedrock().serverName(); + String serverName = geyser.config().bedrock().serverName(); startGamePacket.setLevelId(serverName); startGamePacket.setLevelName(serverName); @@ -1634,7 +1692,7 @@ public void sendDownstreamLoginPacket(Packet packet) { public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) { // protocol can be null when we're not yet logged in (online auth) if (protocol == null) { - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { geyser.getLogger().debug("Tried to send downstream packet with no downstream session!"); Thread.dumpStack(); } @@ -1660,7 +1718,7 @@ public void sendDownstreamPacket(Packet packet) { if (channel == null) { // Channel is only null before the connection has initialized geyser.getLogger().warning("Tried to send a packet to the Java server too early!"); - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { Thread.dumpStack(); } return; @@ -2051,7 +2109,7 @@ public void removeCommandEnum(String name, String enums) { private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) { // There is no need to send command enums if command suggestions are disabled - if (!this.geyser.getConfig().isCommandSuggestions()) { + if (!this.geyser.config().commandSuggestions()) { return; } UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket(); diff --git a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java index 07dd3849185..8aad61ede2b 100644 --- a/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java +++ b/core/src/main/java/org/geysermc/geyser/session/auth/BedrockClientData.java @@ -25,93 +25,97 @@ package org.geysermc.geyser.session.auth; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; import lombok.Getter; import lombok.Setter; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.UiProfile; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.UUID; -@JsonIgnoreProperties(ignoreUnknown = true) @Getter public final class BedrockClientData { - @JsonProperty(value = "GameVersion") + @SerializedName(value = "GameVersion") private String gameVersion; - @JsonProperty(value = "ServerAddress") + @SerializedName(value = "ServerAddress") private String serverAddress; - @JsonProperty(value = "ThirdPartyName") + @SerializedName(value = "ThirdPartyName") private String username; - @JsonProperty(value = "LanguageCode") + @SerializedName(value = "LanguageCode") private String languageCode; - @JsonProperty(value = "SkinId") + @SerializedName(value = "SkinId") private String skinId; - @JsonProperty(value = "SkinData") + @SerializedName(value = "SkinData") private String skinData; - @JsonProperty(value = "SkinImageHeight") + @SerializedName(value = "SkinImageHeight") private int skinImageHeight; - @JsonProperty(value = "SkinImageWidth") + @SerializedName(value = "SkinImageWidth") private int skinImageWidth; - @JsonProperty(value = "CapeId") + @SerializedName(value = "CapeId") private String capeId; - @JsonProperty(value = "CapeData") + @SerializedName(value = "CapeData") + @JsonAdapter(value = StringToByteDeserializer.class) private byte[] capeData; - @JsonProperty(value = "CapeImageHeight") + @SerializedName(value = "CapeImageHeight") private int capeImageHeight; - @JsonProperty(value = "CapeImageWidth") + @SerializedName(value = "CapeImageWidth") private int capeImageWidth; - @JsonProperty(value = "CapeOnClassicSkin") + @SerializedName(value = "CapeOnClassicSkin") private boolean capeOnClassicSkin; - @JsonProperty(value = "SkinResourcePatch") + @SerializedName(value = "SkinResourcePatch") private String geometryName; - @JsonProperty(value = "SkinGeometryData") + @SerializedName(value = "SkinGeometryData") private String geometryData; - @JsonProperty(value = "PersonaSkin") + @SerializedName(value = "PersonaSkin") private boolean personaSkin; - @JsonProperty(value = "PremiumSkin") + @SerializedName(value = "PremiumSkin") private boolean premiumSkin; - @JsonProperty(value = "DeviceId") + @SerializedName(value = "DeviceId") private String deviceId; - @JsonProperty(value = "DeviceModel") + @SerializedName(value = "DeviceModel") private String deviceModel; - @JsonProperty(value = "DeviceOS") + @SerializedName(value = "DeviceOS") private DeviceOs deviceOs; - @JsonProperty(value = "UIProfile") + @SerializedName(value = "UIProfile") private UiProfile uiProfile; - @JsonProperty(value = "GuiScale") + @SerializedName(value = "GuiScale") private int guiScale; - @JsonProperty(value = "CurrentInputMode") + @SerializedName(value = "CurrentInputMode") private InputMode currentInputMode; - @JsonProperty(value = "DefaultInputMode") + @SerializedName(value = "DefaultInputMode") private InputMode defaultInputMode; - @JsonProperty("PlatformOnlineId") + @SerializedName("PlatformOnlineId") private String platformOnlineId; - @JsonProperty(value = "PlatformOfflineId") + @SerializedName(value = "PlatformOfflineId") private String platformOfflineId; - @JsonProperty(value = "SelfSignedId") + @SerializedName(value = "SelfSignedId") private UUID selfSignedId; - @JsonProperty(value = "ClientRandomId") + @SerializedName(value = "ClientRandomId") private long clientRandomId; - @JsonProperty(value = "ArmSize") + @SerializedName(value = "ArmSize") private String armSize; - @JsonProperty(value = "SkinAnimationData") + @SerializedName(value = "SkinAnimationData") private String skinAnimationData; - @JsonProperty(value = "SkinColor") + @SerializedName(value = "SkinColor") private String skinColor; - @JsonProperty(value = "ThirdPartyNameOnly") + @SerializedName(value = "ThirdPartyNameOnly") private boolean thirdPartyNameOnly; - @JsonProperty(value = "PlayFabId") + @SerializedName(value = "PlayFabId") private String playFabId; - @JsonIgnore @Setter - private String originalString = null; + private transient String originalString = null; public DeviceOs getDeviceOs() { return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN; @@ -128,4 +132,11 @@ public InputMode getDefaultInputMode() { public UiProfile getUiProfile() { return uiProfile != null ? uiProfile : UiProfile.CLASSIC; } + + private static final class StringToByteDeserializer implements JsonDeserializer { + @Override + public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return json.getAsString().getBytes(StandardCharsets.UTF_8); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java index 2aeb83fa87c..78a6b75febc 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java @@ -54,15 +54,16 @@ public class PreferencesCache { private boolean prefersCustomSkulls; /** - * Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}. + * Which CooldownType the client prefers. Initially set to the config default. */ @Setter - private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown(); + private CooldownUtils.CooldownType cooldownPreference; public PreferencesCache(GeyserSession session) { this.session = session; - prefersCustomSkulls = session.getGeyser().getConfig().isAllowCustomSkulls(); + prefersCustomSkulls = session.getGeyser().config().allowCustomSkulls(); + cooldownPreference = session.getGeyser().config().showCooldown(); } /** @@ -74,7 +75,7 @@ public PreferencesCache(GeyserSession session) { * {@link GeyserConfiguration#isShowCoordinates()} is disabled */ public void updateShowCoordinates() { - allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().getConfig().isShowCoordinates(); + allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().config().showCoordinates(); session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); } @@ -82,6 +83,6 @@ public void updateShowCoordinates() { * @return true if the session prefers custom skulls, and the config allows them. */ public boolean showCustomSkulls() { - return prefersCustomSkulls && session.getGeyser().getConfig().isAllowCustomSkulls(); + return prefersCustomSkulls && session.getGeyser().config().allowCustomSkulls(); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java index a40a1156dcf..050976c3bf1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java @@ -74,11 +74,11 @@ public class SkullCache { public SkullCache(GeyserSession session) { this.session = session; - this.maxVisibleSkulls = session.getGeyser().getConfig().getMaxVisibleCustomSkulls(); + this.maxVisibleSkulls = session.getGeyser().config().maxVisibleCustomSkulls(); this.cullingEnabled = this.maxVisibleSkulls != -1; // Normal skulls are not rendered beyond 64 blocks - int distance = Math.min(session.getGeyser().getConfig().getCustomSkullRenderDistance(), 64); + int distance = Math.min(session.getGeyser().config().customSkullRenderDistance(), 64); this.skullRenderDistanceSquared = distance * distance; } @@ -98,7 +98,7 @@ public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, Blo } } catch (IOException e) { session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty); - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java index 4e4f529147c..415b6fa4bea 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java +++ b/core/src/main/java/org/geysermc/geyser/skin/FloodgateSkinUploader.java @@ -25,11 +25,9 @@ package org.geysermc.geyser.skin; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; import lombok.Getter; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.floodgate.util.WebsocketEventType; @@ -37,6 +35,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.PluginMessageUtils; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; @@ -52,7 +51,6 @@ import java.util.concurrent.TimeUnit; public final class FloodgateSkinUploader { - private final ObjectMapper JACKSON = new ObjectMapper(); private final List skinQueue = new ArrayList<>(); private final GeyserLogger logger; @@ -79,15 +77,14 @@ public void onOpen(ServerHandshake handshake) { @Override public void onMessage(String message) { - // The reason why I don't like Jackson try { - JsonNode node = JACKSON.readTree(message); + JsonObject node = JsonUtils.parseJson(message); if (node.has("error")) { - logger.error("Got an error: " + node.get("error").asText()); + logger.error("Got an error: " + node.get("error").getAsString()); return; } - int typeId = node.get("event_id").asInt(); + int typeId = node.get("event_id").getAsInt(); WebsocketEventType type = WebsocketEventType.fromId(typeId); if (type == null) { logger.warning(String.format( @@ -98,11 +95,11 @@ public void onMessage(String message) { switch (type) { case SUBSCRIBER_CREATED: - id = node.get("id").asInt(); - verifyCode = node.get("verify_code").asText(); + id = node.get("id").getAsInt(); + verifyCode = node.get("verify_code").getAsString(); break; case SUBSCRIBER_COUNT: - subscribersCount = node.get("subscribers_count").asInt(); + subscribersCount = node.get("subscribers_count").getAsInt(); break; case SKIN_UPLOADED: // if Geyser is the only subscriber we have send it to the server manually @@ -111,19 +108,19 @@ public void onMessage(String message) { break; } - String xuid = node.get("xuid").asText(); + String xuid = node.get("xuid").getAsString(); GeyserSession session = geyser.connectionByXuid(xuid); if (session != null) { - if (!node.get("success").asBoolean()) { + if (!node.get("success").getAsBoolean()) { logger.info("Failed to upload skin for " + session.bedrockUsername()); return; } - JsonNode data = node.get("data"); + JsonObject data = node.getAsJsonObject("data"); - String value = data.get("value").asText(); - String signature = data.get("signature").asText(); + String value = data.get("value").getAsString(); + String signature = data.get("signature").getAsString(); byte[] bytes = (value + '\0' + signature) .getBytes(StandardCharsets.UTF_8); @@ -131,8 +128,8 @@ public void onMessage(String message) { } break; case LOG_MESSAGE: - String logMessage = node.get("message").asText(); - switch (node.get("priority").asInt()) { + String logMessage = node.get("message").getAsString(); + switch (node.get("priority").getAsInt()) { case -1 -> logger.debug("Got a message from skin uploader: " + logMessage); case 0 -> logger.info("Got a message from skin uploader: " + logMessage); case 1 -> logger.error("Got a message from skin uploader: " + logMessage); @@ -150,20 +147,19 @@ public void onMessage(String message) { @Override public void onClose(int code, String reason, boolean remote) { if (reason != null && !reason.isEmpty()) { - // The reason why I don't like Jackson try { - JsonNode node = JACKSON.readTree(reason); + JsonObject node = JsonUtils.parseJson(reason); // info means that the uploader itself did nothing wrong if (node.has("info")) { - String info = node.get("info").asText(); + String info = node.get("info").getAsString(); logger.debug("Got disconnected from the skin uploader: " + info); } // error means that the uploader did something wrong if (node.has("error")) { - String error = node.get("error").asText(); + String error = node.get("error").getAsString(); logger.info("Got disconnected from the skin uploader: " + error); } - } catch (JsonProcessingException ignored) { + } catch (JsonSyntaxException ignored) { // ignore invalid json } catch (Exception e) { logger.error("Error while handling onClose", e); @@ -195,20 +191,13 @@ public void uploadSkin(List chainData, String clientData) { return; } - ObjectNode node = JACKSON.createObjectNode(); - ArrayNode chainDataNode = JACKSON.createArrayNode(); + JsonObject node = new JsonObject(); + JsonArray chainDataNode = new JsonArray(); chainData.forEach(chainDataNode::add); - node.set("chain_data", chainDataNode); - node.put("client_data", clientData); - - // The reason why I don't like Jackson - String jsonString; - try { - jsonString = JACKSON.writeValueAsString(node); - } catch (Exception e) { - logger.error("Failed to upload skin", e); - return; - } + node.add("chain_data", chainDataNode); + node.addProperty("client_data", clientData); + + String jsonString = node.toString(); if (client.isOpen()) { client.send(jsonString); @@ -241,4 +230,4 @@ public void close() { client.close(); } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 88a9c6d01eb..ea99597f1b1 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -25,7 +25,8 @@ package org.geysermc.geyser.skin; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; @@ -43,6 +44,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.JsonUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -194,7 +196,7 @@ public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSessio public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { GeyserImpl geyser = GeyserImpl.getInstance(); - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); } @@ -208,7 +210,7 @@ public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientDat if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) { SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes); SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); - } else if (geyser.getConfig().isDebugMode()) { + } else if (geyser.config().debugMode()) { geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); geyser.getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); } @@ -246,7 +248,7 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { return loadFromJson(skinDataValue); } catch (IOException e) { GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for tag " + tag); - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } return null; @@ -274,7 +276,7 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { } else { GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername() + " with Value: " + texturesProperty); } - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { exception.printStackTrace(); } } @@ -282,30 +284,25 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { } public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException { - // TODO use GameProfile method. - JsonNode skinObject; + JsonObject skinObject; try { - skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); + skinObject = JsonUtils.parseJson(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); } catch (IllegalArgumentException e) { GeyserImpl.getInstance().getLogger().debug("Invalid base64 encoded skin entry: " + encodedJson); return null; } - JsonNode textures = skinObject.get("textures"); - - if (textures == null) { + if (!(skinObject.get("textures") instanceof JsonObject textures)) { return null; } - JsonNode skinTexture = textures.get("SKIN"); - if (skinTexture == null) { + if (!(textures.get("SKIN") instanceof JsonObject skinTexture)) { return null; } String skinUrl; - JsonNode skinUrlNode = skinTexture.get("url"); - if (skinUrlNode != null && skinUrlNode.isTextual()) { - skinUrl = skinUrlNode.asText().replace("http://", "https://"); + if (skinTexture.get("url") instanceof JsonPrimitive skinUrlNode && skinUrlNode.isString()) { + skinUrl = skinUrlNode.getAsString().replace("http://", "https://"); } else { return null; } @@ -322,11 +319,9 @@ public record GameProfileData(String skinUrl, String capeUrl, boolean isAlex) { boolean isAlex = skinTexture.has("metadata"); String capeUrl = null; - JsonNode capeTexture = textures.get("CAPE"); - if (capeTexture != null) { - JsonNode capeUrlNode = capeTexture.get("url"); - if (capeUrlNode != null && capeUrlNode.isTextual()) { - capeUrl = capeUrlNode.asText().replace("http://", "https://"); + if (textures.get("CAPE") instanceof JsonObject capeTexture) { + if (capeTexture.get("url") instanceof JsonPrimitive capeUrlNode && capeUrlNode.isString()) { + capeUrl = capeUrlNode.getAsString().replace("http://", "https://"); } } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 82c4a75f7ab..6efa4ef6c21 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -126,11 +126,6 @@ public class SkinProvider { WEARING_CUSTOM_SKULL = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkull\"}}", wearingCustomSkull); String wearingCustomSkullSlim = new String(FileUtils.readAllBytes("bedrock/skin/geometry.humanoid.wearingCustomSkullSlim.json"), StandardCharsets.UTF_8); WEARING_CUSTOM_SKULL_SLIM = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkullSlim\"}}", wearingCustomSkullSlim); - - GeyserImpl geyser = GeyserImpl.getInstance(); - if (geyser.getConfig().isAllowThirdPartyEars() || geyser.getConfig().isAllowThirdPartyCapes()) { - geyser.getLogger().warning("Third-party ears/capes have been removed from Geyser, if you still wish to have this functionality please use the extension: https://github.com/GeyserMC/ThirdPartyCosmetics"); - } } public static ExecutorService getExecutorService() { @@ -149,7 +144,7 @@ public static void shutdown() { public static void registerCacheImageTask(GeyserImpl geyser) { // Schedule Daily Image Expiry if we are caching them - if (geyser.getConfig().getCacheImages() > 0) { + if (geyser.config().cacheImages() > 0) { geyser.getScheduledThread().scheduleAtFixedRate(() -> { File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile(); if (!cacheFolder.exists()) { @@ -157,7 +152,7 @@ public static void registerCacheImageTask(GeyserImpl geyser) { } int count = 0; - final long expireTime = ((long) GeyserImpl.getInstance().getConfig().getCacheImages()) * ((long)1000 * 60 * 60 * 24); + final long expireTime = ((long) GeyserImpl.getInstance().config().cacheImages()) * ((long)1000 * 60 * 60 * 24); for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) { if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) { //noinspection ResultOfMethodCallIgnored @@ -188,7 +183,7 @@ static SkinData determineFallbackSkinData(UUID uuid) { Cape cape = null; SkinGeometry geometry = SkinGeometry.WIDE; - if (GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { + if (GeyserImpl.getInstance().config().java().authType() != AuthType.ONLINE) { // Let's see if this player is a Bedrock player, and if so, let's pull their skin. GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); if (session != null) { @@ -427,7 +422,7 @@ public static BufferedImage requestImage(String imageUrl, boolean isCape) throws GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl); // Write to cache if we are allowed - if (GeyserImpl.getInstance().getConfig().getCacheImages() > 0) { + if (GeyserImpl.getInstance().config().cacheImages() > 0) { imageFile.getParentFile().mkdirs(); try { ImageIO.write(image, "png", imageFile); @@ -496,7 +491,7 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E return properties.get(0).getAsJsonObject().get("value").getAsString(); } catch (Exception e) { GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid); - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } return null; @@ -515,7 +510,6 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E try { // Offline skin, or no present UUID JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); - System.out.println(node); JsonElement id = node.get("id"); if (id == null) { GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username); @@ -523,7 +517,7 @@ private static byte[] requestImageData(String imageUrl, boolean isCape) throws E } return id.getAsString(); } catch (Exception e) { - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } return null; diff --git a/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java b/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java index 28fd6f9a448..ac0ea27899a 100644 --- a/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java @@ -80,8 +80,8 @@ public static void init(GeyserBootstrap bootstrap) { * Finalize the default locale, now that we know what the default locale should be. */ public static void finalizeDefaultLocale(GeyserImpl geyser) { - String newDefaultLocale = geyser.getConfig().getDefaultLocale(); - if (newDefaultLocale == null) { + String newDefaultLocale = geyser.config().defaultLocale(); + if ("system".equals(newDefaultLocale)) { // We want to use the system locale which is already loaded return; } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 4c426b410df..df5925a27d2 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -207,7 +207,7 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven case PLACE: { TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action; if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { - if (session.getGeyser().getConfig().isDebugMode()) { + if (session.getGeyser().config().debugMode()) { session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); } @@ -312,7 +312,7 @@ public ItemStackResponse translateRequest(GeyserSession session, Inventory inven ItemStackRequestSlotData destination = swapAction.getDestination(); if (!(checkNetId(session, inventory, source) && checkNetId(session, inventory, destination))) { - if (session.getGeyser().getConfig().isDebugMode()) { + if (session.getGeyser().config().debugMode()) { session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.bedrockUsername()); dumpStackRequestDetails(session, inventory, source, destination); } @@ -807,7 +807,7 @@ protected static ItemStackResponse rejectRequest(ItemStackRequest request) { * as bad (false). */ protected static ItemStackResponse rejectRequest(ItemStackRequest request, boolean throwError) { - if (throwError && GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (throwError && GeyserImpl.getInstance().config().debugMode()) { new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace(); } return new ItemStackResponse(ItemStackResponseStatus.ERROR, request.getRequestId(), Collections.emptyList()); @@ -952,4 +952,4 @@ protected enum CraftState { TRANSFER, DONE } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java index c2d457202fe..db3f85946c7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java @@ -120,7 +120,7 @@ private static UUID getUUID(NbtMap profile) { return skull.getBlockDefinition(); } catch (InterruptedException | ExecutionException e) { session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + javaNbt); - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { e.printStackTrace(); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEmoteListTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEmoteListTranslator.java index b20f7a2dd5b..a7d1c3c14bc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEmoteListTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockEmoteListTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.geyser.translator.protocol.bedrock; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; -import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -36,10 +35,6 @@ public class BedrockEmoteListTranslator extends PacketTranslator yaw <= -135f || yaw > 135f; @@ -696,4 +696,4 @@ private void lookAt(GeyserSession session, Vector3f target) { }, 150, TimeUnit.MILLISECONDS)); } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockNetworkStackLatencyTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockNetworkStackLatencyTranslator.java index 6ca0f35000b..bf60d4bb5cc 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockNetworkStackLatencyTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockNetworkStackLatencyTranslator.java @@ -47,7 +47,7 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator= 0) { - if (session.getGeyser().getConfig().isForwardPlayerPing()) { + if (session.getGeyser().config().forwardPlayerPing()) { // use our cached value because // a) bedrock can be inaccurate with the value returned // b) playstation replies with a different magnitude than other platforms diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java index de2df0cb7a0..a738f2a3ae6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockSetLocalPlayerAsInitializedTranslator.java @@ -45,7 +45,7 @@ public void translate(GeyserSession session, SetLocalPlayerAsInitializedPacket p if (session.remoteServer().authType() == AuthType.ONLINE) { if (!session.isLoggedIn()) { - if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) { + if (session.getGeyser().config().savedUserLogins().contains(session.bedrockUsername())) { if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) { LoginEncryptionUtils.buildAndShowConsentWindow(session); } else { diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java index 7a37aa72e4a..13683b2dc39 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java @@ -27,7 +27,6 @@ import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; -import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; @@ -39,15 +38,6 @@ public class BedrockEmoteTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, EmotePacket packet) { - if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { - // Activate the workaround - we should trigger the offhand now - session.requestOffhandSwap(); - - if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { - return; - } - } - // For the future: could have a method that exposes which players will see the emote ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId()); session.getGeyser().eventBus().fire(event); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java index ecfb2d22028..433e586da7b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaCommandsTranslator.java @@ -113,7 +113,7 @@ public boolean equals(BedrockCommandInfo a, BedrockCommandInfo b) { @Override public void translate(GeyserSession session, ClientboundCommandsPacket packet) { // Don't send command suggestions if they are disabled - if (!session.getGeyser().getConfig().isCommandSuggestions()) { + if (!session.getGeyser().config().commandSuggestions()) { session.getGeyser().getLogger().debug("Not sending translated command suggestions as they are disabled."); // Send an empty packet so Bedrock doesn't override /help with its own, built-in help command. diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaKeepAliveTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaKeepAliveTranslator.java index 399c2308085..e711c72c63f 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaKeepAliveTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaKeepAliveTranslator.java @@ -39,7 +39,7 @@ public class JavaKeepAliveTranslator extends PacketTranslator { StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData(); if (stoneCuttingData.getIngredient().getOptions().length == 0) { - if (GeyserImpl.getInstance().getConfig().isDebugMode()) { + if (GeyserImpl.getInstance().config().debugMode()) { GeyserImpl.getInstance().getLogger().debug("Received broken stone cutter recipe: " + stoneCuttingData + " " + recipe.getIdentifier() + " " + Registries.JAVA_ITEMS.get().get(stoneCuttingData.getResult().getId()).javaIdentifier()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java index 2dc780c668b..44ab214aab4 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaSetEntityDataTranslator.java @@ -45,7 +45,7 @@ public void translate(GeyserSession session, ClientboundSetEntityDataPacket pack EntityDefinition definition = entity.getDefinition(); for (EntityMetadata metadata : packet.getMetadata()) { if (metadata.getId() >= definition.translators().size()) { - if (session.getGeyser().getConfig().isDebugMode()) { + if (session.getGeyser().config().debugMode()) { // Minecraft client just ignores these session.getGeyser().getLogger().warning("Metadata ID " + metadata.getId() + " is out of bounds of known entity metadata size " + definition.translators().size() + " for entity type " + entity.getDefinition().entityType()); session.getGeyser().getLogger().debug(metadata.toString()); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java index 413833acf25..3bd069348c6 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/player/JavaPlayerPositionTranslator.java @@ -91,7 +91,7 @@ public void translate(GeyserSession session, ClientboundPlayerPositionPacket pac ChunkUtils.updateChunkPosition(session, pos.toInt()); - if (session.getGeyser().getConfig().isDebugMode()) { + if (session.getGeyser().config().debugMode()) { session.getGeyser().getLogger().debug("Spawned player at " + packet.getX() + " " + packet.getY() + " " + packet.getZ()); } return; diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 152bf41600c..f81e986310a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -357,7 +357,7 @@ public static void handleChatPacket(GeyserSession session, Component message, Ho textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale())); } else { session.getGeyser().getLogger().debug("Likely illegal chat type detection found."); - if (session.getGeyser().getConfig().isDebugMode()) { + if (session.getGeyser().config().debugMode()) { Thread.dumpStack(); } textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale())); diff --git a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java index c020e96b2c6..50faf8ae238 100644 --- a/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/CooldownUtils.java @@ -38,22 +38,12 @@ * Much of the work here is from the wonderful folks from ViaRewind */ public class CooldownUtils { - private static CooldownType DEFAULT_SHOW_COOLDOWN; - - public static void setDefaultShowCooldown(String showCooldown) { - DEFAULT_SHOW_COOLDOWN = CooldownType.getByName(showCooldown); - } - - public static CooldownType getDefaultShowCooldown() { - return DEFAULT_SHOW_COOLDOWN; - } - /** * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is the cooldownPreference in {@link PreferencesCache} * @param session GeyserSession */ public static void sendCooldown(GeyserSession session) { - if (DEFAULT_SHOW_COOLDOWN == CooldownType.DISABLED) return; + if (session.getGeyser().config().showCooldown() == CooldownType.DISABLED) return; CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference(); if (sessionPreference == CooldownType.DISABLED) return; @@ -161,10 +151,6 @@ public enum CooldownType { * @return The converted CooldownType */ public static CooldownType getByName(String name) { - if (name.equalsIgnoreCase("true")) { // Backwards config compatibility - return CooldownType.TITLE; - } - for (CooldownType type : VALUES) { if (type.name().equalsIgnoreCase(name)) { return type; diff --git a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java index 48ade52e27c..eeb0a116daa 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -179,7 +179,7 @@ public static void updateCursor(GeyserSession session) { } public static boolean canStack(GeyserItemStack item1, GeyserItemStack item2) { - if (GeyserImpl.getInstance().getConfig().isDebugMode()) + if (GeyserImpl.getInstance().config().debugMode()) canStackDebug(item1, item2); if (item1.isEmpty() || item2.isEmpty()) return false; @@ -231,7 +231,7 @@ public static IntFunction createUnusableSpaceBlock(String description) private static ItemDefinition getUnusableSpaceBlockDefinition(int protocolVersion) { ItemMappings mappings = Registries.ITEMS.forVersion(protocolVersion); - String unusableSpaceBlock = GeyserImpl.getInstance().getConfig().getUnusableSpaceBlock(); + String unusableSpaceBlock = GeyserImpl.getInstance().config().unusableSpaceBlock(); ItemDefinition itemDefinition = mappings.getDefinition(unusableSpaceBlock); if (itemDefinition == null) { diff --git a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java index a1f11e8607c..d1e4a9d99cc 100644 --- a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java @@ -48,6 +48,10 @@ public static JsonObject fromJson(InputStream stream) { return (JsonObject) new JsonParser().parse(new InputStreamReader(stream)); } + public static JsonObject parseJson(String s) { + return (JsonObject) new JsonParser().parse(s); + } + public static T fromJson(InputStream stream, Type type) { return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type); } diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index 478a6ef962c..c9d4e87bf88 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -25,9 +25,6 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.github.steveice10.mc.auth.service.MsaAuthenticationService; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket; @@ -53,8 +50,6 @@ import java.util.function.BiConsumer; public class LoginEncryptionUtils { - private static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - private static boolean HAS_SENT_ENCRYPTION_MESSAGE = false; public static void encryptPlayerConnection(GeyserSession session, LoginPacket loginPacket) { @@ -69,7 +64,7 @@ private static void encryptConnectionWithCert(GeyserSession session, String clie geyser.getLogger().debug(String.format("Is player data signed? %s", result.signed())); - if (!result.signed() && !session.getGeyser().getConfig().isEnableProxyConnections()) { + if (!result.signed() && !session.getGeyser().config().enableProxyConnections()) { session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.remote.invalid_xbox_account")); return; } @@ -85,8 +80,7 @@ private static void encryptConnectionWithCert(GeyserSession session, String clie throw new IllegalStateException("Client data isn't signed by the given chain data"); } - JsonNode clientDataJson = JSON_MAPPER.readTree(clientDataPayload); - BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class); + BedrockClientData data = JsonUtils.fromJson(clientDataPayload, BedrockClientData.class); data.setOriginalString(clientData); session.setClientData(data); @@ -94,7 +88,7 @@ private static void encryptConnectionWithCert(GeyserSession session, String clie startEncryptionHandshake(session, identityPublicKey); } catch (Throwable e) { // An error can be thrown on older Java 8 versions about an invalid key - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { e.printStackTrace(); } @@ -213,7 +207,7 @@ public static void buildAndShowMicrosoftCodeWindow(GeyserSession session, MsaAut .append("\n%xbox.signin.enterCode\n") .append(ChatColor.GREEN) .append(msCode.user_code); - int timeout = session.getGeyser().getConfig().getPendingAuthenticationTimeout(); + int timeout = session.getGeyser().config().pendingAuthenticationTimeout(); if (timeout != 0) { message.append("\n\n") .append(ChatColor.RESET) diff --git a/core/src/main/java/org/geysermc/geyser/util/Metrics.java b/core/src/main/java/org/geysermc/geyser/util/Metrics.java deleted file mode 100644 index a4ef301e393..00000000000 --- a/core/src/main/java/org/geysermc/geyser/util/Metrics.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2019-2022 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 com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.GeyserImpl; - -import javax.net.ssl.HttpsURLConnection; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.GZIPOutputStream; - -/** - * bStats collects some data for plugin authors. - *

- * Check out bStats to learn more about bStats! - */ -public class Metrics { - - // The version of this bStats class - public static final int B_STATS_VERSION = 1; - - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/server-implementation"; - - // Should failed requests be logged? - private static boolean logFailedRequests = false; - - // The logger for the failed requests - private static Logger logger = Logger.getLogger("bStats"); - - // The name of the server software - private final String name; - - // The uuid of the server - private final String serverUUID; - - // A list with all custom charts - private final List charts = new ArrayList<>(); - - private final static ObjectMapper mapper = new ObjectMapper(); - - private final GeyserImpl geyser; - - /** - * Class constructor. - * - * @param geyser The Geyser instance - * @param name The name of the server software. - * @param serverUUID The uuid of the server. - * @param logFailedRequests Whether failed requests should be logged or not. - * @param logger The logger for the failed requests. - */ - public Metrics(GeyserImpl geyser, String name, String serverUUID, boolean logFailedRequests, Logger logger) { - this.geyser = geyser; - this.name = name; - this.serverUUID = serverUUID; - Metrics.logFailedRequests = logFailedRequests; - Metrics.logger = logger; - - // Start submitting the data - startSubmitting(); - } - - /** - * Adds a custom chart. - * - * @param chart The chart to add. - */ - public void addCustomChart(CustomChart chart) { - if (chart == null) { - throw new IllegalArgumentException("Chart cannot be null!"); - } - charts.add(chart); - } - - /** - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { - geyser.getScheduledThread().scheduleAtFixedRate(this::submitData, 1, 30, TimeUnit.MINUTES); - // Submit the data every 30 minutes, first time after 1 minutes to give other plugins enough time to start - // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! - // WARNING: Just don't do it! - } - - /** - * Gets the plugin specific data. - * - * @return The plugin specific data. - */ - private ObjectNode getPluginData() { - ObjectNode data = mapper.createObjectNode(); - - data.put("pluginName", name); // Append the name of the server software - data.put("pluginVersion", GeyserImpl.VERSION); // Append the name of the server software - - ArrayNode customCharts = mapper.createArrayNode(); - for (CustomChart customChart : charts) { - // Add the data of the custom charts - JsonNode chart = customChart.getRequestJsonNode(); - if (chart == null) { // If the chart is null, we skip it - continue; - } - customCharts.add(chart); - } - data.set("customCharts", customCharts); - - return data; - } - - /** - * Gets the server specific data. - * - * @return The server specific data. - */ - private ObjectNode getServerData() { - // OS specific data - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - String osVersion = System.getProperty("os.version"); - int coreCount = Runtime.getRuntime().availableProcessors(); - - ObjectNode data = mapper.createObjectNode(); - - data.put("serverUUID", serverUUID); - - data.put("osName", osName); - data.put("osArch", osArch); - data.put("osVersion", osVersion); - data.put("coreCount", coreCount); - - return data; - } - - /** - * Collects the data and sends it afterwards. - */ - private void submitData() { - final ObjectNode data = getServerData(); - - ArrayNode pluginData = mapper.createArrayNode(); - pluginData.add(getPluginData()); - data.putPOJO("plugins", pluginData); - - new Thread(() -> { - try { - // We are still in the Thread of the timer, so nothing get blocked :) - sendData(data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - logger.log(Level.WARNING, "Could not submit stats of " + name, e); - } - } - }).start(); - } - - /** - * Sends the data to the bStats server. - * - * @param data The data to send. - * @throws Exception If the request failed. - */ - private static void sendData(ObjectNode data) throws Exception { - if (data == null) { - throw new IllegalArgumentException("Data cannot be null!"); - } - HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - - // Add headers - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format - connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); - - // Send data - connection.setDoOutput(true); - DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); - outputStream.write(compressedData); - outputStream.flush(); - outputStream.close(); - - connection.getInputStream().close(); // We don't care about the response - Just send our data :) - } - - /** - * Gzips the given String. - * - * @param str The string to gzip. - * @return The gzipped String. - * @throws IOException If the compression failed. - */ - private static byte @NonNull [] compress(final @NonNull String str) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - GZIPOutputStream gzip = new GZIPOutputStream(outputStream); - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - gzip.close(); - return outputStream.toByteArray(); - } - - /** - * Represents a custom chart. - */ - public static abstract class CustomChart { - - // The id of the chart - final String chartId; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - */ - CustomChart(String chartId) { - if (chartId == null || chartId.isEmpty()) { - throw new IllegalArgumentException("ChartId cannot be null or empty!"); - } - this.chartId = chartId; - } - - private @Nullable ObjectNode getRequestJsonNode() { - ObjectNode chart = new ObjectMapper().createObjectNode(); - chart.put("chartId", chartId); - try { - ObjectNode data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - chart.putPOJO("data", data); - } catch (Throwable t) { - if (logFailedRequests) { - logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return chart; - } - - - - protected abstract ObjectNode getChartData() throws Exception; - - } - - /** - * Represents a custom simple pie. - */ - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected @Nullable ObjectNode getChartData() throws Exception { - ObjectNode data = mapper.createObjectNode(); - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - data.put("value", value); - return data; - } - } - - /** - * Represents a custom advanced pie. - */ - public static class AdvancedPie extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected @Nullable ObjectNode getChartData() throws Exception { - ObjectNode data = mapper.createObjectNode(); - ObjectNode values = mapper.createObjectNode(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.put(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.putPOJO("values", values); - return data; - } - } - - /** - * Represents a custom drilldown pie. - */ - public static class DrilldownPie extends CustomChart { - - private final Callable>> callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } - - @Override - public @Nullable ObjectNode getChartData() throws Exception { - ObjectNode data = mapper.createObjectNode(); - ObjectNode values = mapper.createObjectNode(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - ObjectNode value = mapper.createObjectNode(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - value.put(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - values.putPOJO(entryValues.getKey(), value); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - data.putPOJO("values", values); - return data; - } - } - - /** - * Represents a custom single line chart. - */ - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId The id of the chart. - * @param callable The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected @Nullable ObjectNode getChartData() throws Exception { - ObjectNode data = mapper.createObjectNode(); - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - data.put("value", value); - return data; - } - - } - -} \ No newline at end of file diff --git a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java index ed97408b996..c87eb0db047 100644 --- a/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/SettingsUtils.java @@ -51,8 +51,8 @@ public static CustomForm buildForm(GeyserSession session) { // Let's store these to avoid issues boolean showCoordinates = session.getPreferencesCache().isAllowShowCoordinates(); - boolean cooldownShown = CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED; - boolean customSkulls = session.getGeyser().getConfig().isAllowCustomSkulls(); + boolean cooldownShown = session.getGeyser().config().showCooldown() != CooldownUtils.CooldownType.DISABLED; + boolean customSkulls = session.getGeyser().config().allowCustomSkulls(); // Only show the client title if any of the client settings are available boolean showClientSettings = showCoordinates || cooldownShown || customSkulls; diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 0e4c7154a7f..1c9df9c08d2 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -199,7 +199,7 @@ public static String postForm(String reqURL, Map fields) throws return ((String) attr.get(0)).split(" "); } } catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes - if (geyser.getConfig().isDebugMode()) { + if (geyser.config().debugMode()) { geyser.getLogger().debug("Exception while trying to find an SRV record for the remote host."); ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f5ceb160590..d8695642f51 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] base-api = "1.0.0-SNAPSHOT" +bstats = "3.0.2" cumulus = "1.1.2" configurate = "4.2.0-GeyserMC-SNAPSHOT" erosion = "1.1-20240515.191456-1" @@ -130,6 +131,8 @@ protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-con math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" } +bstats = { group = "org.bstats", name = "bstats-base", version.ref = "bstats"} + # plugins indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" } shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" } From c095c276ef9f08b99a73a74391001ecf4df82d86 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 6 Jul 2024 22:49:28 -0400 Subject: [PATCH 13/42] The configuration is gone. Long live the config. --- bootstrap/bungeecord/build.gradle.kts | 1 + .../bungeecord/GeyserBungeeConfiguration.java | 50 --- .../bungeecord/GeyserBungeeInjector.java | 8 +- .../bungeecord/GeyserBungeePlugin.java | 50 +-- bootstrap/mod/fabric/build.gradle.kts | 3 +- .../platform/fabric/GeyserFabricPlatform.java | 2 +- .../neoforge/GeyserNeoForgePlatform.java | 2 +- .../platform/mod/GeyserModBootstrap.java | 47 +-- .../platform/mod/GeyserModConfiguration.java | 48 --- .../platform/mod/GeyserModInjector.java | 5 +- bootstrap/spigot/build.gradle.kts | 1 + .../spigot/GeyserSpigotConfiguration.java | 51 --- .../platform/spigot/GeyserSpigotInjector.java | 5 +- .../platform/spigot/GeyserSpigotPlugin.java | 44 +-- .../GeyserStandaloneConfiguration.java | 42 -- bootstrap/velocity/build.gradle.kts | 3 +- .../velocity/GeyserVelocityConfiguration.java | 52 --- .../velocity/GeyserVelocityPlugin.java | 45 +-- bootstrap/viaproxy/build.gradle.kts | 1 + .../viaproxy/GeyserViaProxyConfiguration.java | 67 ---- .../viaproxy/GeyserViaProxyPlugin.java | 44 ++- .../geysermc/geyser/FloodgateKeyLoader.java | 12 +- .../org/geysermc/geyser/GeyserBootstrap.java | 10 - .../configuration/GeyserConfiguration.java | 191 --------- .../GeyserJacksonConfiguration.java | 366 ------------------ .../configuration/GeyserPluginConfig.java | 33 +- .../org/geysermc/geyser/dump/DumpInfo.java | 15 - .../geyser/session/GeyserSession.java | 4 +- .../session/cache/PreferencesCache.java | 4 +- 29 files changed, 159 insertions(+), 1047 deletions(-) delete mode 100644 bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeConfiguration.java delete mode 100644 bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModConfiguration.java delete mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotConfiguration.java delete mode 100644 bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneConfiguration.java delete mode 100644 bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityConfiguration.java delete mode 100644 bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyConfiguration.java delete mode 100644 core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java delete mode 100644 core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts index 910e507239b..c9ae080d914 100644 --- a/bootstrap/bungeecord/build.gradle.kts +++ b/bootstrap/bungeecord/build.gradle.kts @@ -9,6 +9,7 @@ platformRelocate("com.fasterxml.jackson") platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound platformRelocate("net.kyori") platformRelocate("org.yaml") // Broken as of 1.20 +platformRelocate("org.bstats") // These dependencies are already present on the platform provided(libs.bungeecord.proxy) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeConfiguration.java deleted file mode 100644 index bc084a34e90..00000000000 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.bungeecord; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Getter; -import net.md_5.bungee.api.plugin.Plugin; -import org.geysermc.geyser.FloodgateKeyLoader; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.nio.file.Path; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -public final class GeyserBungeeConfiguration extends GeyserJacksonConfiguration { - @JsonIgnore - private Path floodgateKeyPath; - - public void loadFloodgate(GeyserBungeePlugin plugin) { - Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate"); - Path geyserDataFolder = plugin.getDataFolder().toPath(); - Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; - - floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger()); - } -} diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index fbd3bbd6e88..53df33acc57 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -40,8 +40,8 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.network.netty.LocalSession; @@ -67,7 +67,7 @@ public GeyserBungeeInjector(Plugin plugin) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { // TODO - allow Geyser to specify its own listener info properties if (proxy.getConfig().getListeners().size() != 1) { throw new UnsupportedOperationException("Geyser does not currently support multiple listeners with injection! " + @@ -142,7 +142,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { } initChannel.invoke(channelInitializer, ch); - if (bootstrap.config().asPluginConfig().orElseThrow().useDirectConnection()) { + if (bootstrap.config().useDirectConnection()) { ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler", new GeyserBungeeCompressionDisabler()); } @@ -191,7 +191,7 @@ public void onProxyReload(ProxyReloadEvent event) { this.bungeeChannels = null; if (this.localChannel != null) { shutdown(); - initializeLocalChannel(GeyserImpl.getInstance().getBootstrap()); + initializeLocalChannel((GeyserPluginBootstrap) GeyserImpl.getInstance().getBootstrap()); } } } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index cd6b59f6449..2a356140b10 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -32,19 +32,20 @@ import net.md_5.bungee.protocol.ProtocolConstants; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.FloodgateKeyLoader; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; @@ -56,13 +57,12 @@ import java.util.Collection; import java.util.Map; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.TimeUnit; -public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { +public class GeyserBungeePlugin extends Plugin implements GeyserPluginBootstrap { private GeyserCommandManager geyserCommandManager; - private GeyserBungeeConfiguration geyserConfig; + private GeyserPluginConfig geyserConfig; private GeyserBungeeInjector geyserInjector; private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger()); private IGeyserPingPassthrough geyserBungeePingPassthrough; @@ -95,8 +95,7 @@ public void onGeyserInitialize() { if (!this.loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); this.geyserInjector = new GeyserBungeeInjector(this); } @@ -140,8 +139,7 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); } else { // For consistency with other platforms - create command manager before GeyserImpl#start() // This ensures the command events are called before the item/block ones are @@ -151,7 +149,7 @@ public void onGeyserEnable() { // Force-disable query if enabled, or else Geyser won't enable for (ListenerInfo info : getProxy().getConfig().getListeners()) { - if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) { + if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.bedrock().port()) { try { Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled"); queryField.setAccessible(true); @@ -169,7 +167,7 @@ public void onGeyserEnable() { GeyserImpl.start(); - if (geyserConfig.isLegacyPingPassthrough()) { + if (!geyserConfig.integratedPingPassthrough()) { this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy()); @@ -216,8 +214,8 @@ public void onDisable() { } @Override - public GeyserBungeeConfiguration getGeyserConfig() { - return geyserConfig; + public GeyserPluginConfig config() { + return this.geyserConfig; } @Override @@ -269,11 +267,16 @@ public int getServerPort() { @Override public boolean testFloodgatePluginPresent() { - if (getProxy().getPluginManager().getPlugin("floodgate") != null) { - geyserConfig.loadFloodgate(this); - return true; - } - return false; + return getProxy().getPluginManager().getPlugin("floodgate") != null; + } + + @Override + public Path getFloodgateKeyPath() { + Plugin floodgate = getProxy().getPluginManager().getPlugin("floodgate"); + Path geyserDataFolder = getDataFolder().toPath(); + Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; + + return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger); } private Optional findCompatibleListener() { @@ -286,14 +289,13 @@ private Optional findCompatibleListener() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { try { - if (!getDataFolder().exists()) //noinspection ResultOfMethodCallIgnored + if (!getDataFolder().exists()) { + //noinspection ResultOfMethodCallIgnored getDataFolder().mkdir(); - File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), - "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class); + } + this.geyserConfig = ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); return false; } return true; diff --git a/bootstrap/mod/fabric/build.gradle.kts b/bootstrap/mod/fabric/build.gradle.kts index 25bd0af9d6c..c9e531bb507 100644 --- a/bootstrap/mod/fabric/build.gradle.kts +++ b/bootstrap/mod/fabric/build.gradle.kts @@ -50,6 +50,7 @@ application { relocate("org.cloudburstmc.netty") relocate("org.cloudburstmc.protocol") relocate("com.github.steveice10.mc.auth") +platformRelocate("org.bstats") tasks { remapJar { @@ -67,4 +68,4 @@ modrinth { dependencies { required.project("fabric-api") } -} \ No newline at end of file +} 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 4631ab4931a..345da4459e9 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 @@ -73,7 +73,7 @@ public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) Optional floodgate = FabricLoader.getInstance().getModContainer("floodgate"); if (floodgate.isPresent()) { Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate"); - bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder); + bootstrap.loadFloodgate(floodgateDataFolder); return true; } diff --git a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgePlatform.java b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgePlatform.java index 41562baf326..c314009fb54 100644 --- a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgePlatform.java +++ b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgePlatform.java @@ -73,7 +73,7 @@ public GeyserNeoForgePlatform(ModContainer container) { public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) { if (ModList.get().isLoaded("floodgate")) { Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate"); - bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder); + bootstrap.loadFloodgate(floodgateDataFolder); return true; } return false; diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java index d7373f0a9b0..c8597bc548c 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java @@ -36,14 +36,16 @@ import net.minecraft.world.entity.player.Player; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.FloodgateKeyLoader; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -52,18 +54,15 @@ import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.SocketAddress; import java.nio.file.Path; import java.util.Map; -import java.util.UUID; @RequiredArgsConstructor -public abstract class GeyserModBootstrap implements GeyserBootstrap { +public abstract class GeyserModBootstrap implements GeyserPluginBootstrap { @Getter private static GeyserModBootstrap instance; @@ -77,7 +76,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap { private MinecraftServer server; private GeyserCommandManager geyserCommandManager; - private GeyserModConfiguration geyserConfig; + private GeyserPluginConfig geyserConfig; private GeyserModInjector geyserInjector; private final GeyserModLogger geyserLogger = new GeyserModLogger(); private IGeyserPingPassthrough geyserPingPassthrough; @@ -91,8 +90,7 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); this.geyser = GeyserImpl.load(this.platform.platformType(), this); // Create command manager here, since the permission handler on neo needs it @@ -105,13 +103,12 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); } GeyserImpl.start(); - if (geyserConfig.isLegacyPingPassthrough()) { + if (!geyserConfig.integratedPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger); @@ -196,7 +193,7 @@ public void onGeyserShutdown() { } @Override - public GeyserModConfiguration getGeyserConfig() { + public GeyserPluginConfig config() { return geyserConfig; } @@ -249,12 +246,8 @@ public SocketAddress getSocketAddress() { @Override public int getServerPort() { - if (isServer()) { - return ((GeyserServerPortGetter) server).geyser$getServerPort(); - } else { - // Set in the IntegratedServerMixin - return geyserConfig.getRemote().port(); - } + // TODO test + return ((GeyserServerPortGetter) server).geyser$getServerPort(); } public abstract boolean isServer(); @@ -264,6 +257,17 @@ public boolean testFloodgatePluginPresent() { return this.platform.testFloodgatePluginPresent(this); } + private Path floodgateKeyPath; + + public void loadFloodgate(Path floodgateDataFolder) { + floodgateKeyPath = FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, dataFolder, geyserLogger); + } + + @Override + public Path getFloodgateKeyPath() { + return floodgateKeyPath; + } + @Nullable @Override public InputStream getResourceOrNull(String resource) { @@ -282,13 +286,10 @@ private boolean loadConfig() { dataFolder.toFile().mkdir(); } - File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class); + this.geyserConfig = ConfigLoaderTemp.load(dataFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); return true; } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); return false; } } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModConfiguration.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModConfiguration.java deleted file mode 100644 index a24380bd6a5..00000000000 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModConfiguration.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.mod; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.geysermc.geyser.FloodgateKeyLoader; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.nio.file.Path; - -public class GeyserModConfiguration extends GeyserJacksonConfiguration { - @JsonIgnore - private Path floodgateKeyPath; - - public void loadFloodgate(GeyserModBootstrap geyser, Path floodgateDataFolder) { - Path geyserDataFolder = geyser.getConfigFolder(); - - floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger()); - } - - @Override - public Path getFloodgateKeyPath() { - return floodgateKeyPath; - } -} diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java index 89a92d789f1..5346124fb2f 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java @@ -38,6 +38,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; @@ -63,7 +64,7 @@ public GeyserModInjector(MinecraftServer server, GeyserModPlatform platform) { } @Override - protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { ServerConnectionListener connection = this.server.getConnection(); // Find the channel that Minecraft uses to listen to connections @@ -96,7 +97,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression()) { + if (bootstrap.config().disableCompression()) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler()); } } diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index e4161164e78..810bbc5b25b 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -42,6 +42,7 @@ platformRelocate("org.yaml") // Broken as of 1.20 platformRelocate("org.spongepowered") platformRelocate("io.leangen.geantyref") platformRelocate("marcono1234.gson") +platformRelocate("org.bstats") // These dependencies are already present on the platform provided(libs.viaversion) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotConfiguration.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotConfiguration.java deleted file mode 100644 index 3320ffa6595..00000000000 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotConfiguration.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.spigot; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Getter; -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; -import org.geysermc.geyser.FloodgateKeyLoader; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.nio.file.Path; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration { - @JsonIgnore - private Path floodgateKeyPath; - - public void loadFloodgate(GeyserSpigotPlugin plugin) { - Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate"); - Path geyserDataFolder = plugin.getDataFolder().toPath(); - Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; - - floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger()); - } -} diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index d1d4b50ea0b..bb6295b6a84 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.spigot; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer; import io.netty.bootstrap.ServerBootstrap; @@ -60,7 +61,7 @@ public GeyserSpigotInjector(boolean isViaVersion) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { Class serverClazz; try { serverClazz = Class.forName("net.minecraft.server.MinecraftServer"); @@ -122,7 +123,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { + if (bootstrap.config().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler()); } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 60be30725ed..1eccca30eee 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -44,8 +44,9 @@ import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.Constants; -import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.FloodgateKeyLoader; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.adapters.paper.PaperAdapters; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.api.command.Command; @@ -53,7 +54,6 @@ import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.ConfigLoaderTemp; -import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; @@ -69,7 +69,6 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; import java.io.File; import java.io.IOException; @@ -80,12 +79,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.UUID; -public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { +public class GeyserSpigotPlugin extends JavaPlugin implements GeyserPluginBootstrap { private GeyserSpigotCommandManager geyserCommandManager; - private GeyserSpigotConfiguration geyserConfig; + private GeyserPluginConfig geyserConfig; private GeyserSpigotInjector geyserInjector; private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger()); @@ -156,8 +154,7 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); // Turn "(MC: 1.16.4)" into 1.16.4. this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0]; @@ -209,13 +206,12 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(this.geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(this.geyserConfig.debugMode()); } GeyserImpl.start(); - if (geyserConfig.isLegacyPingPassthrough()) { + if (!geyserConfig.integratedPingPassthrough()) { this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { if (ReflectedNames.checkPaperPingEvent()) { @@ -269,7 +265,7 @@ public void onGeyserEnable() { } geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName()); } catch (Throwable e) { - if (geyserConfig.isDebugMode()) { + if (geyserConfig.debugMode()) { geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)"); e.printStackTrace(); } @@ -382,8 +378,8 @@ public void onDisable() { } @Override - public GeyserSpigotConfiguration getGeyserConfig() { - return geyserConfig; + public GeyserPluginConfig config() { + return this.geyserConfig; } @Override @@ -469,11 +465,16 @@ public int getServerPort() { @Override public boolean testFloodgatePluginPresent() { - if (Bukkit.getPluginManager().getPlugin("floodgate") != null) { - geyserConfig.loadFloodgate(this); - return true; - } - return false; + return Bukkit.getPluginManager().getPlugin("floodgate") != null; + } + + @Override + public Path getFloodgateKeyPath() { + Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate"); + Path geyserDataFolder = getDataFolder().toPath(); + Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; + + return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") @@ -484,10 +485,7 @@ private boolean loadConfig() { //noinspection ResultOfMethodCallIgnored getDataFolder().mkdir(); } - File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", - (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); - ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); + this.geyserConfig = ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneConfiguration.java deleted file mode 100644 index 1102ed0a998..00000000000 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneConfiguration.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.standalone; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Getter; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.nio.file.Path; -import java.nio.file.Paths; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -public final class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration { - @Override - public Path getFloodgateKeyPath() { - return Paths.get(getFloodgateKeyFile()); - } -} diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts index 4daad978470..103e40dacff 100644 --- a/bootstrap/velocity/build.gradle.kts +++ b/bootstrap/velocity/build.gradle.kts @@ -9,6 +9,7 @@ platformRelocate("com.fasterxml.jackson") platformRelocate("it.unimi.dsi.fastutil") platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl") platformRelocate("org.yaml") +platformRelocate("org.bstats") exclude("com.google.*:*") @@ -74,4 +75,4 @@ tasks.withType { modrinth { uploadFile.set(tasks.getByPath("shadowJar")) loaders.addAll("velocity") -} \ No newline at end of file +} diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityConfiguration.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityConfiguration.java deleted file mode 100644 index 4c8ea53cc89..00000000000 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.velocity; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.velocitypowered.api.plugin.PluginContainer; -import com.velocitypowered.api.proxy.ProxyServer; -import lombok.Getter; -import org.geysermc.geyser.FloodgateKeyLoader; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -public final class GeyserVelocityConfiguration extends GeyserJacksonConfiguration { - @JsonIgnore - private Path floodgateKeyPath; - - public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) { - Optional floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); - Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null; - floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataPath, dataFolder.toPath(), plugin.getGeyserLogger()); - } -} diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 539bdadbfb5..edda21d285b 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -34,42 +34,43 @@ import com.velocitypowered.api.network.ListenerType; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.FloodgateKeyLoader; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; import org.slf4j.Logger; -import java.io.File; import java.io.IOException; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; -import java.util.UUID; +import java.util.Optional; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") -public class GeyserVelocityPlugin implements GeyserBootstrap { +public class GeyserVelocityPlugin implements GeyserPluginBootstrap { private final ProxyServer proxyServer; private final CommandManager commandManager; private final GeyserVelocityLogger geyserLogger; private GeyserCommandManager geyserCommandManager; - private GeyserVelocityConfiguration geyserConfig; + private GeyserPluginConfig geyserConfig; private GeyserVelocityInjector geyserInjector; private IGeyserPingPassthrough geyserPingPassthrough; private GeyserImpl geyser; @@ -101,8 +102,7 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); this.geyserInjector = new GeyserVelocityInjector(proxyServer); @@ -114,8 +114,7 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + this.geyserLogger.setDebug(geyserConfig.debugMode()); } else { this.geyserCommandManager = new GeyserCommandManager(geyser); this.geyserCommandManager.init(); @@ -123,7 +122,7 @@ public void onGeyserEnable() { GeyserImpl.start(); - if (geyserConfig.isLegacyPingPassthrough()) { + if (!geyserConfig.integratedPingPassthrough()) { this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer); @@ -165,7 +164,7 @@ public void onGeyserShutdown() { } @Override - public GeyserVelocityConfiguration getGeyserConfig() { + public GeyserPluginConfig config() { return geyserConfig; } @@ -232,22 +231,24 @@ public int getServerPort() { @Override public boolean testFloodgatePluginPresent() { var floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); - if (floodgate.isPresent()) { - geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); - return true; - } - return false; + return floodgate.isPresent(); + } + + @Override + public Path getFloodgateKeyPath() { + Optional floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); + Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null; + return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataPath, configFolder, geyserLogger); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { try { - if (!configFolder.toFile().exists()) + if (!configFolder.toFile().exists()) { //noinspection ResultOfMethodCallIgnored configFolder.toFile().mkdirs(); - File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(), - "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class); + } + this.geyserConfig = ConfigLoaderTemp.load(configFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/viaproxy/build.gradle.kts b/bootstrap/viaproxy/build.gradle.kts index 6eadc790f78..358622d63c7 100644 --- a/bootstrap/viaproxy/build.gradle.kts +++ b/bootstrap/viaproxy/build.gradle.kts @@ -8,6 +8,7 @@ platformRelocate("net.kyori") platformRelocate("org.yaml") platformRelocate("it.unimi.dsi.fastutil") platformRelocate("org.cloudburstmc.netty") +platformRelocate("org.bstats") // These dependencies are already present on the platform provided(libs.viaproxy) diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyConfiguration.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyConfiguration.java deleted file mode 100644 index afc46fa6aac..00000000000 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyConfiguration.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2019-2023 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.viaproxy; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import net.raphimc.vialegacy.api.LegacyProtocolVersion; -import net.raphimc.viaproxy.ViaProxy; -import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; - -import java.io.File; -import java.nio.file.Path; - -@JsonIgnoreProperties(ignoreUnknown = true) -@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final -public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration { - - private RemoteConfiguration remote = new RemoteConfiguration() { - @Override - public boolean isForwardHost() { - return super.isForwardHost() || !ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE); - } - }; - - @Override - public Path getFloodgateKeyPath() { - return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath(); - } - - @Override - public int getPingPassthroughInterval() { - int interval = super.getPingPassthroughInterval(); - if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) { - // <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made - interval = 15; - } - return interval; - } - - @Override - public RemoteConfiguration getRemote() { - return this.remote; - } - -} diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index bdc80335a65..04276f64d14 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -33,24 +33,26 @@ import net.raphimc.viaproxy.plugins.events.ProxyStartEvent; import net.raphimc.viaproxy.plugins.events.ProxyStopEvent; import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; import org.apache.logging.log4j.LogManager; -import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; +import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.viaproxy.listener.GeyserServerTransferListener; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.LoopbackUtil; import org.jetbrains.annotations.NotNull; +import org.spongepowered.configurate.serialize.SerializationException; import java.io.File; import java.io.IOException; @@ -59,12 +61,12 @@ import java.nio.file.Path; import java.util.UUID; -public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, EventRegistrar { +public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserPluginBootstrap, EventRegistrar { - public static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser"); + private static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser"); private final GeyserViaProxyLogger logger = new GeyserViaProxyLogger(LogManager.getLogger("Geyser")); - private GeyserViaProxyConfiguration config; + private GeyserPluginConfig config; private GeyserImpl geyser; private GeyserCommandManager commandManager; private IGeyserPingPassthrough pingPassthrough; @@ -156,7 +158,7 @@ public void onGeyserShutdown() { } @Override - public GeyserConfiguration getGeyserConfig() { + public GeyserPluginConfig config() { return this.config; } @@ -209,17 +211,35 @@ public boolean testFloodgatePluginPresent() { return false; } + @Override + public Path getFloodgateKeyPath() { + return new File(ROOT_FOLDER, config.floodgateKeyFile()).toPath(); + } + private boolean loadConfig() { try { - final File configFile = FileUtils.fileOrCopiedFromResource(new File(ROOT_FOLDER, "config.yml"), "config.yml", s -> s.replaceAll("generateduuid", UUID.randomUUID().toString()), this); - this.config = FileUtils.loadConfig(configFile, GeyserViaProxyConfiguration.class); + this.config = ConfigLoaderTemp.load(new File(ROOT_FOLDER, "config.yml"), GeyserPluginConfig.class, node -> { + try { + if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) { // TODO + node.node("java", "forward-host").set(true); + } + + var pingPassthroughInterval = node.node("ping-passthrough-interval"); + int interval = pingPassthroughInterval.getInt(); + if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) { + // <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made + node.set(15); + } + } catch (SerializationException e) { + throw new RuntimeException(e); + } + }); } catch (IOException e) { this.logger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); return false; } - this.config.getRemote().setAuthType(Files.isRegularFile(this.config.getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE); - this.logger.setDebug(this.config.isDebugMode()); - GeyserConfiguration.checkGeyserConfiguration(this.config, this.logger); + this.config.java().authType(Files.isRegularFile(getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE); + this.logger.setDebug(this.config.debugMode()); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index 761e67a6725..abaa8e62618 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -25,14 +25,14 @@ package org.geysermc.geyser; -import org.geysermc.geyser.configuration.GeyserJacksonConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.text.GeyserLocale; import java.nio.file.Files; import java.nio.file.Path; public class FloodgateKeyLoader { - public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { + public static Path getKeyPath(GeyserConfig config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { // Always prioritize Floodgate's key, if it is installed. // This mostly prevents people from trying to copy the key and corrupting it in the process if (floodgateDataFolder != null) { @@ -45,13 +45,7 @@ public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateD } } - Path floodgateKey; - if (config.getFloodgateKeyFile().equals("public-key.pem")) { - logger.debug("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead"); - floodgateKey = geyserDataFolder.resolve("key.pem"); - } else { - floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile()); - } + Path floodgateKey = geyserDataFolder.resolve(config.floodgateKeyFile()); if (!Files.exists(floodgateKey)) { logger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed")); diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index 96a0078bdc8..2703236086e 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -29,7 +29,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.configuration.GeyserConfig; -import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.WorldManager; @@ -68,15 +67,6 @@ public interface GeyserBootstrap { */ void onGeyserShutdown(); - /** - * Returns the current GeyserConfiguration - * - * @return The current GeyserConfiguration - */ - default GeyserConfiguration getGeyserConfig() { - throw new UnsupportedOperationException(); - } - /** * Returns the current GeyserConfig * diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java deleted file mode 100644 index 88bb98171e6..00000000000 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2019-2024 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.configuration; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.api.network.BedrockListener; -import org.geysermc.geyser.api.network.RemoteServer; -import org.geysermc.geyser.network.CIDRMatcher; -import org.geysermc.geyser.network.GameProtocol; -import org.geysermc.geyser.text.GeyserLocale; - -import java.nio.file.Path; -import java.util.List; - -public interface GeyserConfiguration { - /** - * If the config was originally 'auto' before the values changed - */ - void setAutoconfiguredRemote(boolean autoconfiguredRemote); - - // Modify this when you introduce breaking changes into the config - int CURRENT_CONFIG_VERSION = 4; - - IBedrockConfiguration getBedrock(); - - IRemoteConfiguration getRemote(); - - List getSavedUserLogins(); - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean isCommandSuggestions(); - - @JsonIgnore - boolean isPassthroughMotd(); - - @JsonIgnore - boolean isPassthroughPlayerCounts(); - - @JsonIgnore - boolean isLegacyPingPassthrough(); - - int getPingPassthroughInterval(); - - boolean isForwardPlayerPing(); - - int getMaxPlayers(); - - boolean isDebugMode(); - - @Deprecated - boolean isAllowThirdPartyCapes(); - - @Deprecated - boolean isAllowThirdPartyEars(); - - String getShowCooldown(); - - boolean isShowCoordinates(); - - boolean isDisableBedrockScaffolding(); - - EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); - - String getDefaultLocale(); - - Path getFloodgateKeyPath(); - - boolean isAddNonBedrockItems(); - - boolean isAboveBedrockNetherBuilding(); - - boolean isForceResourcePacks(); - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean isXboxAchievementsEnabled(); - - int getCacheImages(); - - boolean isAllowCustomSkulls(); - - int getMaxVisibleCustomSkulls(); - - int getCustomSkullRenderDistance(); - - boolean isLogPlayerIpAddresses(); - - boolean isNotifyOnNewBedrockUpdate(); - - String getUnusableSpaceBlock(); - - IMetricsInfo getMetrics(); - - int getPendingAuthenticationTimeout(); - - boolean isAutoconfiguredRemote(); - - interface IBedrockConfiguration extends BedrockListener { - void setAddress(String address); - - void setPort(int port); - - void setBroadcastPort(int broadcastPort); - - boolean isCloneRemotePort(); - - int getCompressionLevel(); - - boolean isEnableProxyProtocol(); - - List getProxyProtocolWhitelistedIPs(); - - /** - * @return Unmodifiable list of {@link CIDRMatcher}s from {@link #getProxyProtocolWhitelistedIPs()} - */ - List getWhitelistedIPsMatchers(); - } - - interface IRemoteConfiguration extends RemoteServer { - - void setAddress(String address); - - void setPort(int port); - - boolean isUseProxyProtocol(); - - boolean isForwardHost(); - - default String minecraftVersion() { - return GameProtocol.getJavaMinecraftVersion(); - } - - default int protocolVersion() { - return GameProtocol.getJavaProtocolVersion(); - } - - void setAuthType(AuthType authType); - } - - interface IMetricsInfo { - - boolean isEnabled(); - - String getUniqueId(); - } - - int getScoreboardPacketThreshold(); - - // if u have offline mode enabled pls be safe - boolean isEnableProxyConnections(); - - int getMtu(); - - boolean isUseDirectConnection(); - - boolean isDisableCompression(); - - int getConfigVersion(); - - static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) { - if (geyserConfig.getConfigVersion() < CURRENT_CONFIG_VERSION) { - geyserLogger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.outdated")); - } else if (geyserConfig.getConfigVersion() > CURRENT_CONFIG_VERSION) { - geyserLogger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.too_new")); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java deleted file mode 100644 index 81ac824e432..00000000000 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.configuration; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import lombok.Getter; -import lombok.Setter; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.api.network.AuthType; -import org.geysermc.geyser.network.CIDRMatcher; -import org.geysermc.geyser.text.AsteriskSerializer; -import org.geysermc.geyser.text.GeyserLocale; -import org.geysermc.geyser.util.WebUtils; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final -public abstract class GeyserJacksonConfiguration implements GeyserConfiguration { - - @Setter - private boolean autoconfiguredRemote = false; - - private BedrockConfiguration bedrock = new BedrockConfiguration(); - private RemoteConfiguration remote = new RemoteConfiguration(); - - @JsonProperty("saved-user-logins") - private List savedUserLogins = Collections.emptyList(); - - @JsonProperty("floodgate-key-file") - private String floodgateKeyFile = "key.pem"; - - public abstract Path getFloodgateKeyPath(); - - @JsonProperty("command-suggestions") - private boolean commandSuggestions = true; - - @JsonProperty("passthrough-motd") - private boolean isPassthroughMotd = false; - - @JsonProperty("passthrough-player-counts") - private boolean isPassthroughPlayerCounts = false; - - @JsonProperty("legacy-ping-passthrough") - private boolean isLegacyPingPassthrough = false; - - @JsonProperty("ping-passthrough-interval") - private int pingPassthroughInterval = 3; - - @JsonProperty("forward-player-ping") - private boolean forwardPlayerPing = false; - - @JsonProperty("max-players") - private int maxPlayers = 100; - - @JsonProperty("debug-mode") - private boolean debugMode = false; - - @JsonProperty("allow-third-party-capes") - private boolean allowThirdPartyCapes = false; - - @JsonProperty("show-cooldown") - private String showCooldown = "title"; - - @JsonProperty("show-coordinates") - private boolean showCoordinates = true; - - @JsonProperty("disable-bedrock-scaffolding") - private boolean disableBedrockScaffolding = false; - - @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) - @JsonProperty("emote-offhand-workaround") - private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; - - @JsonProperty("allow-third-party-ears") - private boolean allowThirdPartyEars = false; - - @JsonProperty("default-locale") - private String defaultLocale = null; // is null by default so system language takes priority - - @JsonProperty("cache-images") - private int cacheImages = 0; - - @JsonProperty("allow-custom-skulls") - private boolean allowCustomSkulls = true; - - @JsonProperty("max-visible-custom-skulls") - private int maxVisibleCustomSkulls = 128; - - @JsonProperty("custom-skull-render-distance") - private int customSkullRenderDistance = 32; - - @JsonProperty("add-non-bedrock-items") - private boolean addNonBedrockItems = true; - - @JsonProperty("above-bedrock-nether-building") - private boolean aboveBedrockNetherBuilding = false; - - @JsonProperty("force-resource-packs") - private boolean forceResourcePacks = true; - - @JsonProperty("xbox-achievements-enabled") - private boolean xboxAchievementsEnabled = false; - - @JsonProperty("log-player-ip-addresses") - private boolean logPlayerIpAddresses = true; - - @JsonProperty("notify-on-new-bedrock-update") - private boolean notifyOnNewBedrockUpdate = true; - - @JsonProperty("unusable-space-block") - private String unusableSpaceBlock = "minecraft:barrier"; - - private MetricsInfo metrics = new MetricsInfo(); - - @JsonProperty("pending-authentication-timeout") - private int pendingAuthenticationTimeout = 120; - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class BedrockConfiguration implements IBedrockConfiguration { - @AsteriskSerializer.Asterisk(isIp = true) - @JsonProperty("address") - @Setter - private String address = "0.0.0.0"; - - @Override - public @NonNull String address() { - return address; - } - - @Setter - @JsonProperty("port") - private int port = 19132; - - @Override - public int port() { - return port; - } - - @Setter - @JsonProperty("broadcast-port") - private int broadcastPort = 0; - - @Override - public int broadcastPort() { - return broadcastPort; - } - - @Getter - @JsonProperty("clone-remote-port") - private boolean cloneRemotePort = false; - - @JsonProperty("motd1") - private String motd1 = "GeyserMC"; - - @Override - public String primaryMotd() { - return motd1; - } - - @JsonProperty("motd2") - private String motd2 = "Geyser"; - - @Override - public String secondaryMotd() { - return motd2; - } - - @JsonProperty("server-name") - private String serverName = GeyserImpl.NAME; - - @Override - public String serverName() { - return serverName; - } - - @JsonProperty("compression-level") - private int compressionLevel = 6; - - public int getCompressionLevel() { - return Math.max(-1, Math.min(compressionLevel, 9)); - } - - @Getter - @JsonProperty("enable-proxy-protocol") - private boolean enableProxyProtocol = false; - - @Getter - @JsonProperty("proxy-protocol-whitelisted-ips") - private List proxyProtocolWhitelistedIPs = Collections.emptyList(); - - @JsonIgnore - private List whitelistedIPsMatchers = null; - - @Override - public List getWhitelistedIPsMatchers() { - // Effective Java, Third Edition; Item 83: Use lazy initialization judiciously - List matchers = this.whitelistedIPsMatchers; - if (matchers == null) { - synchronized (this) { - // Check if proxyProtocolWhitelistedIPs contains URLs we need to fetch and parse by line - List whitelistedCIDRs = new ArrayList<>(); - for (String ip: proxyProtocolWhitelistedIPs) { - if (!ip.startsWith("http")) { - whitelistedCIDRs.add(ip); - continue; - } - - WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add); - } - - this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream() - .map(CIDRMatcher::new) - .collect(Collectors.toList()); - } - } - return Collections.unmodifiableList(matchers); - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class RemoteConfiguration implements IRemoteConfiguration { - @Setter - @AsteriskSerializer.Asterisk(isIp = true) - @JsonProperty("address") - private String address = "auto"; - - @Override - public String address() { - return address; - } - - @JsonDeserialize(using = PortDeserializer.class) - @Setter - @JsonProperty("port") - private int port = 25565; - - @Override - public int port() { - return port; - } - - @Setter - @JsonDeserialize(using = AuthTypeDeserializer.class) - @JsonProperty("auth-type") - private AuthType authType = AuthType.ONLINE; - - @Override - public @NonNull AuthType authType() { - return authType; - } - - @Override - public boolean resolveSrv() { - return false; - } - - @Getter - @JsonProperty("use-proxy-protocol") - private boolean useProxyProtocol = false; - - @Getter - @JsonProperty("forward-hostname") - private boolean forwardHost = false; - } - - @Getter - @JsonIgnoreProperties(ignoreUnknown = true) - public static class MetricsInfo implements IMetricsInfo { - private boolean enabled = true; - - @JsonDeserialize(using = MetricsIdDeserializer.class) - @JsonProperty("uuid") - private String uniqueId = UUID.randomUUID().toString(); - - private static class MetricsIdDeserializer extends JsonDeserializer { - @Override - public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String uuid = p.getValueAsString(); - if ("generateduuid".equals(uuid)) { - // Compensate for configs not copied from the jar - return UUID.randomUUID().toString(); - } - return uuid; - } - } - } - - @JsonProperty("scoreboard-packet-threshold") - private int scoreboardPacketThreshold = 10; - - @JsonProperty("enable-proxy-connections") - private boolean enableProxyConnections = false; - - @JsonProperty("mtu") - private int mtu = 1400; - - @JsonProperty("use-direct-connection") - private boolean useDirectConnection = true; - - @JsonProperty("disable-compression") - private boolean isDisableCompression = true; - - @JsonProperty("config-version") - private int configVersion = 0; - - /** - * Ensure that the port deserializes in the config as a number no matter what. - */ - protected static class PortDeserializer extends JsonDeserializer { - @Override - public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.invalid_port")); - return 25565; - } - } - } - - public static class AuthTypeDeserializer extends JsonDeserializer { - @Override - public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - return AuthType.getByName(p.getValueAsString()); - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index c2f9b97a0c5..2ae6214e056 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -25,15 +25,13 @@ package org.geysermc.geyser.configuration; -import org.geysermc.geyser.GeyserImpl; import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.spongepowered.configurate.interfaces.meta.Field; import org.spongepowered.configurate.interfaces.meta.Hidden; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; -import java.io.File; - @ConfigSerializable public interface GeyserPluginConfig extends GeyserConfig { @Override @@ -54,35 +52,18 @@ interface IntegratedBedrockConfig extends BedrockConfig { @ConfigSerializable interface IntegratedJavaConfig extends JavaConfig { @Override - @Exclude - default String address() { - return GeyserImpl.getInstance().getBootstrap().getServerBindAddress(); - } + @Field + String address(); @Override - default void address(String address) { - throw new IllegalStateException(); - } + void address(String address); @Override - @Exclude - default int port() { - return GeyserImpl.getInstance().getBootstrap().getServerPort(); - } + @Field + int port(); @Override - default void port(int port) { - throw new IllegalStateException(); - } - -// @Nonnull -// @Comment(""" -// What type of authentication Bedrock players will be checked against when logging into the Java server. -// Floodgate allows Bedrock players to join without needing a Java account. It's not recommended to change this.""") -// @Override -// default AuthType authType() { -// return AuthType.FLOODGATE; -// } + void port(int port); @Override @Exclude diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index a6186026ce5..d8e03be2559 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -36,7 +36,6 @@ import lombok.Getter; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.floodgate.util.DeviceOs; -import org.geysermc.floodgate.util.FloodgateInfoHolder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; @@ -61,7 +60,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Properties; import java.util.stream.Collectors; @Getter @@ -75,7 +73,6 @@ public class DumpInfo { private final String systemEncoding; private final GitInfo gitInfo; private final GeyserConfig config; - private final Floodgate floodgate; private final Object2IntMap userPlatforms; private final int connectionAttempts; private final HashInfo hashInfo; @@ -96,7 +93,6 @@ public DumpInfo(boolean addLog) { this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); this.config = GeyserImpl.getInstance().config(); - this.floodgate = new Floodgate(); String md5Hash = "unknown"; String sha256Hash = "unknown"; @@ -233,17 +229,6 @@ public static class MCInfo { } } - @Getter - public static class Floodgate { - private final Properties gitInfo; - private final Object config; - - Floodgate() { - this.gitInfo = FloodgateInfoHolder.getGitProperties(); - this.config = FloodgateInfoHolder.getConfig(); - } - } - @Getter public static class LogsInfo { private String link; diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 2d5f2cbdceb..e04146e2fd8 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -115,7 +115,7 @@ import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandSource; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.GeyserEntityData; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; @@ -600,7 +600,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server. - * Only used if {@link GeyserConfiguration#isForwardPlayerPing()} is enabled. + * Only used if {@link GeyserConfig#forwardPlayerPing()} is enabled. */ private final Queue keepAliveCache = new ConcurrentLinkedQueue<>(); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java index 78a6b75febc..7eee215fa28 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/PreferencesCache.java @@ -27,7 +27,7 @@ import lombok.Getter; import lombok.Setter; -import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.CooldownUtils; @@ -72,7 +72,7 @@ public PreferencesCache(GeyserSession session) { * If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply:
*
* {@link GeyserSession#isReducedDebugInfo()} is enabled - * {@link GeyserConfiguration#isShowCoordinates()} is disabled + * {@link GeyserConfig#showCoordinates()} is disabled */ public void updateShowCoordinates() { allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().config().showCoordinates(); From ab4cc97b38bb5f799aca7e966c4645d76e40dfbd Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 9 Jul 2024 12:26:27 -0400 Subject: [PATCH 14/42] More changes and migrations away from Jackson --- bootstrap/bungeecord/build.gradle.kts | 3 +- .../bungeecord/GeyserBungeePlugin.java | 4 +- .../platform/fabric/GeyserFabricDumpInfo.java | 3 +- .../neoforge/GeyserNeoForgeDumpInfo.java | 5 +- .../platform/mod/GeyserModBootstrap.java | 4 +- bootstrap/spigot/build.gradle.kts | 1 - .../platform/spigot/GeyserSpigotDumpInfo.java | 3 +- .../platform/spigot/GeyserSpigotPlugin.java | 4 +- .../standalone/GeyserStandaloneBootstrap.java | 4 +- bootstrap/velocity/build.gradle.kts | 3 +- .../velocity/GeyserVelocityDumpInfo.java | 3 +- .../velocity/GeyserVelocityPlugin.java | 4 +- .../viaproxy/GeyserViaProxyDumpInfo.java | 3 +- .../viaproxy/GeyserViaProxyPlugin.java | 4 +- ...onfigLoaderTemp.java => ConfigLoader.java} | 5 +- .../EmoteOffhandWorkaroundOption.java | 50 ----------- .../geyser/dump/BootstrapDumpInfo.java | 3 +- .../geysermc/geyser/ping/GeyserPingInfo.java | 10 --- .../CustomSkullRegistryPopulator.java | 2 +- .../geyser/text/AsteriskSerializer.java | 82 +++++-------------- .../org/geysermc/geyser/util/FileUtils.java | 28 +------ .../org/geysermc/geyser/util/WebUtils.java | 9 +- 22 files changed, 65 insertions(+), 172 deletions(-) rename core/src/main/java/org/geysermc/geyser/configuration/{ConfigLoaderTemp.java => ConfigLoader.java} (98%) delete mode 100644 core/src/main/java/org/geysermc/geyser/configuration/EmoteOffhandWorkaroundOption.java diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts index c9ae080d914..fbf77f85876 100644 --- a/bootstrap/bungeecord/build.gradle.kts +++ b/bootstrap/bungeecord/build.gradle.kts @@ -5,10 +5,11 @@ dependencies { } platformRelocate("net.md_5.bungee.jni") -platformRelocate("com.fasterxml.jackson") platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound platformRelocate("net.kyori") platformRelocate("org.yaml") // Broken as of 1.20 +platformRelocate("org.spongepowered") +platformRelocate("io.leangen.geantyref") platformRelocate("org.bstats") // These dependencies are already present on the platform diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index 2a356140b10..369788674ca 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -39,7 +39,7 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -293,7 +293,7 @@ private boolean loadConfig() { //noinspection ResultOfMethodCallIgnored getDataFolder().mkdir(); } - this.geyserConfig = ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); + this.geyserConfig = ConfigLoader.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); return false; diff --git a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricDumpInfo.java b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricDumpInfo.java index 75da9125f3f..c4efdf9fffb 100644 --- a/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricDumpInfo.java +++ b/bootstrap/mod/fabric/src/main/java/org/geysermc/geyser/platform/fabric/GeyserFabricDumpInfo.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.fabric; +import com.google.gson.annotations.JsonAdapter; import lombok.AllArgsConstructor; import lombok.Getter; import net.fabricmc.api.EnvType; @@ -48,7 +49,7 @@ public class GeyserFabricDumpInfo extends BootstrapDumpInfo { private final String minecraftVersion; private final EnvType environmentType; - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) private final String serverIP; private final int serverPort; private final boolean onlineMode; diff --git a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeDumpInfo.java b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeDumpInfo.java index 623f68d3a1f..c8104f2d487 100644 --- a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeDumpInfo.java +++ b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeDumpInfo.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.neoforge; +import com.google.gson.annotations.JsonAdapter; import lombok.AllArgsConstructor; import lombok.Getter; import net.minecraft.server.MinecraftServer; @@ -47,7 +48,7 @@ public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo { private final String minecraftVersion; private final Dist dist; - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) private final String serverIP; private final int serverPort; private final boolean onlineMode; @@ -81,4 +82,4 @@ public static class ModInfo { public String version; public String url; } -} \ No newline at end of file +} diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java index c8597bc548c..7410b359cb5 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java @@ -44,7 +44,7 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; @@ -286,7 +286,7 @@ private boolean loadConfig() { dataFolder.toFile().mkdir(); } - this.geyserConfig = ConfigLoaderTemp.load(dataFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); + this.geyserConfig = ConfigLoader.load(dataFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); return true; } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index 810bbc5b25b..adac8d2a039 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -33,7 +33,6 @@ dependencies { } platformRelocate("it.unimi.dsi.fastutil") -platformRelocate("com.fasterxml.jackson") // Relocate net.kyori but exclude the component logger platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger") platformRelocate("org.objectweb.asm") diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java index 329663709fc..6e063f41575 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotDumpInfo.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.spigot; +import com.google.gson.annotations.JsonAdapter; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; @@ -42,7 +43,7 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo { private final String platformAPIVersion; private final boolean onlineMode; - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) private final String serverIP; private final int serverPort; private final List plugins; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 1eccca30eee..7686333ef02 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -53,7 +53,7 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; @@ -485,7 +485,7 @@ private boolean loadConfig() { //noinspection ResultOfMethodCallIgnored getDataFolder().mkdir(); } - this.geyserConfig = ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); + this.geyserConfig = ConfigLoader.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 0037e376d69..ea29e5e1707 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -38,7 +38,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.configuration.GeyserRemoteConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -160,7 +160,7 @@ public void onGeyserInitialize() { @Override public void onGeyserEnable() { try { - geyserConfig = ConfigLoaderTemp.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions); + geyserConfig = ConfigLoader.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions); } catch (IOException ex) { geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); if (gui == null) { diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts index 103e40dacff..a50d922b90f 100644 --- a/bootstrap/velocity/build.gradle.kts +++ b/bootstrap/velocity/build.gradle.kts @@ -5,10 +5,11 @@ dependencies { compileOnlyApi(libs.velocity.api) } -platformRelocate("com.fasterxml.jackson") platformRelocate("it.unimi.dsi.fastutil") platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl") platformRelocate("org.yaml") +platformRelocate("org.spongepowered") +platformRelocate("io.leangen.geantyref") platformRelocate("org.bstats") exclude("com.google.*:*") diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java index 45eb7abb9a5..6bc309f2990 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityDumpInfo.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.platform.velocity; +import com.google.gson.annotations.JsonAdapter; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; @@ -42,7 +43,7 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo { private final String platformVendor; private final boolean onlineMode; - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) private final String serverIP; private final int serverPort; private final List plugins; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index edda21d285b..08b2c28d783 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -46,7 +46,7 @@ import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.network.GameProtocol; @@ -248,7 +248,7 @@ private boolean loadConfig() { //noinspection ResultOfMethodCallIgnored configFolder.toFile().mkdirs(); } - this.geyserConfig = ConfigLoaderTemp.load(configFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); + this.geyserConfig = ConfigLoader.load(configFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); } catch (IOException ex) { geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyDumpInfo.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyDumpInfo.java index 0bfc9d022f2..4478d11ef1c 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyDumpInfo.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyDumpInfo.java @@ -24,6 +24,7 @@ */ package org.geysermc.geyser.platform.viaproxy; +import com.google.gson.annotations.JsonAdapter; import lombok.Getter; import net.raphimc.viaproxy.ViaProxy; import net.raphimc.viaproxy.plugins.ViaProxyPlugin; @@ -41,7 +42,7 @@ public class GeyserViaProxyDumpInfo extends BootstrapDumpInfo { private final String platformVersion; private final boolean onlineMode; - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) private final String serverIP; private final int serverPort; private final List plugins; diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index 04276f64d14..59a0d786624 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -42,7 +42,7 @@ import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommandManager; -import org.geysermc.geyser.configuration.ConfigLoaderTemp; +import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -218,7 +218,7 @@ public Path getFloodgateKeyPath() { private boolean loadConfig() { try { - this.config = ConfigLoaderTemp.load(new File(ROOT_FOLDER, "config.yml"), GeyserPluginConfig.class, node -> { + this.config = ConfigLoader.load(new File(ROOT_FOLDER, "config.yml"), GeyserPluginConfig.class, node -> { try { if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) { // TODO node.node("java", "forward-host").set(true); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java similarity index 98% rename from core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java rename to core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 59a68ba0e43..43e5fa1f7d8 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoaderTemp.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -39,7 +39,7 @@ import static org.spongepowered.configurate.transformation.TransformAction.remove; import static org.spongepowered.configurate.transformation.TransformAction.rename; -public final class ConfigLoaderTemp { +public final class ConfigLoader { private static final String HEADER = """ -------------------------------- Geyser Configuration File @@ -132,4 +132,7 @@ public static T load(File file, Class configClass, @ return config; } + + private ConfigLoader() { + } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/EmoteOffhandWorkaroundOption.java b/core/src/main/java/org/geysermc/geyser/configuration/EmoteOffhandWorkaroundOption.java deleted file mode 100644 index fd44d390389..00000000000 --- a/core/src/main/java/org/geysermc/geyser/configuration/EmoteOffhandWorkaroundOption.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2019-2022 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.configuration; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import java.io.IOException; - -public enum EmoteOffhandWorkaroundOption { - NO_EMOTES, - EMOTES_AND_OFFHAND, - DISABLED; - - public static class Deserializer extends JsonDeserializer { - @Override - public EmoteOffhandWorkaroundOption deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); - return switch (value) { - case "no-emotes" -> NO_EMOTES; - case "emotes-and-offhand" -> EMOTES_AND_OFFHAND; - default -> DISABLED; - }; - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java index 7851fadfd3c..2956c0329ae 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.dump; +import com.google.gson.annotations.JsonAdapter; import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; @@ -55,7 +56,7 @@ public static class PluginInfo { @AllArgsConstructor public static class ListenerInfo { - @AsteriskSerializer.Asterisk(isIp = true) + @JsonAdapter(value = AsteriskSerializer.class) public String ip; public int port; } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java index 9d8da114d1c..cff46d834e1 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java @@ -25,9 +25,6 @@ package org.geysermc.geyser.ping; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import org.checkerframework.checker.nullness.qual.Nullable; @@ -36,7 +33,6 @@ * designed for the format received by {@link GeyserLegacyPingPassthrough}. */ @Data -@JsonIgnoreProperties(ignoreUnknown = true) public class GeyserPingInfo { @Nullable @@ -58,13 +54,7 @@ public GeyserPingInfo(@Nullable String description, int maxPlayers, int onlinePl this.players = new Players(maxPlayers, onlinePlayers); } - @JsonSetter("description") - void setDescription(JsonNode description) { - this.description = description.toString(); - } - @Data - @JsonIgnoreProperties(ignoreUnknown = true) public static class Players { private int max; diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java index e44550d71a2..472b4c8ac07 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CustomSkullRegistryPopulator.java @@ -64,7 +64,7 @@ public static void populate() { GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml"); File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap); - skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class); + skullConfig = FileUtils.loadConfig(skullConfigFile, GeyserCustomSkullConfiguration.class); } catch (IOException e) { GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); return; diff --git a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java index 66b61dbff0f..325eaccae21 100644 --- a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java +++ b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java @@ -25,76 +25,26 @@ package org.geysermc.geyser.text; -import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; -import java.io.IOException; -import java.io.Serial; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Optional; - -public class AsteriskSerializer extends StdSerializer implements ContextualSerializer { - - @Serial - private static final long serialVersionUID = 1L; +import java.lang.reflect.Type; +import java.net.InetAddress; +public class AsteriskSerializer implements JsonSerializer { public static final String[] NON_SENSITIVE_ADDRESSES = {"", "0.0.0.0", "localhost", "127.0.0.1", "auto", "unknown"}; public static boolean showSensitive = false; - @Target({ElementType.FIELD}) - @Retention(RetentionPolicy.RUNTIME) - @JacksonAnnotationsInside - @JsonSerialize(using = AsteriskSerializer.class) - public @interface Asterisk { - String value() default "***"; - /** - * If true, this value will be shown if {@link #showSensitive} is true, or if the IP is determined to not be a public IP - * - * @return true if this should be analyzed and treated as an IP - */ - boolean isIp() default false; - } - - String asterisk; - boolean isIp; - - @SuppressWarnings("unused") // Used by Jackson for Geyser dumps - public AsteriskSerializer() { - super(Object.class); - } - - public AsteriskSerializer(String asterisk, boolean isIp) { - super(Object.class); - this.asterisk = asterisk; - this.isIp = isIp; - } - @Override - public JsonSerializer createContextual(SerializerProvider serializerProvider, BeanProperty property) { - Optional anno = Optional.ofNullable(property) - .map(prop -> prop.getAnnotation(Asterisk.class)); - - return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null), anno.map(Asterisk::isIp).orElse(false)); - } - - @Override - public void serialize(Object obj, JsonGenerator gen, SerializerProvider prov) throws IOException { - if (isIp && (showSensitive || !isSensitiveIp((String) obj))) { - gen.writeObject(obj); - return; + public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) { + if (showSensitive || !isSensitiveIp(src)) { + return new JsonPrimitive(src); } - gen.writeString(asterisk); + return new JsonPrimitive("***"); } private boolean isSensitiveIp(String ip) { @@ -103,6 +53,16 @@ private boolean isSensitiveIp(String ip) { return false; } } + + try { + InetAddress address = InetAddress.getByName(ip); + if (address.isSiteLocalAddress() || address.isLoopbackAddress()) { + return false; + } + } catch (Exception e) { + // Ignore + } + return true; } } diff --git a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java index 0bf389d3fdf..3ea113154f2 100644 --- a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java @@ -25,11 +25,6 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.annotation.JsonSetter; -import com.fasterxml.jackson.annotation.Nulls; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.spongepowered.configurate.ConfigurationNode; @@ -51,26 +46,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class FileUtils { - - /** - * Load the given YAML file into the given class - * - * @param src File to load - * @param valueType Class to load file into - * @param the type - * @return The data as the given class - * @throws IOException if the config could not be loaded - */ +public final class FileUtils { public static T loadConfig(File src, Class valueType) throws IOException { - ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()) - // Allow inference of single values as arrays - .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)); - return objectMapper.readValue(src, valueType); - } - - public static T loadConfigNew(File src, Class valueType) throws IOException { YamlConfigurationLoader loader = YamlConfigurationLoader.builder() .file(src) .build(); @@ -257,4 +234,7 @@ public static Set> getGeneratedClassesForAnnotation(String input) { throw new RuntimeException(e); } } + + private FileUtils() { + } } diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 1c9df9c08d2..5f489b900e4 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.databind.JsonNode; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; @@ -34,7 +33,11 @@ import javax.naming.directory.Attribute; import javax.naming.directory.InitialDirContext; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; @@ -69,7 +72,7 @@ public static String getBody(String reqURL) { } /** - * Makes a web request to the given URL and returns the body as a {@link JsonNode}. + * Makes a web request to the given URL and returns the body as a {@link JsonObject}. * * @param reqURL URL to fetch * @return the response as JSON From 76ca326590ff63ccf76a8eae1aadfffcc6a0e836 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:15:47 -0500 Subject: [PATCH 15/42] Improve node ordering when updating configs --- .../viaproxy/GeyserViaProxyPlugin.java | 2 +- .../geyser/configuration/ConfigLoader.java | 41 ++++++--- .../ConfigurationCommentMover.java | 85 +++++++++++++++++++ .../configuration/ConfigLoaderTest.java | 79 +++++++++++++++++ 4 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/ConfigurationCommentMover.java create mode 100644 core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index 59a0d786624..908ceec7935 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -228,7 +228,7 @@ private boolean loadConfig() { int interval = pingPassthroughInterval.getInt(); if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) { // <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made - node.set(15); + pingPassthroughInterval.set(15); } } catch (SerializationException e) { throw new RuntimeException(e); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 43e5fa1f7d8..4872a8c17bd 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -29,6 +29,7 @@ import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.yaml.NodeStyle; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.File; @@ -61,13 +62,16 @@ public static T load(File file, Class configClass) t public static T load(File file, Class configClass, @Nullable Consumer transformer) throws IOException { var loader = YamlConfigurationLoader.builder() .file(file) - .defaultOptions(options -> InterfaceDefaultOptions.addTo(options + .indent(2) + .nodeStyle(NodeStyle.BLOCK) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options) + .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. .header(HEADER) - .serializers(builder -> builder.register(new LowercaseEnumSerializer())))) + .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) .build(); - var node = loader.load(); - // temp fix for node.virtual() being broken - boolean virtual = file.exists(); + + CommentedConfigurationNode node = loader.load(); + boolean originallyEmpty = !file.exists() || node.isNull(); // Note for Tim? Needed or else Configurate breaks. var migrations = ConfigurationTransformation.versionedBuilder() @@ -120,14 +124,31 @@ public static T load(File file, Class configClass, @ T config = node.get(configClass); - if (virtual || currentVersion != newVersion) { - loader.save(node); + // Serialize the instance to ensure strict field ordering. Additionally, If we serialized back + // to the old node, existing nodes would only have their value changed, keeping their position + // at the top of the ordered map, forcing all new nodes to the bottom (regardless of field order). + // For that reason, we must also create a new node. + CommentedConfigurationNode newRoot = CommentedConfigurationNode.root(loader.defaultOptions()); + newRoot.set(config); + + if (originallyEmpty || currentVersion != newVersion) { + + if (!originallyEmpty && currentVersion != 4) { + // Only copy comments over if the file already existed, and we are going to replace it + + // Second case: Version 4 is pre-configurate where there were commented out nodes. + // These get treated as comments on lower nodes, which produces very undesirable results. + + ConfigurationCommentMover.moveComments(node, newRoot); + } + + loader.save(newRoot); } if (transformer != null) { - // Do not let the transformer change the node. - transformer.accept(node); - config = node.get(configClass); + // We transform AFTER saving so that these specific transformations aren't applied to file. + transformer.accept(newRoot); + config = newRoot.get(configClass); } return config; diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigurationCommentMover.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigurationCommentMover.java new file mode 100644 index 00000000000..4aed64da738 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigurationCommentMover.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.ConfigurationVisitor; + +/** + * Moves comments from a different node and puts them on this node + */ +public final class ConfigurationCommentMover implements ConfigurationVisitor.Stateless { + + private final CommentedConfigurationNode otherRoot; + + private ConfigurationCommentMover(@NonNull CommentedConfigurationNode otherNode) { + this.otherRoot = otherNode; + } + + @Override + public void enterNode(final ConfigurationNode node) { + if (!(node instanceof CommentedConfigurationNode destination)) { + // Should not occur because all nodes in a tree are the same type, + // and our static method below ensures this visitor is only used on CommentedConfigurationNodes + throw new IllegalStateException(node.path() + " is not a CommentedConfigurationNode"); + } + // Node with the same path + CommentedConfigurationNode source = otherRoot.node(node.path()); + + moveSingle(source, destination); + } + + private static void moveSingle(@NonNull CommentedConfigurationNode source, @NonNull CommentedConfigurationNode destination) { + // Only transfer the comment, overriding if necessary + String comment = source.comment(); + if (comment != null) { + destination.comment(comment); + } + } + + /** + * Moves comments from a source node and its children to a destination node and its children (of a different tree), overriding if necessary. + * Comments are only moved to the destination node and its children which exist. + * Comments are only moved to and from nodes with the exact same path. + * + * @param source the source of the comments, which must be the topmost parent of a tree + * @param destination the destination of the comments, any node in a different tree + */ + public static void moveComments(@NonNull CommentedConfigurationNode source, @NonNull CommentedConfigurationNode destination) { + if (source.parent() != null) { + throw new IllegalArgumentException("source is not the base of the tree it is within: " + source.path()); + } + + if (source.isNull()) { + // It has no value(s), but may still have a comment on it. Don't both traversing the whole destination tree. + moveSingle(source, destination); + } else { + destination.visit(new ConfigurationCommentMover(source)); + } + } +} diff --git a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java new file mode 100644 index 00000000000..0dcc7d33d70 --- /dev/null +++ b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.util.CheckedConsumer; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ConfigLoaderTest { + + @TempDir + Path tempDirectory; + + // Hack until improving ConfigLoader + CommentedConfigurationNode config1; + CommentedConfigurationNode config2; + + @Test + void testCreateNewConfig() throws Exception { + // Test that the result of generating a config, and the result of reading it back after writing it, is the same + + File file = tempDirectory.resolve("config.yml").toFile(); + + forAllConfigs(type -> { + ConfigLoader.load(file, type, n -> this.config1 = n.copy()); + long initialModification = file.lastModified(); + assertTrue(file.exists()); // should have been created + List firstContents = Files.readAllLines(file.toPath()); + + ConfigLoader.load(file, type, n -> this.config2 = n.copy()); + List secondContents = Files.readAllLines(file.toPath()); + + assertEquals(initialModification, file.lastModified()); // should not have been touched + assertEquals(firstContents, secondContents); + + // Must ignore this, as when the config is read back, the header is interpreted as a comment on the first node in the map + config1.node("java").comment(null); + config2.node("java").comment(null); + assertEquals(config1, config2); + }); + } + + void forAllConfigs(CheckedConsumer, Exception> consumer) throws Exception { + consumer.accept(GeyserRemoteConfig.class); + consumer.accept(GeyserPluginConfig.class); + } +} From 0952f79c445515ab20b87519a550508eccae8ab8 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:21:14 -0500 Subject: [PATCH 16/42] typo --- .../java/org/geysermc/geyser/configuration/ConfigLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 4872a8c17bd..844c3f8f039 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -124,7 +124,7 @@ public static T load(File file, Class configClass, @ T config = node.get(configClass); - // Serialize the instance to ensure strict field ordering. Additionally, If we serialized back + // Serialize the instance to ensure strict field ordering. Additionally, if we serialized back // to the old node, existing nodes would only have their value changed, keeping their position // at the top of the ordered map, forcing all new nodes to the bottom (regardless of field order). // For that reason, we must also create a new node. From e506c14eb4bfb5404269a620582c1d5ae252a251 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:30:44 -0500 Subject: [PATCH 17/42] Better check for ignoring non-configurate configs for considering comment moving --- .../java/org/geysermc/geyser/configuration/ConfigLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 844c3f8f039..e8e2052757e 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -133,7 +133,7 @@ public static T load(File file, Class configClass, @ if (originallyEmpty || currentVersion != newVersion) { - if (!originallyEmpty && currentVersion != 4) { + if (!originallyEmpty && currentVersion > 4) { // Only copy comments over if the file already existed, and we are going to replace it // Second case: Version 4 is pre-configurate where there were commented out nodes. From d9d78cd9ca27e3380c15699326c8bfc72050e9fd Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:38:13 -0400 Subject: [PATCH 18/42] Ensure metrics UUID is valid --- .../org/geysermc/geyser/configuration/ConfigLoader.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index e8e2052757e..99bb069ed92 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -34,6 +34,7 @@ import java.io.File; import java.io.IOException; +import java.util.UUID; import java.util.function.Consumer; import static org.spongepowered.configurate.NodePath.path; @@ -107,6 +108,13 @@ public static T load(File file, Class configClass, @ } return null; }) + .addAction(path("metrics", "uuid"), (path, value) -> { + if ("generateduuid".equals(value.getString())) { + // Manually copied config without Metrics UUID creation? + return new Object[]{UUID.randomUUID()}; + } + return null; + }) .addAction(path("bedrock", "motd1"), rename("primary-motd")) .addAction(path("bedrock", "motd2"), rename("secondary-motd")) // Legacy config values From f113c8967ed8a72174157de03522cee73878f30c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 24 Aug 2024 18:56:15 -0400 Subject: [PATCH 19/42] Initial advanced config --- .../bungeecord/GeyserBungeeInjector.java | 2 +- .../platform/mod/GeyserModInjector.java | 2 +- .../platform/spigot/GeyserSpigotInjector.java | 2 +- .../standalone/GeyserStandaloneBootstrap.java | 2 +- .../velocity/GeyserVelocityInjector.java | 2 +- .../viaproxy/GeyserViaProxyPlugin.java | 2 +- .../java/org/geysermc/geyser/Constants.java | 1 + .../geysermc/geyser/FloodgateKeyLoader.java | 2 +- .../java/org/geysermc/geyser/GeyserImpl.java | 4 +- .../geyser/configuration/AdvancedConfig.java | 111 ++++++++++++++++++ .../geyser/configuration/ConfigLoader.java | 107 +++++++++++++++-- .../geyser/configuration/GeyserConfig.java | 67 +++-------- .../configuration/GeyserPluginConfig.java | 13 -- .../configuration/GeyserRemoteConfig.java | 8 -- .../org/geysermc/geyser/dump/DumpInfo.java | 3 + .../geyser/network/netty/GeyserInjector.java | 2 +- .../geyser/network/netty/GeyserServer.java | 4 +- .../geyser/scoreboard/ScoreboardUpdater.java | 2 +- .../geyser/session/cache/SkullCache.java | 4 +- .../geysermc/geyser/skin/SkinProvider.java | 6 +- .../geysermc/geyser/util/InventoryUtils.java | 2 +- 21 files changed, 245 insertions(+), 103 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 53df33acc57..35d572cbd84 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -142,7 +142,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { } initChannel.invoke(channelInitializer, ch); - if (bootstrap.config().useDirectConnection()) { + if (bootstrap.config().advanced().disableCompression()) { ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler", new GeyserBungeeCompressionDisabler()); } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java index 5346124fb2f..ccbc2a1e774 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java @@ -97,7 +97,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.config().disableCompression()) { + if (bootstrap.config().advanced().disableCompression()) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler()); } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index bb6295b6a84..a0ab1c83924 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -123,7 +123,7 @@ protected void initChannel(@NonNull Channel ch) throws Exception { int index = ch.pipeline().names().indexOf("encoder"); String baseName = index != -1 ? "encoder" : "outbound_config"; - if (bootstrap.config().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { + if (bootstrap.config().advanced().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler()); } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index d4756f5b5cc..232d67cdd08 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -287,7 +287,7 @@ public boolean testFloodgatePluginPresent() { @Override public Path getFloodgateKeyPath() { - return Path.of(geyserConfig.floodgateKeyFile()); + return Path.of(geyserConfig.advanced().floodgateKeyFile()); } /** diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java index caeaf15b579..7039706e5c8 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java @@ -85,7 +85,7 @@ protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws E protected void initChannel(@NonNull Channel ch) throws Exception { initChannel.invoke(channelInitializer, ch); - if (bootstrap.config().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { + if (bootstrap.config().advanced().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler", new GeyserVelocityCompressionDisabler()); } diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index ef6410497e2..4ab50dd8d7b 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -232,7 +232,7 @@ public boolean testFloodgatePluginPresent() { @Override public Path getFloodgateKeyPath() { - return new File(ROOT_FOLDER, config.floodgateKeyFile()).toPath(); + return new File(ROOT_FOLDER, config.advanced().floodgateKeyFile()).toPath(); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") diff --git a/core/src/main/java/org/geysermc/geyser/Constants.java b/core/src/main/java/org/geysermc/geyser/Constants.java index 6d397855f8a..6dcccd738f2 100644 --- a/core/src/main/java/org/geysermc/geyser/Constants.java +++ b/core/src/main/java/org/geysermc/geyser/Constants.java @@ -46,6 +46,7 @@ public final class Constants { public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/"; public static final int CONFIG_VERSION = 5; + public static final int ADVANCED_CONFIG_VERSION = 1; public static final int BSTATS_ID = 5273; diff --git a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java index abaa8e62618..cde2ff973d8 100644 --- a/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java +++ b/core/src/main/java/org/geysermc/geyser/FloodgateKeyLoader.java @@ -45,7 +45,7 @@ public static Path getKeyPath(GeyserConfig config, Path floodgateDataFolder, Pat } } - Path floodgateKey = geyserDataFolder.resolve(config.floodgateKeyFile()); + Path floodgateKey = geyserDataFolder.resolve(config.advanced().floodgateKeyFile()); if (!Files.exists(floodgateKey)) { logger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed")); diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index e87880028ce..1790901c63c 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -461,10 +461,10 @@ private void startInstance() { } } - if (config.metrics().enabled()) { + if (config.enableMetrics()) { metrics = new MetricsBase( "server-implementation", - config.metrics().uuid().toString(), + config.advanced().metricsUuid().toString(), Constants.BSTATS_ID, true, // Already checked above. builder -> { diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java new file mode 100644 index 00000000000..781f6ce6086 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024 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.configuration; + +import org.geysermc.geyser.Constants; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; +import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; + +import java.util.UUID; + +@ConfigSerializable +public interface AdvancedConfig { + // Cannot be type File yet because we may want to hide it in plugin instances. + @Comment(""" + Floodgate uses encryption to ensure use from authorized sources. + This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) + You can ignore this when not using Floodgate. + If you're using a plugin version of Floodgate on the same server, the key will automatically be picked up from Floodgate.""") + @DefaultString("key.pem") + String floodgateKeyFile(); + + @Comment(""" + The maximum number of custom skulls to be displayed per player. Increasing this may decrease performance on weaker devices. + Setting this to -1 will cause all custom skulls to be displayed regardless of distance or number.""") + @DefaultNumeric(128) + int maxVisibleCustomSkulls(); + + @Comment("The radius in blocks around the player in which custom skulls are displayed.") + @DefaultNumeric(32) + int customSkullRenderDistance(); + + @Comment(""" + Specify how many days images will be cached to disk to save downloading them from the internet. + A value of 0 is disabled. (Default: 0)""") + int cacheImages(); + + @Comment(""" + Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, + or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.""") + @DefaultString("minecraft:barrier") + String unusableSpaceBlock(); + + @Comment(""" + Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle + a lot of scoreboard packets per second can cause serious lag. + This option allows you to specify after how many Scoreboard packets per seconds + the Scoreboard updates will be limited to four updates per second.""") + @DefaultNumeric(20) + int scoreboardPacketThreshold(); + + @Comment(""" + The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation. + 1400 is the default.""") + @DefaultNumeric(1400) + int mtu(); + + @Comment(""" + Only for plugin versions of Geyser. + Whether to connect directly into the Java server without creating a TCP connection. + This should only be disabled if a plugin that interfaces with packets or the network does not work correctly with Geyser. + If enabled on plugin versions, the remote address and port sections are ignored + If disabled on plugin versions, expect performance decrease and latency increase + """) + @DefaultBoolean(true) + boolean useDirectConnection(); + + @Comment(""" + Only for plugin versions of Geyser. + Whether Geyser should attempt to disable compression for Bedrock players. This should be a benefit as there is no need to compress data + when Java packets aren't being handled over the network. + This requires use-direct-connection to be true. + """) + @DefaultBoolean(true) + boolean disableCompression(); + + @Comment("Do not touch!") + default UUID metricsUuid() { + return UUID.randomUUID(); + } + + @Comment("Do not touch!") + default int version() { + return Constants.ADVANCED_CONFIG_VERSION; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 99bb069ed92..996bac03c15 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -26,7 +26,10 @@ package org.geysermc.geyser.configuration; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.Constants; import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.NodePath; import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; import org.spongepowered.configurate.transformation.ConfigurationTransformation; import org.spongepowered.configurate.yaml.NodeStyle; @@ -34,8 +37,10 @@ import java.io.File; import java.io.IOException; +import java.util.List; import java.util.UUID; import java.util.function.Consumer; +import java.util.stream.Stream; import static org.spongepowered.configurate.NodePath.path; import static org.spongepowered.configurate.transformation.TransformAction.remove; @@ -56,20 +61,21 @@ public final class ConfigLoader { In most cases, especially with server hosting providers, further hosting-specific configuration is required. --------------------------------"""; + private static final String ADVANCED_HEADER = """ + -------------------------------- + Geyser ADVANCED Configuration File + + In most cases, you do *not* need to mess with this file to get Geyser running. + Tread with caution. + -------------------------------- + """; + public static T load(File file, Class configClass) throws IOException { return load(file, configClass, null); } public static T load(File file, Class configClass, @Nullable Consumer transformer) throws IOException { - var loader = YamlConfigurationLoader.builder() - .file(file) - .indent(2) - .nodeStyle(NodeStyle.BLOCK) - .defaultOptions(options -> InterfaceDefaultOptions.addTo(options) - .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. - .header(HEADER) - .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) - .build(); + var loader = createLoader(file, HEADER); CommentedConfigurationNode node = loader.load(); boolean originallyEmpty = !file.exists() || node.isNull(); @@ -111,10 +117,21 @@ public static T load(File file, Class configClass, @ .addAction(path("metrics", "uuid"), (path, value) -> { if ("generateduuid".equals(value.getString())) { // Manually copied config without Metrics UUID creation? - return new Object[]{UUID.randomUUID()}; + value.set(UUID.randomUUID()); + } + return null; + }) + .addAction(path("remote", "address"), (path, value) -> { + if ("auto".equals(value.getString())) { + // Auto-convert back to localhost + value.set("127.0.0.1"); } return null; }) + .addAction(path("metrics", "enabled"), (path, value) -> { + // Move to the root, not in the Metrics class. + return new Object[]{"enable-metrics"}; + }) .addAction(path("bedrock", "motd1"), rename("primary-motd")) .addAction(path("bedrock", "motd2"), rename("secondary-motd")) // Legacy config values @@ -139,6 +156,11 @@ public static T load(File file, Class configClass, @ CommentedConfigurationNode newRoot = CommentedConfigurationNode.root(loader.defaultOptions()); newRoot.set(config); + // Create the path in a way that Standalone changing the config name will be fine. + int extensionIndex = file.getName().lastIndexOf("."); + File advancedConfigPath = new File(file.getParent(), file.getName().substring(0, extensionIndex) + "_advanced" + file.getName().substring(extensionIndex)); + AdvancedConfig advancedConfig = null; + if (originallyEmpty || currentVersion != newVersion) { if (!originallyEmpty && currentVersion > 4) { @@ -148,10 +170,15 @@ public static T load(File file, Class configClass, @ // These get treated as comments on lower nodes, which produces very undesirable results. ConfigurationCommentMover.moveComments(node, newRoot); + } else if (currentVersion <= 4) { + advancedConfig = migrateToAdvancedConfig(advancedConfigPath, node); } loader.save(newRoot); } + if (advancedConfig == null) { + advancedConfig = loadAdvancedConfig(advancedConfigPath); + } if (transformer != null) { // We transform AFTER saving so that these specific transformations aren't applied to file. @@ -159,9 +186,69 @@ public static T load(File file, Class configClass, @ config = newRoot.get(configClass); } + config.advanced(advancedConfig); + return config; } + private static AdvancedConfig migrateToAdvancedConfig(File file, ConfigurationNode configRoot) throws IOException { + List copyFromOldConfig = Stream.of("max-visible-custom-skulls", "custom-skull-render-distance", "scoreboard-packet-threshold", "mtu", + "floodgate-key-file", "use-direct-connection", "disable-compression") + .map(NodePath::path).toList(); + + var loader = createLoader(file, ADVANCED_HEADER); + + CommentedConfigurationNode advancedNode = CommentedConfigurationNode.root(loader.defaultOptions()); + copyFromOldConfig.forEach(path -> { + ConfigurationNode node = configRoot.node(path); + if (!node.virtual()) { + advancedNode.node(path).mergeFrom(node); + configRoot.removeChild(path); + } + }); + + ConfigurationNode metricsUuid = configRoot.node("metrics", "uuid"); + if (!metricsUuid.virtual()) { + advancedNode.node("metrics-uuid").set(metricsUuid.get(UUID.class)); + } + + advancedNode.node("version").set(Constants.ADVANCED_CONFIG_VERSION); + + AdvancedConfig advancedConfig = advancedNode.get(AdvancedConfig.class); + // Ensure all fields get populated + CommentedConfigurationNode newNode = CommentedConfigurationNode.root(loader.defaultOptions()); + newNode.set(advancedConfig); + loader.save(newNode); + return advancedConfig; + } + + private static AdvancedConfig loadAdvancedConfig(File file) throws IOException { + var loader = createLoader(file, ADVANCED_HEADER); + if (file.exists()) { + ConfigurationNode node = loader.load(); + return node.get(AdvancedConfig.class); + } else { + ConfigurationNode node = CommentedConfigurationNode.root(loader.defaultOptions()); + node.node("version").set(Constants.ADVANCED_CONFIG_VERSION); + AdvancedConfig advancedConfig = node.get(AdvancedConfig.class); + node.set(advancedConfig); + loader.save(node); + return advancedConfig; + } + } + + private static YamlConfigurationLoader createLoader(File file, String header) { + return YamlConfigurationLoader.builder() + .file(file) + .indent(2) + .nodeStyle(NodeStyle.BLOCK) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options) + .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. + .header(header) + .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) + .build(); + } + private ConfigLoader() { } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 6828200eb1c..de254b240a0 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -33,6 +33,7 @@ import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.util.CooldownUtils; import org.spongepowered.configurate.interfaces.meta.Exclude; +import org.spongepowered.configurate.interfaces.meta.Field; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; @@ -43,7 +44,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; @ConfigSerializable public interface GeyserConfig { @@ -51,10 +51,6 @@ public interface GeyserConfig { JavaConfig java(); - // Cannot be type File yet because we want to hide it in plugin instances. - @DefaultString("key.pem") - String floodgateKeyFile(); - @Comment(""" For online mode authentication type only. Stores a list of Bedrock players that should have their Java Edition account saved after login. @@ -128,25 +124,10 @@ default CooldownUtils.CooldownType showCooldown() { @DefaultString("system") String defaultLocale(); - @Comment(""" - Specify how many days images will be cached to disk to save downloading them from the internet. - A value of 0 is disabled. (Default: 0)""") - int cacheImages(); - @Comment("Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices.") @DefaultBoolean(true) boolean allowCustomSkulls(); - @Comment(""" - The maximum number of custom skulls to be displayed per player. Increasing this may decrease performance on weaker devices. - Setting this to -1 will cause all custom skulls to be displayed regardless of distance or number.""") - @DefaultNumeric(128) - int maxVisibleCustomSkulls(); - - @Comment("The radius in blocks around the player in which custom skulls are displayed.") - @DefaultNumeric(32) - int customSkullRenderDistance(); - @Comment(""" Whether to add any items and blocks which normally does not exist in Bedrock Edition. This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching. @@ -186,18 +167,23 @@ default CooldownUtils.CooldownType showCooldown() { @DefaultBoolean(true) boolean notifyOnNewBedrockUpdate(); - @Comment(""" - Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, - or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.""") - @DefaultString("minecraft:barrier") - String unusableSpaceBlock(); - @Comment(""" bStats is a stat tracker that is entirely anonymous and tracks only basic information about Geyser, such as how many people are online, how many servers are using Geyser, what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. https://bstats.org/plugin/server-implementation/GeyserMC""") - MetricsInfo metrics(); + @DefaultBoolean(true) + boolean enableMetrics(); + + /** + * A separate config file added to this class manually. + */ + @Field + @NonNull + AdvancedConfig advanced(); + + @Field + void advanced(AdvancedConfig config); @ConfigSerializable interface BedrockConfig extends BedrockListener { @@ -315,39 +301,14 @@ default boolean resolveSrv() { } } - @ConfigSerializable - interface MetricsInfo { - @Comment("If metrics should be enabled") - @DefaultBoolean(true) - boolean enabled(); - - @Comment("UUID of server. Don't change!") - default UUID uuid() { //TODO rename? - return UUID.randomUUID(); - } - } - - @Comment(""" - Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle - a lot of scoreboard packets per second can cause serious lag. - This option allows you to specify after how many Scoreboard packets per seconds - the Scoreboard updates will be limited to four updates per second.""") - @DefaultNumeric(20) - int scoreboardPacketThreshold(); - @Comment(""" Allow connections from ProxyPass and Waterdog. See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP.""") // if u have offline mode enabled pls be safe boolean enableProxyConnections(); - @Comment(""" - The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation. - 1400 is the default.""") - @DefaultNumeric(1400) - int mtu(); - @Comment("Do not change!") + @SuppressWarnings("unused") default int configVersion() { return Constants.CONFIG_VERSION; } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index 2ae6214e056..b5076752a68 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -27,7 +27,6 @@ import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.Field; -import org.spongepowered.configurate.interfaces.meta.Hidden; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; @@ -72,10 +71,6 @@ default boolean forwardHostname() { } } - @Override - @Hidden - String floodgateKeyFile(); - @Comment(""" Use server API methods to determine the Java server's MOTD and ping passthrough. There is no need to disable this unless your MOTD or player count does not appear properly.""") @@ -87,12 +82,4 @@ default boolean forwardHostname() { Only relevant if integrated-ping-passthrough is disabled.""") @Override int pingPassthroughInterval(); - - @Hidden - @DefaultBoolean(true) - boolean useDirectConnection(); - - @Hidden - @DefaultBoolean(true) - boolean disableCompression(); } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index fa3e53aec80..5c48c6b2f76 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -39,14 +39,6 @@ public interface GeyserRemoteConfig extends GeyserConfig { @Override RemoteConfig java(); - @Override - @Comment(""" - Floodgate uses encryption to ensure use from authorized sources. - This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) - You can ignore this when not using Floodgate. - If you're using a plugin version of Floodgate on the same server, the key will automatically be picked up from Floodgate.""") - String floodgateKeyFile(); - @ConfigSerializable interface RemoteConfig extends JavaConfig { @Override diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 69b35398d43..489d9d86a9e 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -39,6 +39,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.configuration.AdvancedConfig; import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; @@ -73,6 +74,7 @@ public class DumpInfo { private final String systemEncoding; private final GitInfo gitInfo; private final GeyserConfig config; + private final AdvancedConfig advancedConfig; private final Object2IntMap userPlatforms; private final int connectionAttempts; private final HashInfo hashInfo; @@ -93,6 +95,7 @@ public DumpInfo(GeyserImpl geyser, boolean addLog) { this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); this.config = geyser.config(); + this.advancedConfig = geyser.config().advanced(); String md5Hash = "unknown"; String sha256Hash = "unknown"; diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java index 2340e021fad..f19f159ba58 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java @@ -50,7 +50,7 @@ public abstract class GeyserInjector { * @param bootstrap the bootstrap of the Geyser instance. */ public void initializeLocalChannel(GeyserPluginBootstrap bootstrap) { - if (!bootstrap.config().useDirectConnection()) { + if (!bootstrap.config().advanced().useDirectConnection()) { bootstrap.getGeyserLogger().debug("Disabling direct injection!"); return; } diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java index db90c3ef7bd..a82ca5f612c 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java @@ -224,7 +224,7 @@ private ServerBootstrap createBootstrap() { GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser); playerGroup = serverInitializer.getEventLoopGroup(); - this.geyser.getLogger().debug("Setting MTU to " + this.geyser.config().mtu()); + this.geyser.getLogger().debug("Setting MTU to " + this.geyser.config().advanced().mtu()); int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT); this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit); @@ -239,7 +239,7 @@ private ServerBootstrap createBootstrap() { .channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel())) .group(group, childGroup) .option(RakChannelOption.RAK_HANDLE_PING, true) - .option(RakChannelOption.RAK_MAX_MTU, this.geyser.config().mtu()) + .option(RakChannelOption.RAK_MAX_MTU, this.geyser.config().advanced().mtu()) .option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) .option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit) .option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie) diff --git a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java index 6c9f8a6957e..72c9def8771 100644 --- a/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java +++ b/core/src/main/java/org/geysermc/geyser/scoreboard/ScoreboardUpdater.java @@ -47,7 +47,7 @@ public final class ScoreboardUpdater extends Thread { static { GeyserConfig config = GeyserImpl.getInstance().config(); - FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); + FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.advanced().scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); DEBUG_ENABLED = config.debugMode(); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java index 8cccfd27fe6..46160545ed2 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java @@ -74,11 +74,11 @@ public class SkullCache { public SkullCache(GeyserSession session) { this.session = session; - this.maxVisibleSkulls = session.getGeyser().config().maxVisibleCustomSkulls(); + this.maxVisibleSkulls = session.getGeyser().config().advanced().maxVisibleCustomSkulls(); this.cullingEnabled = this.maxVisibleSkulls != -1; // Normal skulls are not rendered beyond 64 blocks - int distance = Math.min(session.getGeyser().config().customSkullRenderDistance(), 64); + int distance = Math.min(session.getGeyser().config().advanced().customSkullRenderDistance(), 64); this.skullRenderDistanceSquared = distance * distance; } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java index 6efa4ef6c21..b7b9b732dd8 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinProvider.java @@ -144,7 +144,7 @@ public static void shutdown() { public static void registerCacheImageTask(GeyserImpl geyser) { // Schedule Daily Image Expiry if we are caching them - if (geyser.config().cacheImages() > 0) { + if (geyser.config().advanced().cacheImages() > 0) { geyser.getScheduledThread().scheduleAtFixedRate(() -> { File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile(); if (!cacheFolder.exists()) { @@ -152,7 +152,7 @@ public static void registerCacheImageTask(GeyserImpl geyser) { } int count = 0; - final long expireTime = ((long) GeyserImpl.getInstance().config().cacheImages()) * ((long)1000 * 60 * 60 * 24); + final long expireTime = ((long) GeyserImpl.getInstance().config().advanced().cacheImages()) * ((long)1000 * 60 * 60 * 24); for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) { if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) { //noinspection ResultOfMethodCallIgnored @@ -422,7 +422,7 @@ public static BufferedImage requestImage(String imageUrl, boolean isCape) throws GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl); // Write to cache if we are allowed - if (GeyserImpl.getInstance().config().cacheImages() > 0) { + if (GeyserImpl.getInstance().config().advanced().cacheImages() > 0) { imageFile.getParentFile().mkdirs(); try { ImageIO.write(image, "png", imageFile); diff --git a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java index 13257a905f5..260f9c8356a 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/InventoryUtils.java @@ -236,7 +236,7 @@ public static IntFunction createUnusableSpaceBlock(String description) private static ItemDefinition getUnusableSpaceBlockDefinition(int protocolVersion) { ItemMappings mappings = Registries.ITEMS.forVersion(protocolVersion); - String unusableSpaceBlock = GeyserImpl.getInstance().config().unusableSpaceBlock(); + String unusableSpaceBlock = GeyserImpl.getInstance().config().advanced().unusableSpaceBlock(); ItemDefinition itemDefinition = mappings.getDefinition(unusableSpaceBlock); if (itemDefinition == null) { From 5870856ef77f80ebfc3512a1630c9029581b7089 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 29 Aug 2024 14:39:55 -0400 Subject: [PATCH 20/42] Remove Jackson; finish config value placements --- core/build.gradle.kts | 3 +-- .../java/org/geysermc/geyser/GeyserImpl.java | 7 ++++--- .../defaults/ConnectionTestCommand.java | 2 +- .../standalone/PermissionConfiguration.java | 3 +-- .../geyser/configuration/GeyserConfig.java | 17 ++++++++-------- .../configuration/GeyserPluginConfig.java | 13 +----------- .../configuration/GeyserRemoteConfig.java | 20 +++++++++++++++++++ gradle/libs.versions.toml | 7 ++----- 8 files changed, 38 insertions(+), 34 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 7b0f5225cdd..c04ad6fe26a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -13,8 +13,7 @@ dependencies { api(projects.common) api(projects.api) - // Jackson JSON and YAML serialization - api(libs.bundles.jackson) + api(libs.yaml) // Used for extensions annotationProcessor(libs.configurate.`interface`.ap) api(libs.configurate.`interface`) implementation(libs.configurate.yaml) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 1790901c63c..6002b1549ca 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -73,6 +73,7 @@ import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.command.CommandRegistry; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.erosion.UnixSocketClientListener; import org.geysermc.geyser.event.GeyserEventBus; @@ -322,12 +323,12 @@ private void startInstance() { config.java().address(InetAddress.getLoopbackAddress().getHostAddress()); } } - if (javaPort != -1 && config.asPluginConfig().isEmpty()) { + if (javaPort != -1) { config.java().port(javaPort); } boolean forceMatchServerPort = "server".equals(pluginUdpPort); - if ((config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false) || forceMatchServerPort) && javaPort != -1) { + if ((config.bedrock().cloneRemotePort() || forceMatchServerPort) && javaPort != -1) { config.bedrock().port(javaPort); if (forceMatchServerPort) { if (geyserUdpPort.isEmpty()) { @@ -388,7 +389,7 @@ private void startInstance() { } } - if (config.asPluginConfig().isEmpty()) { + if (!(config instanceof GeyserPluginConfig)) { String remoteAddress = config.java().address(); // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index 0a720694ac8..b416b04e689 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -115,7 +115,7 @@ public void execute(CommandContext context) { source.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration (" + config.bedrock().port() + ")"); source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config."); - if (config.asPluginConfig().map(plugin -> plugin.bedrock().cloneRemotePort()).orElse(false)) { + if (config.bedrock().cloneRemotePort()) { source.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead."); } } else { diff --git a/core/src/main/java/org/geysermc/geyser/command/standalone/PermissionConfiguration.java b/core/src/main/java/org/geysermc/geyser/command/standalone/PermissionConfiguration.java index 8c93a2f53be..a8e1ee9dd78 100644 --- a/core/src/main/java/org/geysermc/geyser/command/standalone/PermissionConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/command/standalone/PermissionConfiguration.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.command.standalone; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.spongepowered.configurate.objectmapping.ConfigSerializable; @@ -34,8 +33,8 @@ @Getter @ConfigSerializable +@SuppressWarnings("FieldMayBeFinal") public class PermissionConfiguration { - @JsonProperty("default-permissions") private Set defaultPermissions = Collections.emptySet(); } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index de254b240a0..cbc2f6c109a 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -43,7 +43,6 @@ import java.util.Collections; import java.util.List; -import java.util.Optional; @ConfigSerializable public interface GeyserConfig { @@ -84,6 +83,8 @@ default List savedUserLogins() { @DefaultBoolean(true) boolean passthroughPlayerCounts(); + boolean integratedPingPassthrough(); + @Comment("How often to ping the Java server to refresh MOTD and player count, in seconds.") @DefaultNumeric(3) int pingPassthroughInterval(); @@ -209,6 +210,12 @@ interface BedrockConfig extends BedrockListener { @NumericRange(from = 0, to = 65535) int broadcastPort(); + @Comment(""" + Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. + This option makes the Bedrock port the same as the Java port every time you start the server.""") + @DefaultBoolean + boolean cloneRemotePort(); + void address(String address); void port(int port); @@ -312,12 +319,4 @@ default boolean resolveSrv() { default int configVersion() { return Constants.CONFIG_VERSION; } - - @Exclude - default Optional asPluginConfig() { - if (this instanceof GeyserPluginConfig config) { - return Optional.of(config); - } - return Optional.empty(); - } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index b5076752a68..19bcd50128c 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -33,21 +33,9 @@ @ConfigSerializable public interface GeyserPluginConfig extends GeyserConfig { - @Override - IntegratedBedrockConfig bedrock(); - @Override IntegratedJavaConfig java(); - @ConfigSerializable - interface IntegratedBedrockConfig extends BedrockConfig { - @Comment(""" - Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. - This option makes the Bedrock port the same as the Java port every time you start the server.""") - @DefaultBoolean - boolean cloneRemotePort(); - } - @ConfigSerializable interface IntegratedJavaConfig extends JavaConfig { @Override @@ -75,6 +63,7 @@ default boolean forwardHostname() { Use server API methods to determine the Java server's MOTD and ping passthrough. There is no need to disable this unless your MOTD or player count does not appear properly.""") @DefaultBoolean(true) + @Override boolean integratedPingPassthrough(); @Comment(""" diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index 5c48c6b2f76..3baa5a5cb95 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.configuration; +import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; import org.spongepowered.configurate.interfaces.meta.range.NumericRange; @@ -36,9 +37,21 @@ */ @ConfigSerializable public interface GeyserRemoteConfig extends GeyserConfig { + @Override + RemoteBedrock bedrock(); + @Override RemoteConfig java(); + @ConfigSerializable + interface RemoteBedrock extends BedrockConfig { + @Override + @Exclude // We can bring this back if there's a use-case but it's not really justified here. + default boolean cloneRemotePort() { + return false; + } + } + @ConfigSerializable interface RemoteConfig extends JavaConfig { @Override @@ -58,4 +71,11 @@ interface RemoteConfig extends JavaConfig { This is designed to be used for forced hosts on proxies""") boolean forwardHostname(); } + + @Exclude + @Override + default boolean integratedPingPassthrough() { + // Does nothing here. + return false; + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b7c56f4d6ae..41c13da6175 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ cumulus = "1.1.2" configurate = "4.2.0-GeyserMC-SNAPSHOT" erosion = "1.1-20240521.000109-3" events = "1.1-SNAPSHOT" -jackson = "2.17.0" +yaml = "2.2" fastutil = "8.5.2" netty = "4.1.107.Final" netty-io-uring = "0.0.25.Final-SNAPSHOT" @@ -59,9 +59,7 @@ erosion-bukkit-common = { group = "org.geysermc.erosion", name = "bukkit-common" erosion-bukkit-nms = { group = "org.geysermc.erosion", name = "bukkit-nms", version.ref = "erosion" } erosion-common = { group = "org.geysermc.erosion", name = "common", version.ref = "erosion" } -jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" } -jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } -jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" } +yaml = { module = "org.yaml:snakeyaml", version.ref = "yaml" } configurate-interface-ap = { module = "org.spongepowered:configurate-extra-interface-ap", version.ref = "configurate" } configurate-interface = { module = "org.spongepowered:configurate-extra-interface", version.ref = "configurate" } @@ -155,7 +153,6 @@ indra = { id = "net.kyori.indra", version.ref = "indra" } blossom = { id = "net.kyori.blossom", version.ref = "blossom" } [bundles] -jackson = [ "jackson-annotations", "jackson-core", "jackson-dataformat-yaml" ] fastutil = [ "fastutil-int-int-maps", "fastutil-int-long-maps", "fastutil-int-byte-maps", "fastutil-int-boolean-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ] adventure = [ "adventure-text-serializer-gson", "adventure-text-serializer-legacy", "adventure-text-serializer-plain" ] log4j = [ "log4j-api", "log4j-core", "log4j-slf4j2-impl" ] From f40cbca9006e2bb4bc0c3b104ac84c88a644f4ef Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:46:26 -0400 Subject: [PATCH 21/42] Remove duplicate relocate declarations --- bootstrap/bungeecord/build.gradle.kts | 3 +-- bootstrap/spigot/build.gradle.kts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts index 903752cb4da..e2021b855b3 100644 --- a/bootstrap/bungeecord/build.gradle.kts +++ b/bootstrap/bungeecord/build.gradle.kts @@ -10,10 +10,9 @@ platformRelocate("net.md_5.bungee.jni") platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound platformRelocate("net.kyori") platformRelocate("org.incendo") -platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated +platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated platformRelocate("org.yaml") // Broken as of 1.20 platformRelocate("org.spongepowered") -platformRelocate("io.leangen.geantyref") platformRelocate("org.bstats") // These dependencies are already present on the platform diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index fb682038500..a21defe2cc2 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -38,10 +38,9 @@ platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLo platformRelocate("org.objectweb.asm") platformRelocate("me.lucko.commodore") platformRelocate("org.incendo") -platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated +platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated platformRelocate("org.yaml") // Broken as of 1.20 platformRelocate("org.spongepowered") -platformRelocate("io.leangen.geantyref") platformRelocate("marcono1234.gson") platformRelocate("org.bstats") From a6c21b1f00e94f84b1ad689458f2ba4648f562a7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:07:18 -0400 Subject: [PATCH 22/42] Let annotations work --- .../bungeecord/GeyserBungeePlugin.java | 28 ++--- .../platform/mod/GeyserModBootstrap.java | 27 ++--- .../platform/spigot/GeyserSpigotPlugin.java | 24 ++--- .../standalone/GeyserStandaloneBootstrap.java | 21 ++-- .../velocity/GeyserVelocityPlugin.java | 31 ++---- .../viaproxy/GeyserViaProxyPlugin.java | 26 +++-- .../org/geysermc/geyser/GeyserBootstrap.java | 9 ++ .../java/org/geysermc/geyser/GeyserImpl.java | 20 ++-- .../geyser/command/CommandRegistry.java | 2 +- .../command/defaults/VersionCommand.java | 2 +- .../geyser/configuration/AdvancedConfig.java | 8 +- .../geyser/configuration/ConfigLoader.java | 100 +++++++++++++++--- .../geyser/configuration/ExcludePlatform.java | 37 +++++++ .../geyser/configuration/GeyserConfig.java | 6 ++ .../configuration/GeyserPluginConfig.java | 8 -- .../configuration/GeyserRemoteConfig.java | 20 ---- .../configuration/PlatformTypeSpecific.java | 37 +++++++ .../geyser/dump/BootstrapDumpInfo.java | 2 +- .../geyser/session/GeyserSession.java | 4 +- .../BedrockCommandRequestTranslator.java | 4 +- .../java/JavaLoginDisconnectTranslator.java | 2 +- .../java/level/JavaBlockEventTranslator.java | 2 +- .../org/geysermc/geyser/util/WebUtils.java | 2 +- 23 files changed, 262 insertions(+), 160 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/ExcludePlatform.java create mode 100644 core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index f07f0d7f05c..d8338a2ce6f 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -37,11 +37,11 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.util.PlatformType; -import org.geysermc.geyser.configuration.ConfigLoader; -import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandSourceConverter; import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.configuration.ConfigLoader; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -51,8 +51,6 @@ import org.incendo.cloud.bungee.BungeeCommandManager; import org.incendo.cloud.execution.ExecutionCoordinator; -import java.io.File; -import java.io.IOException; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -97,8 +95,7 @@ public void onGeyserInitialize() { if (!this.loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); - this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this); + this.geyser = GeyserImpl.load(this); this.geyserInjector = new GeyserBungeeInjector(this); // Registration of listeners occurs only once @@ -162,7 +159,6 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); } // Force-disable query if enabled, or else Geyser won't enable @@ -221,6 +217,11 @@ public void onDisable() { this.onGeyserShutdown(); } + @Override + public PlatformType platformType() { + return PlatformType.BUNGEECORD; + } + @Override public GeyserPluginConfig config() { return this.geyserConfig; @@ -296,16 +297,7 @@ private Optional findCompatibleListener() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { - try { - if (!getDataFolder().exists()) { - //noinspection ResultOfMethodCallIgnored - getDataFolder().mkdir(); - } - this.geyserConfig = ConfigLoader.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); - } catch (IOException ex) { - geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - return false; - } - return true; + this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); + return this.geyserConfig != null; } } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java index e81fff860a0..75bae5acde5 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java @@ -35,9 +35,10 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserPluginBootstrap; +import org.geysermc.geyser.api.util.PlatformType; +import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserPluginConfig; -import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -46,7 +47,6 @@ import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager; import org.geysermc.geyser.text.GeyserLocale; -import java.io.IOException; import java.io.InputStream; import java.net.SocketAddress; import java.nio.file.Path; @@ -82,8 +82,7 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); - this.geyser = GeyserImpl.load(this.platform.platformType(), this); + this.geyser = GeyserImpl.load(this); } public void onGeyserEnable() { @@ -96,7 +95,6 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); } GeyserImpl.start(); @@ -141,6 +139,11 @@ public void onGeyserShutdown() { } } + @Override + public PlatformType platformType() { + return this.platform.platformType(); + } + @Override public GeyserPluginConfig config() { return geyserConfig; @@ -226,17 +229,7 @@ public InputStream getResourceOrNull(String resource) { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { - try { - if (!dataFolder.toFile().exists()) { - //noinspection ResultOfMethodCallIgnored - dataFolder.toFile().mkdir(); - } - - this.geyserConfig = ConfigLoader.load(dataFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); - return true; - } catch (IOException ex) { - geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - return false; - } + this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); + return this.geyserConfig != null; } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 1e9262bc023..9eafd510033 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -71,8 +71,6 @@ import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.paper.LegacyPaperCommandManager; -import java.io.File; -import java.io.IOException; import java.net.SocketAddress; import java.nio.file.Path; import java.util.List; @@ -165,12 +163,11 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); // Turn "(MC: 1.16.4)" into 1.16.4. this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0]; - this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this); + this.geyser = GeyserImpl.load(this); } @Override @@ -229,7 +226,6 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(this.geyserConfig.debugMode()); } GeyserImpl.start(); @@ -361,6 +357,11 @@ public void onDisable() { this.onGeyserShutdown(); } + @Override + public PlatformType platformType() { + return PlatformType.SPIGOT; + } + @Override public GeyserPluginConfig config() { return this.geyserConfig; @@ -463,20 +464,11 @@ public Path getFloodgateKeyPath() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { - // This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed - try { - if (!getDataFolder().exists()) { - //noinspection ResultOfMethodCallIgnored - getDataFolder().mkdir(); - } - this.geyserConfig = ConfigLoader.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class); - } catch (IOException ex) { - geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); + this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); + if (this.geyserConfig == null) { Bukkit.getPluginManager().disablePlugin(this); return false; } - return true; } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index 232d67cdd08..a3a17bb9963 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -37,11 +37,11 @@ import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; +import org.geysermc.geyser.command.CommandRegistry; +import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager; import org.geysermc.geyser.configuration.ConfigLoader; import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.configuration.GeyserRemoteConfig; -import org.geysermc.geyser.command.CommandRegistry; -import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -53,7 +53,6 @@ import org.spongepowered.configurate.serialize.SerializationException; import java.io.File; -import java.io.IOException; import java.lang.reflect.Method; import java.nio.file.Path; import java.nio.file.Paths; @@ -161,10 +160,10 @@ public void onGeyserInitialize() { @Override public void onGeyserEnable() { - try { - geyserConfig = ConfigLoader.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions); - } catch (IOException ex) { - geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); + this.geyserConfig = new ConfigLoader(this) + .transformer(this::handleArgsConfigOptions) + .load(GeyserRemoteConfig.class); + if (this.geyserConfig == null) { if (gui == null) { System.exit(1); } else { @@ -172,12 +171,11 @@ public void onGeyserEnable() { return; } } - geyserLogger.setDebug(geyserConfig.debugMode()); // Allow libraries like Protocol to have their debug information passthrough log4jLogger.get().setLevel(geyserConfig.debugMode() ? Level.DEBUG : Level.INFO); - geyser = GeyserImpl.load(PlatformType.STANDALONE, this); + geyser = GeyserImpl.load(this); boolean reloading = geyser.isReloading(); if (!reloading) { @@ -232,6 +230,11 @@ public void onGeyserShutdown() { System.exit(0); } + @Override + public PlatformType platformType() { + return PlatformType.STANDALONE; + } + @Override public GeyserConfig config() { return this.geyserConfig; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 3f1d6b6a775..1b8674871d7 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -42,15 +42,12 @@ import org.geysermc.geyser.FloodgateKeyLoader; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserPluginBootstrap; -import org.geysermc.geyser.api.command.Command; -import org.geysermc.geyser.api.extension.Extension; -import org.geysermc.geyser.api.util.PlatformType; -import org.geysermc.geyser.configuration.ConfigLoader; -import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandSourceConverter; import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.configuration.ConfigLoader; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; @@ -62,7 +59,6 @@ import org.incendo.cloud.velocity.VelocityCommandManager; import org.slf4j.Logger; -import java.io.IOException; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; @@ -107,9 +103,8 @@ public void onGeyserInitialize() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); - this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); + this.geyser = GeyserImpl.load(this); this.geyserInjector = new GeyserVelocityInjector(proxyServer); // We need to register commands here, rather than in onGeyserEnable which is invoked during the appropriate ListenerBoundEvent. @@ -139,7 +134,6 @@ public void onGeyserEnable() { if (!loadConfig()) { return; } - this.geyserLogger.setDebug(geyserConfig.debugMode()); } GeyserImpl.start(); @@ -173,6 +167,11 @@ public void onGeyserShutdown() { } } + @Override + public PlatformType platformType() { + return PlatformType.VELOCITY; + } + @Override public GeyserPluginConfig config() { return geyserConfig; @@ -253,17 +252,7 @@ public Path getFloodgateKeyPath() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { - try { - if (!configFolder.toFile().exists()) { - //noinspection ResultOfMethodCallIgnored - configFolder.toFile().mkdirs(); - } - this.geyserConfig = ConfigLoader.load(configFolder.resolve("config.yml").toFile(), GeyserPluginConfig.class); - } catch (IOException ex) { - geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); - ex.printStackTrace(); - return false; - } - return true; + this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); + return this.geyserConfig != null; } } diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index 4ab50dd8d7b..56157c87faf 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -42,10 +42,10 @@ import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.util.PlatformType; -import org.geysermc.geyser.configuration.ConfigLoader; -import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager; +import org.geysermc.geyser.configuration.ConfigLoader; +import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough; @@ -56,7 +56,6 @@ import org.spongepowered.configurate.serialize.SerializationException; import java.io.File; -import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; @@ -127,7 +126,7 @@ public void onGeyserInitialize() { return; } - this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this); + this.geyser = GeyserImpl.load(this); this.geyser.eventBus().register(this, new GeyserServerTransferListener()); LoopbackUtil.checkAndApplyLoopback(this.logger); } @@ -176,6 +175,11 @@ public void onGeyserShutdown() { this.geyser.shutdown(); } + @Override + public PlatformType platformType() { + return PlatformType.VIAPROXY; + } + @Override public GeyserPluginConfig config() { return this.config; @@ -237,10 +241,10 @@ public Path getFloodgateKeyPath() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { - try { - this.config = ConfigLoader.load(new File(ROOT_FOLDER, "config.yml"), GeyserPluginConfig.class, node -> { + this.config = new ConfigLoader(this) + .transformer(node -> { try { - if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) { // TODO + if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) { node.node("java", "forward-host").set(true); } @@ -253,13 +257,13 @@ private boolean loadConfig() { } catch (SerializationException e) { throw new RuntimeException(e); } - }); - } catch (IOException e) { - this.logger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); + }) + .configFile(new File(ROOT_FOLDER, "config.yml")) + .load(GeyserPluginConfig.class); + if (this.config == null) { return false; } this.config.java().authType(Files.isRegularFile(getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE); - this.logger.setDebug(this.config.debugMode()); return true; } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index cb5cd40f495..121a43cc177 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.dump.BootstrapDumpInfo; @@ -67,6 +68,14 @@ public interface GeyserBootstrap { */ void onGeyserShutdown(); + /** + * Returns the platform type this Geyser instance is running on. + * + * @return The current PlatformType + */ + @NonNull + PlatformType platformType(); + /** * Returns the current GeyserConfig * diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 2b96cdadcab..1f93a42a39b 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -167,7 +167,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { private ScheduledExecutorService scheduledThread; private GeyserServer geyserServer; - private final PlatformType platformType; private final GeyserBootstrap bootstrap; private final EventBus eventBus; @@ -193,12 +192,11 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { @Setter private boolean isEnabled; - private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) { + private GeyserImpl(GeyserBootstrap bootstrap) { instance = this; Geyser.set(this); - this.platformType = platformType; this.bootstrap = bootstrap; /* Initialize event bus */ @@ -270,7 +268,7 @@ public void initialize() { message += " " + GeyserLocale.getLocaleStringLog("geyser.core.finish.console"); logger.info(message); - if (platformType == PlatformType.STANDALONE) { + if (platformType() == PlatformType.STANDALONE) { if (config.java().authType() != AuthType.FLOODGATE) { // If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); @@ -306,7 +304,7 @@ private void startInstance() { boolean portPropertyApplied = false; String pluginUdpAddress = System.getProperty("geyserUdpAddress", System.getProperty("pluginUdpAddress", "")); - if (platformType != PlatformType.STANDALONE) { + if (platformType() != PlatformType.STANDALONE) { int javaPort = bootstrap.getServerPort(); String serverAddress = bootstrap.getServerBindAddress(); if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) { @@ -360,7 +358,7 @@ private void startInstance() { } - if (platformType != PlatformType.VIAPROXY) { + if (platformType() != PlatformType.VIAPROXY) { boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); if (config.java().authType() == AuthType.FLOODGATE && !floodgatePresent) { logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " @@ -496,7 +494,7 @@ private void startInstance() { metrics.addCustomChart(new SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT))); - metrics.addCustomChart(new SimplePie("platform", platformType::platformName)); + metrics.addCustomChart(new SimplePie("platform", platformType()::platformName)); metrics.addCustomChart(new SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); metrics.addCustomChart(new SimplePie("version", () -> GeyserImpl.VERSION)); metrics.addCustomChart(new AdvancedPie("playerPlatform", () -> { @@ -532,7 +530,7 @@ private void startInstance() { if (minecraftVersion != null) { Map> versionMap = new HashMap<>(); Map platformMap = new HashMap<>(); - platformMap.put(platformType.platformName(), 1); + platformMap.put(platformType().platformName(), 1); versionMap.put(minecraftVersion, platformMap); metrics.addCustomChart(new DrilldownPie("minecraftServerVersion", () -> { @@ -840,7 +838,7 @@ public Path packDirectory() { @Override @NonNull public PlatformType platformType() { - return platformType; + return bootstrap.platformType(); } @Override @@ -871,9 +869,9 @@ public int buildNumber() { return Integer.parseInt(BUILD_NUMBER); } - public static GeyserImpl load(PlatformType platformType, GeyserBootstrap bootstrap) { + public static GeyserImpl load(GeyserBootstrap bootstrap) { if (instance == null) { - return new GeyserImpl(platformType, bootstrap); + return new GeyserImpl(bootstrap); } return instance; diff --git a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java index 54681abea31..1618932c2f6 100644 --- a/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/command/CommandRegistry.java @@ -141,7 +141,7 @@ public CommandRegistry(GeyserImpl geyser, CommandManager cl registerBuiltInCommand(new AdvancedTooltipsCommand("tooltips", "geyser.commands.advancedtooltips.desc", "geyser.command.tooltips")); registerBuiltInCommand(new ConnectionTestCommand(geyser, "connectiontest", "geyser.commands.connectiontest.desc", "geyser.command.connectiontest")); registerBuiltInCommand(new PingCommand("ping", "geyser.commands.ping.desc", "geyser.command.ping")); - if (this.geyser.getPlatformType() == PlatformType.STANDALONE) { + if (this.geyser.platformType() == PlatformType.STANDALONE) { registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop")); } diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java index 0bbfa714492..0f25377600c 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/VersionCommand.java @@ -73,7 +73,7 @@ public void execute(CommandContext context) { GeyserImpl.NAME, GeyserImpl.VERSION, javaVersions, bedrockVersions)); // Disable update checking in dev mode and for players in Geyser Standalone - if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) { + if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.platformType() == PlatformType.STANDALONE)) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index 781f6ce6086..1d64f3c6a20 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -81,22 +81,22 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o int mtu(); @Comment(""" - Only for plugin versions of Geyser. Whether to connect directly into the Java server without creating a TCP connection. This should only be disabled if a plugin that interfaces with packets or the network does not work correctly with Geyser. - If enabled on plugin versions, the remote address and port sections are ignored - If disabled on plugin versions, expect performance decrease and latency increase + If enabled, the remote address and port sections are ignored + If disabled, expect performance decrease and latency increase """) @DefaultBoolean(true) + @PlatformTypeSpecific boolean useDirectConnection(); @Comment(""" - Only for plugin versions of Geyser. Whether Geyser should attempt to disable compression for Bedrock players. This should be a benefit as there is no need to compress data when Java packets aren't being handled over the network. This requires use-direct-connection to be true. """) @DefaultBoolean(true) + @PlatformTypeSpecific boolean disableCompression(); @Comment("Do not touch!") diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 996bac03c15..dcda2fe0b89 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -26,18 +26,23 @@ package org.geysermc.geyser.configuration; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.Constants; +import org.geysermc.geyser.GeyserBootstrap; +import org.geysermc.geyser.api.util.PlatformType; +import org.geysermc.geyser.text.GeyserLocale; import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.NodePath; import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; +import org.spongepowered.configurate.objectmapping.meta.Processor; import org.spongepowered.configurate.transformation.ConfigurationTransformation; import org.spongepowered.configurate.yaml.NodeStyle; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.File; import java.io.IOException; -import java.util.List; +import java.nio.file.Path; import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Stream; @@ -70,15 +75,58 @@ public final class ConfigLoader { -------------------------------- """; - public static T load(File file, Class configClass) throws IOException { - return load(file, configClass, null); + private final GeyserBootstrap bootstrap; + private @Nullable Consumer transformer; + private File configFile; + + public ConfigLoader(GeyserBootstrap bootstrap) { + this.bootstrap = bootstrap; + configFile = new File(bootstrap.getConfigFolder().toFile(), "config.yml"); + } + + /** + * Creates the directory as indicated by {@link GeyserBootstrap#getConfigFolder()} + */ + @This + public ConfigLoader createFolder() { + Path dataFolder = this.bootstrap.getConfigFolder(); + if (!dataFolder.toFile().exists()) { + //noinspection ResultOfMethodCallIgnored + dataFolder.toFile().mkdir(); + } + return this; } - public static T load(File file, Class configClass, @Nullable Consumer transformer) throws IOException { - var loader = createLoader(file, HEADER); + @This + public ConfigLoader transformer(Consumer transformer) { + this.transformer = transformer; + return this; + } + + @This + public ConfigLoader configFile(File configFile) { + this.configFile = configFile; + return this; + } + + /** + * @return null if the config failed to load. + */ + @Nullable + public T load(Class configClass) { + try { + return load0(configClass); + } catch (IOException ex) { + bootstrap.getGeyserLogger().error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); + return null; + } + } + + private T load0(Class configClass) throws IOException { + var loader = createLoader(configFile, HEADER); CommentedConfigurationNode node = loader.load(); - boolean originallyEmpty = !file.exists() || node.isNull(); + boolean originallyEmpty = !configFile.exists() || node.isNull(); // Note for Tim? Needed or else Configurate breaks. var migrations = ConfigurationTransformation.versionedBuilder() @@ -157,8 +205,8 @@ public static T load(File file, Class configClass, @ newRoot.set(config); // Create the path in a way that Standalone changing the config name will be fine. - int extensionIndex = file.getName().lastIndexOf("."); - File advancedConfigPath = new File(file.getParent(), file.getName().substring(0, extensionIndex) + "_advanced" + file.getName().substring(extensionIndex)); + int extensionIndex = configFile.getName().lastIndexOf("."); + File advancedConfigPath = new File(configFile.getParent(), configFile.getName().substring(0, extensionIndex) + "_advanced" + configFile.getName().substring(extensionIndex)); AdvancedConfig advancedConfig = null; if (originallyEmpty || currentVersion != newVersion) { @@ -188,13 +236,15 @@ public static T load(File file, Class configClass, @ config.advanced(advancedConfig); + bootstrap.getGeyserLogger().setDebug(config.debugMode()); + return config; } - private static AdvancedConfig migrateToAdvancedConfig(File file, ConfigurationNode configRoot) throws IOException { - List copyFromOldConfig = Stream.of("max-visible-custom-skulls", "custom-skull-render-distance", "scoreboard-packet-threshold", "mtu", + private AdvancedConfig migrateToAdvancedConfig(File file, ConfigurationNode configRoot) throws IOException { + Stream copyFromOldConfig = Stream.of("max-visible-custom-skulls", "custom-skull-render-distance", "scoreboard-packet-threshold", "mtu", "floodgate-key-file", "use-direct-connection", "disable-compression") - .map(NodePath::path).toList(); + .map(NodePath::path); var loader = createLoader(file, ADVANCED_HEADER); @@ -222,7 +272,7 @@ private static AdvancedConfig migrateToAdvancedConfig(File file, ConfigurationNo return advancedConfig; } - private static AdvancedConfig loadAdvancedConfig(File file) throws IOException { + private AdvancedConfig loadAdvancedConfig(File file) throws IOException { var loader = createLoader(file, ADVANCED_HEADER); if (file.exists()) { ConfigurationNode node = loader.load(); @@ -237,18 +287,38 @@ private static AdvancedConfig loadAdvancedConfig(File file) throws IOException { } } - private static YamlConfigurationLoader createLoader(File file, String header) { + private YamlConfigurationLoader createLoader(File file, String header) { return YamlConfigurationLoader.builder() .file(file) .indent(2) .nodeStyle(NodeStyle.BLOCK) - .defaultOptions(options -> InterfaceDefaultOptions.addTo(options) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options, builder -> + builder.addProcessor(ExcludePlatform.class, excludePlatform(bootstrap.platformType().platformName())) + .addProcessor(PlatformTypeSpecific.class, platformTypeSpecific(bootstrap.platformType() != PlatformType.STANDALONE))) .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. .header(header) .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) .build(); } + + private static Processor.Factory excludePlatform(String thisPlatform) { + return (data, fieldType) -> (value, destination) -> { + for (String platform : data.platforms()) { + if (thisPlatform.equals(platform)) { + //noinspection DataFlowIssue + destination.parent().removeChild(destination.key()); + break; + } + } + }; + } - private ConfigLoader() { + private static Processor.Factory platformTypeSpecific(boolean thisConfigPlugin) { + return (data, fieldType) -> (value, destination) -> { + if (data.forPlugin() != thisConfigPlugin) { + //noinspection DataFlowIssue + destination.parent().removeChild(destination.key()); + } + }; } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ExcludePlatform.java b/core/src/main/java/org/geysermc/geyser/configuration/ExcludePlatform.java new file mode 100644 index 00000000000..1308872c318 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/ExcludePlatform.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 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.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcludePlatform { + String[] platforms(); +} diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index cbc2f6c109a..e055ab9d229 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -83,6 +83,11 @@ default List savedUserLogins() { @DefaultBoolean(true) boolean passthroughPlayerCounts(); + @Comment(""" + Use server API methods to determine the Java server's MOTD and ping passthrough. + There is no need to disable this unless your MOTD or player count does not appear properly.""") + @DefaultBoolean(true) + @PlatformTypeSpecific boolean integratedPingPassthrough(); @Comment("How often to ping the Java server to refresh MOTD and player count, in seconds.") @@ -214,6 +219,7 @@ interface BedrockConfig extends BedrockListener { Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. This option makes the Bedrock port the same as the Java port every time you start the server.""") @DefaultBoolean + @PlatformTypeSpecific boolean cloneRemotePort(); void address(String address); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java index 19bcd50128c..c08875baa4e 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserPluginConfig.java @@ -27,7 +27,6 @@ import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.Field; -import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; @@ -59,13 +58,6 @@ default boolean forwardHostname() { } } - @Comment(""" - Use server API methods to determine the Java server's MOTD and ping passthrough. - There is no need to disable this unless your MOTD or player count does not appear properly.""") - @DefaultBoolean(true) - @Override - boolean integratedPingPassthrough(); - @Comment(""" How often to ping the Java server to refresh MOTD and player count, in seconds. Only relevant if integrated-ping-passthrough is disabled.""") diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index 3baa5a5cb95..5c48c6b2f76 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -25,7 +25,6 @@ package org.geysermc.geyser.configuration; -import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; import org.spongepowered.configurate.interfaces.meta.range.NumericRange; @@ -37,21 +36,9 @@ */ @ConfigSerializable public interface GeyserRemoteConfig extends GeyserConfig { - @Override - RemoteBedrock bedrock(); - @Override RemoteConfig java(); - @ConfigSerializable - interface RemoteBedrock extends BedrockConfig { - @Override - @Exclude // We can bring this back if there's a use-case but it's not really justified here. - default boolean cloneRemotePort() { - return false; - } - } - @ConfigSerializable interface RemoteConfig extends JavaConfig { @Override @@ -71,11 +58,4 @@ interface RemoteConfig extends JavaConfig { This is designed to be used for forced hosts on proxies""") boolean forwardHostname(); } - - @Exclude - @Override - default boolean integratedPingPassthrough() { - // Does nothing here. - return false; - } } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java b/core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java new file mode 100644 index 00000000000..a5eb4044761 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 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.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PlatformTypeSpecific { + boolean forPlugin() default true; +} diff --git a/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java index 2956c0329ae..e373309a4ff 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/BootstrapDumpInfo.java @@ -39,7 +39,7 @@ public class BootstrapDumpInfo { private final PlatformType platform; public BootstrapDumpInfo() { - this.platform = GeyserImpl.getInstance().getPlatformType(); + this.platform = GeyserImpl.getInstance().platformType(); } @Getter diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index fdda92a0a3d..eecb3c7fc27 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1084,7 +1084,7 @@ public void disconnected(DisconnectedEvent event) { disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale()); // Explain that they may be looking for Floodgate. geyser.getLogger().warning(GeyserLocale.getLocaleStringLog( - geyser.getPlatformType() == PlatformType.STANDALONE ? + geyser.platformType() == PlatformType.STANDALONE ? "geyser.network.remote.floodgate_explanation_standalone" : "geyser.network.remote.floodgate_explanation_plugin", Constants.FLOODGATE_DOWNLOAD_LOCATION @@ -1092,7 +1092,7 @@ public void disconnected(DisconnectedEvent event) { } else { // Likely that Floodgate is not configured correctly. disconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale()); - if (geyser.getPlatformType() == PlatformType.STANDALONE) { + if (geyser.platformType() == PlatformType.STANDALONE) { geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone")); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java index 1e84f032e0b..1257e410f41 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockCommandRequestTranslator.java @@ -44,8 +44,8 @@ public void translate(GeyserSession session, CommandRequestPacket packet) { } static void handleCommand(GeyserSession session, String command) { - if (session.getGeyser().getPlatformType() == PlatformType.STANDALONE || - session.getGeyser().getPlatformType() == PlatformType.VIAPROXY) { + if (session.getGeyser().platformType() == PlatformType.STANDALONE || + session.getGeyser().platformType() == PlatformType.VIAPROXY) { // try to handle the command within the standalone/viaproxy command manager String[] args = command.split(" "); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginDisconnectTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginDisconnectTranslator.java index e92ae1bbd75..164c7638f58 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginDisconnectTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginDisconnectTranslator.java @@ -50,7 +50,7 @@ public void translate(GeyserSession session, ClientboundLoginDisconnectPacket pa String disconnectMessage; if (testForOutdatedServer(disconnectReason)) { String locale = session.locale(); - PlatformType platform = session.getGeyser().getPlatformType(); + PlatformType platform = session.getGeyser().platformType(); String outdatedType = (platform == PlatformType.BUNGEECORD || platform == PlatformType.VELOCITY || platform == PlatformType.VIAPROXY) ? "geyser.network.remote.outdated.proxy" : "geyser.network.remote.outdated.server"; disconnectMessage = GeyserLocale.getPlayerLocaleString(outdatedType, locale, GameProtocol.getJavaVersions().get(0)) + '\n' diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java index c94468c17bd..8c953f8cfe5 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaBlockEventTranslator.java @@ -92,7 +92,7 @@ public void translate(GeyserSession session, ClientboundBlockEventPacket packet) // Retracting sticky pistons is an exception, since the event is not called on Spigot from 1.13.2 - 1.17.1 // See https://github.com/PaperMC/Paper/blob/6fa1983e9ce177a4a412d5b950fd978620174777/patches/server/0304-Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch boolean isSticky = isSticky(pistonBlock); - if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT && !isSticky) { + if (session.getGeyser().platformType() == PlatformType.SPIGOT && !isSticky) { return; } diff --git a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java index 5f489b900e4..363c7e3d4bd 100644 --- a/core/src/main/java/org/geysermc/geyser/util/WebUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/WebUtils.java @@ -233,6 +233,6 @@ public static Stream getLineStream(String reqURL) { } public static String getUserAgent() { - return "Geyser-" + GeyserImpl.getInstance().getPlatformType().platformName() + "/" + GeyserImpl.VERSION; + return "Geyser-" + GeyserImpl.getInstance().platformType().platformName() + "/" + GeyserImpl.VERSION; } } From b71927840b5c025c1c8fec01b4e47430b1b5f7bc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:36:23 -0400 Subject: [PATCH 23/42] Renaming to PluginSpecific --- .../org/geysermc/geyser/configuration/AdvancedConfig.java | 4 ++-- .../org/geysermc/geyser/configuration/ConfigLoader.java | 4 ++-- .../org/geysermc/geyser/configuration/GeyserConfig.java | 4 ++-- .../{PlatformTypeSpecific.java => PluginSpecific.java} | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) rename core/src/main/java/org/geysermc/geyser/configuration/{PlatformTypeSpecific.java => PluginSpecific.java} (91%) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index 1d64f3c6a20..60d27814698 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -87,7 +87,7 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o If disabled, expect performance decrease and latency increase """) @DefaultBoolean(true) - @PlatformTypeSpecific + @PluginSpecific boolean useDirectConnection(); @Comment(""" @@ -96,7 +96,7 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o This requires use-direct-connection to be true. """) @DefaultBoolean(true) - @PlatformTypeSpecific + @PluginSpecific boolean disableCompression(); @Comment("Do not touch!") diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index dcda2fe0b89..634d827cc9a 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -294,7 +294,7 @@ private YamlConfigurationLoader createLoader(File file, String header) { .nodeStyle(NodeStyle.BLOCK) .defaultOptions(options -> InterfaceDefaultOptions.addTo(options, builder -> builder.addProcessor(ExcludePlatform.class, excludePlatform(bootstrap.platformType().platformName())) - .addProcessor(PlatformTypeSpecific.class, platformTypeSpecific(bootstrap.platformType() != PlatformType.STANDALONE))) + .addProcessor(PluginSpecific.class, integrationSpecific(bootstrap.platformType() != PlatformType.STANDALONE))) .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. .header(header) .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) @@ -313,7 +313,7 @@ private static Processor.Factory excludePlatform(String }; } - private static Processor.Factory platformTypeSpecific(boolean thisConfigPlugin) { + private static Processor.Factory integrationSpecific(boolean thisConfigPlugin) { return (data, fieldType) -> (value, destination) -> { if (data.forPlugin() != thisConfigPlugin) { //noinspection DataFlowIssue diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index e055ab9d229..16380e21b99 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -87,7 +87,7 @@ default List savedUserLogins() { Use server API methods to determine the Java server's MOTD and ping passthrough. There is no need to disable this unless your MOTD or player count does not appear properly.""") @DefaultBoolean(true) - @PlatformTypeSpecific + @PluginSpecific boolean integratedPingPassthrough(); @Comment("How often to ping the Java server to refresh MOTD and player count, in seconds.") @@ -219,7 +219,7 @@ interface BedrockConfig extends BedrockListener { Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. This option makes the Bedrock port the same as the Java port every time you start the server.""") @DefaultBoolean - @PlatformTypeSpecific + @PluginSpecific boolean cloneRemotePort(); void address(String address); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java b/core/src/main/java/org/geysermc/geyser/configuration/PluginSpecific.java similarity index 91% rename from core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java rename to core/src/main/java/org/geysermc/geyser/configuration/PluginSpecific.java index a5eb4044761..91871687acd 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/PlatformTypeSpecific.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/PluginSpecific.java @@ -30,8 +30,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Add to a config value to indicate this field is only for plugin versions of Geyser, + * or vice-versa. + */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) -public @interface PlatformTypeSpecific { +public @interface PluginSpecific { boolean forPlugin() default true; } From 1271505c26e20f50c5a6b1697dfdff94f16c0cb3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:36:42 -0400 Subject: [PATCH 24/42] Use global bStats config where possible --- .../platform/bungeecord/BungeeMetrics.java | 99 +++++++++++++++++++ .../bungeecord/GeyserBungeePlugin.java | 15 +++ .../platform/spigot/GeyserSpigotPlugin.java | 6 ++ .../geyser/platform/spigot/SpigotMetrics.java | 91 +++++++++++++++++ .../velocity/GeyserVelocityPlugin.java | 15 +++ .../platform/velocity/VelocityMetrics.java | 69 +++++++++++++ .../org/geysermc/geyser/GeyserBootstrap.java | 7 ++ .../java/org/geysermc/geyser/GeyserImpl.java | 12 ++- .../geyser/configuration/AdvancedConfig.java | 1 + .../geyser/configuration/GeyserConfig.java | 1 + .../geyser/util/metrics/MetricsPlatform.java | 38 +++++++ .../util/metrics/ProvidedMetricsPlatform.java | 56 +++++++++++ .../configuration/ConfigLoaderTest.java | 40 ++++---- 13 files changed, 423 insertions(+), 27 deletions(-) create mode 100644 bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/BungeeMetrics.java create mode 100644 bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/SpigotMetrics.java create mode 100644 bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/VelocityMetrics.java create mode 100644 core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java create mode 100644 core/src/main/java/org/geysermc/geyser/util/metrics/ProvidedMetricsPlatform.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/BungeeMetrics.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/BungeeMetrics.java new file mode 100644 index 00000000000..8fa3719f87f --- /dev/null +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/BungeeMetrics.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 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.bungeecord; + +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; +import org.geysermc.geyser.util.metrics.MetricsPlatform; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.UUID; + +public final class BungeeMetrics implements MetricsPlatform { + private final Configuration configuration; + + public BungeeMetrics(Plugin plugin) throws IOException { + // https://github.com/Bastian/bstats-metrics/blob/master/bungeecord/src/main/java/org/bstats/bungeecord/Metrics.java + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + //noinspection ResultOfMethodCallIgnored + bStatsFolder.mkdirs(); + File configFile = new File(bStatsFolder, "config.yml"); + if (!configFile.exists()) { + writeFile(configFile, + "# bStats (https://bStats.org) collects some basic information for plugin authors, like how", + "# many people use their plugin and their total player count. It's recommended to keep bStats", + "# enabled, but if you're not comfortable with this, you can turn this setting off. There is no", + "# performance penalty associated with having metrics enabled, and data sent to bStats is fully", + "# anonymous.", + "enabled: true", + "serverUuid: \"" + UUID.randomUUID() + "\"", + "logFailedRequests: false", + "logSentData: false", + "logResponseStatusText: false"); + } + + this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); + } + + @Override + public boolean enabled() { + return configuration.getBoolean("enabled", true); + } + + @Override + public String serverUuid() { + return configuration.getString("serverUuid"); + } + + @Override + public boolean logFailedRequests() { + return configuration.getBoolean("logFailedRequests", false); + } + + @Override + public boolean logSentData() { + return configuration.getBoolean("logSentData", false); + } + + @Override + public boolean logResponseStatusText() { + return configuration.getBoolean("logResponseStatusText", false); + } + + private void writeFile(File file, String... lines) throws IOException { + try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) { + for (String line : lines) { + bufferedWriter.write(line); + bufferedWriter.newLine(); + } + } + } +} diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index d8338a2ce6f..9745b0497b5 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -47,10 +47,12 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.metrics.MetricsPlatform; import org.incendo.cloud.CommandManager; import org.incendo.cloud.bungee.BungeeCommandManager; import org.incendo.cloud.execution.ExecutionCoordinator; +import java.io.IOException; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -288,6 +290,19 @@ public Path getFloodgateKeyPath() { return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger); } + @Override + public MetricsPlatform createMetricsPlatform() { + try { + return new BungeeMetrics(this); + } catch (IOException e) { + this.geyserLogger.debug("Integrated bStats support failed to load."); + if (this.config().debugMode()) { + e.printStackTrace(); + } + return null; + } + } + private Optional findCompatibleListener() { return getProxy().getConfig().getListeners().stream() .filter(info -> info.getSocketAddress() instanceof InetSocketAddress) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index 9eafd510033..f3fbbe7bda9 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -67,6 +67,7 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager; import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.metrics.MetricsPlatform; import org.incendo.cloud.bukkit.BukkitCommandManager; import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.paper.LegacyPaperCommandManager; @@ -462,6 +463,11 @@ public Path getFloodgateKeyPath() { return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger); } + @Override + public MetricsPlatform createMetricsPlatform() { + return new SpigotMetrics(this); + } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/SpigotMetrics.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/SpigotMetrics.java new file mode 100644 index 00000000000..796ae4b881e --- /dev/null +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/SpigotMetrics.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 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.spigot; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.geysermc.geyser.util.metrics.MetricsPlatform; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; + +public final class SpigotMetrics implements MetricsPlatform { + private final YamlConfiguration config; + + public SpigotMetrics(Plugin plugin) { + // https://github.com/Bastian/bstats-metrics/blob/master/bukkit/src/main/java/org/bstats/bukkit/Metrics.java + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + config = YamlConfiguration.loadConfiguration(configFile); + + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + + // Inform the server owners about bStats + config.options().header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous." + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { } + } + } + + @Override + public boolean enabled() { + return config.getBoolean("enabled", true); + } + + @Override + public String serverUuid() { + return config.getString("serverUuid"); + } + + @Override + public boolean logFailedRequests() { + return config.getBoolean("logFailedRequests", false); + } + + @Override + public boolean logSentData() { + return config.getBoolean("logSentData", false); + } + + @Override + public boolean logResponseStatusText() { + return config.getBoolean("logResponseStatusText", false); + } +} diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 1b8674871d7..a39a7a89121 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -54,11 +54,13 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.metrics.MetricsPlatform; import org.incendo.cloud.CommandManager; import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.velocity.VelocityCommandManager; import org.slf4j.Logger; +import java.io.IOException; import java.net.SocketAddress; import java.nio.file.Path; import java.nio.file.Paths; @@ -250,6 +252,19 @@ public Path getFloodgateKeyPath() { return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataPath, configFolder, geyserLogger); } + @Override + public MetricsPlatform createMetricsPlatform() { + try { + return new VelocityMetrics(this.configFolder); + } catch (IOException e) { + this.geyserLogger.debug("Integrated bStats support failed to load."); + if (this.config().debugMode()) { + e.printStackTrace(); + } + return null; + } + } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean loadConfig() { this.geyserConfig = new ConfigLoader(this).createFolder().load(GeyserPluginConfig.class); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/VelocityMetrics.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/VelocityMetrics.java new file mode 100644 index 00000000000..5939c6678fd --- /dev/null +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/VelocityMetrics.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 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.velocity; + +import org.bstats.config.MetricsConfig; +import org.geysermc.geyser.util.metrics.MetricsPlatform; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +public final class VelocityMetrics implements MetricsPlatform { + private final MetricsConfig config; + + public VelocityMetrics(Path dataDirectory) throws IOException { + // https://github.com/Bastian/bstats-metrics/blob/master/velocity/src/main/java/org/bstats/velocity/Metrics.java + File configFile = dataDirectory.getParent().resolve("bStats").resolve("config.txt").toFile(); + this.config = new MetricsConfig(configFile, true); + // No logger message is implemented as Velocity should print its own before we do. + } + + @Override + public boolean enabled() { + return config.isEnabled(); + } + + @Override + public String serverUuid() { + return config.getServerUUID(); + } + + @Override + public boolean logFailedRequests() { + return config.isLogErrorsEnabled(); + } + + @Override + public boolean logSentData() { + return config.isLogSentDataEnabled(); + } + + @Override + public boolean logResponseStatusText() { + return config.isLogResponseStatusTextEnabled(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java index 121a43cc177..ed713cf0c25 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserBootstrap.java @@ -34,6 +34,8 @@ import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.WorldManager; import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.geysermc.geyser.util.metrics.MetricsPlatform; +import org.geysermc.geyser.util.metrics.ProvidedMetricsPlatform; import java.io.InputStream; import java.net.SocketAddress; @@ -203,4 +205,9 @@ default Path getLogsPath() { * TEMPORARY - will be removed after The Merge:tm:. */ Path getFloodgateKeyPath(); + + @Nullable + default MetricsPlatform createMetricsPlatform() { + return new ProvidedMetricsPlatform(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 1f93a42a39b..c426015d435 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -103,6 +103,7 @@ import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.VersionCheckUtils; import org.geysermc.geyser.util.WebUtils; +import org.geysermc.geyser.util.metrics.MetricsPlatform; import org.geysermc.mcprotocollib.network.tcp.TcpSession; import java.io.File; @@ -465,10 +466,11 @@ private void startInstance() { } } - if (config.enableMetrics()) { + MetricsPlatform metricsPlatform = bootstrap.createMetricsPlatform(); + if (metricsPlatform != null && metricsPlatform.enabled()) { metrics = new MetricsBase( "server-implementation", - config.advanced().metricsUuid().toString(), + metricsPlatform.serverUuid(), Constants.BSTATS_ID, true, // Already checked above. builder -> { @@ -488,9 +490,9 @@ private void startInstance() { () -> true, logger::error, logger::info, - config.debugMode(), - config.debugMode(), - config.debugMode()); + metricsPlatform.logFailedRequests(), + metricsPlatform.logSentData(), + metricsPlatform.logResponseStatusText()); metrics.addCustomChart(new SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT))); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index 60d27814698..e19b896e04d 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -100,6 +100,7 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o boolean disableCompression(); @Comment("Do not touch!") + @ExcludePlatform(platforms = {"BungeeCord", "Spigot", "Velocity"}) // bStats platform versions used default UUID metricsUuid() { return UUID.randomUUID(); } diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 16380e21b99..e9074ceae92 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -179,6 +179,7 @@ default CooldownUtils.CooldownType showCooldown() { what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. https://bstats.org/plugin/server-implementation/GeyserMC""") @DefaultBoolean(true) + @ExcludePlatform(platforms = {"BungeeCord", "Spigot", "Velocity"}) // bStats platform versions used boolean enableMetrics(); /** diff --git a/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java b/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java new file mode 100644 index 00000000000..c4f3b47c207 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 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.metrics; + +public interface MetricsPlatform { + boolean enabled(); + + String serverUuid(); + + boolean logFailedRequests(); + + boolean logSentData(); + + boolean logResponseStatusText(); +} diff --git a/core/src/main/java/org/geysermc/geyser/util/metrics/ProvidedMetricsPlatform.java b/core/src/main/java/org/geysermc/geyser/util/metrics/ProvidedMetricsPlatform.java new file mode 100644 index 00000000000..c2162bf74b3 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/metrics/ProvidedMetricsPlatform.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 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.metrics; + +import org.geysermc.geyser.GeyserImpl; + +public final class ProvidedMetricsPlatform implements MetricsPlatform { + + @Override + public boolean enabled() { + return GeyserImpl.getInstance().config().enableMetrics(); + } + + @Override + public String serverUuid() { + return GeyserImpl.getInstance().config().advanced().metricsUuid().toString(); + } + + @Override + public boolean logFailedRequests() { + return GeyserImpl.getInstance().config().debugMode(); + } + + @Override + public boolean logSentData() { + return GeyserImpl.getInstance().config().debugMode(); + } + + @Override + public boolean logResponseStatusText() { + return GeyserImpl.getInstance().config().debugMode(); + } +} diff --git a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java index 0dcc7d33d70..d2f0cbbf3a6 100644 --- a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java +++ b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java @@ -31,12 +31,7 @@ import org.spongepowered.configurate.util.CheckedConsumer; import java.io.File; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; public class ConfigLoaderTest { @@ -53,23 +48,24 @@ void testCreateNewConfig() throws Exception { File file = tempDirectory.resolve("config.yml").toFile(); - forAllConfigs(type -> { - ConfigLoader.load(file, type, n -> this.config1 = n.copy()); - long initialModification = file.lastModified(); - assertTrue(file.exists()); // should have been created - List firstContents = Files.readAllLines(file.toPath()); - - ConfigLoader.load(file, type, n -> this.config2 = n.copy()); - List secondContents = Files.readAllLines(file.toPath()); - - assertEquals(initialModification, file.lastModified()); // should not have been touched - assertEquals(firstContents, secondContents); - - // Must ignore this, as when the config is read back, the header is interpreted as a comment on the first node in the map - config1.node("java").comment(null); - config2.node("java").comment(null); - assertEquals(config1, config2); - }); + // Sorry Konicai... +// forAllConfigs(type -> { +// ConfigLoader.load(file, type, n -> this.config1 = n.copy()); +// long initialModification = file.lastModified(); +// assertTrue(file.exists()); // should have been created +// List firstContents = Files.readAllLines(file.toPath()); +// +// ConfigLoader.load(file, type, n -> this.config2 = n.copy()); +// List secondContents = Files.readAllLines(file.toPath()); +// +// assertEquals(initialModification, file.lastModified()); // should not have been touched +// assertEquals(firstContents, secondContents); +// +// // Must ignore this, as when the config is read back, the header is interpreted as a comment on the first node in the map +// config1.node("java").comment(null); +// config2.node("java").comment(null); +// assertEquals(config1, config2); +// }); } void forAllConfigs(CheckedConsumer, Exception> consumer) throws Exception { From e99759d701b0a4a9e27cf67d25ea48044d628e62 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:45:40 -0400 Subject: [PATCH 25/42] Fix test --- .../geyser/configuration/ConfigLoader.java | 25 +++++++++--- .../configuration/ConfigLoaderTest.java | 39 +++++++++++-------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 634d827cc9a..2639672af13 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.configuration; +import com.google.common.annotations.VisibleForTesting; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.returnsreceiver.qual.This; import org.geysermc.geyser.Constants; @@ -75,7 +76,10 @@ public final class ConfigLoader { -------------------------------- """; - private final GeyserBootstrap bootstrap; + /** + * Only nullable for testing. + */ + private final @Nullable GeyserBootstrap bootstrap; private @Nullable Consumer transformer; private File configFile; @@ -84,6 +88,12 @@ public ConfigLoader(GeyserBootstrap bootstrap) { configFile = new File(bootstrap.getConfigFolder().toFile(), "config.yml"); } + @VisibleForTesting + ConfigLoader(File file) { + this.bootstrap = null; + configFile = file; + } + /** * Creates the directory as indicated by {@link GeyserBootstrap#getConfigFolder()} */ @@ -236,7 +246,9 @@ private T load0(Class configClass) throws IOExceptio config.advanced(advancedConfig); - bootstrap.getGeyserLogger().setDebug(config.debugMode()); + if (this.bootstrap != null) { // Null for testing only. + this.bootstrap.getGeyserLogger().setDebug(config.debugMode()); + } return config; } @@ -292,9 +304,12 @@ private YamlConfigurationLoader createLoader(File file, String header) { .file(file) .indent(2) .nodeStyle(NodeStyle.BLOCK) - .defaultOptions(options -> InterfaceDefaultOptions.addTo(options, builder -> - builder.addProcessor(ExcludePlatform.class, excludePlatform(bootstrap.platformType().platformName())) - .addProcessor(PluginSpecific.class, integrationSpecific(bootstrap.platformType() != PlatformType.STANDALONE))) + .defaultOptions(options -> InterfaceDefaultOptions.addTo(options, builder -> { + if (this.bootstrap != null) { // Testing only. + builder.addProcessor(ExcludePlatform.class, excludePlatform(bootstrap.platformType().platformName())) + .addProcessor(PluginSpecific.class, integrationSpecific(bootstrap.platformType() != PlatformType.STANDALONE)); + } + }) .shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node. .header(header) .serializers(builder -> builder.register(new LowercaseEnumSerializer()))) diff --git a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java index d2f0cbbf3a6..098dce7dc52 100644 --- a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java +++ b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java @@ -31,7 +31,12 @@ import org.spongepowered.configurate.util.CheckedConsumer; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ConfigLoaderTest { @@ -49,23 +54,23 @@ void testCreateNewConfig() throws Exception { File file = tempDirectory.resolve("config.yml").toFile(); // Sorry Konicai... -// forAllConfigs(type -> { -// ConfigLoader.load(file, type, n -> this.config1 = n.copy()); -// long initialModification = file.lastModified(); -// assertTrue(file.exists()); // should have been created -// List firstContents = Files.readAllLines(file.toPath()); -// -// ConfigLoader.load(file, type, n -> this.config2 = n.copy()); -// List secondContents = Files.readAllLines(file.toPath()); -// -// assertEquals(initialModification, file.lastModified()); // should not have been touched -// assertEquals(firstContents, secondContents); -// -// // Must ignore this, as when the config is read back, the header is interpreted as a comment on the first node in the map -// config1.node("java").comment(null); -// config2.node("java").comment(null); -// assertEquals(config1, config2); -// }); + forAllConfigs(type -> { + new ConfigLoader(file).transformer(n -> this.config1 = n.copy()).load(type); + long initialModification = file.lastModified(); + assertTrue(file.exists()); // should have been created + List firstContents = Files.readAllLines(file.toPath()); + + new ConfigLoader(file).transformer(n -> this.config2 = n.copy()).load(type); + List secondContents = Files.readAllLines(file.toPath()); + + assertEquals(initialModification, file.lastModified()); // should not have been touched + assertEquals(firstContents, secondContents); + + // Must ignore this, as when the config is read back, the header is interpreted as a comment on the first node in the map + config1.node("java").comment(null); + config2.node("java").comment(null); + assertEquals(config1, config2); + }); } void forAllConfigs(CheckedConsumer, Exception> consumer) throws Exception { From 5551169c4ce6cbdcd1578a81079b25c1a04df8e7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:00:30 -0400 Subject: [PATCH 26/42] Re-introduce asterisk behavior in configs --- .../geyser/configuration/GeyserConfig.java | 2 ++ .../configuration/GeyserRemoteConfig.java | 2 ++ .../org/geysermc/geyser/dump/DumpInfo.java | 32 +++++++++++++++---- .../geysermc/geyser/ping/GeyserPingInfo.java | 18 +++++++++++ .../geyser/text/AsteriskSerializer.java | 25 ++++++++++++++- 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index e9074ceae92..f3a98db10b3 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -31,6 +31,7 @@ import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.util.CooldownUtils; import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.Field; @@ -200,6 +201,7 @@ interface BedrockConfig extends BedrockListener { Generally, you should only uncomment and change this if you want to limit what IPs can connect to your server.""") @NonNull @DefaultString("0.0.0.0") + @AsteriskSerializer.Asterisk String address(); @Override diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java index 5c48c6b2f76..67496e5a17f 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserRemoteConfig.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.configuration; +import org.geysermc.geyser.text.AsteriskSerializer; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; import org.spongepowered.configurate.interfaces.meta.range.NumericRange; @@ -44,6 +45,7 @@ interface RemoteConfig extends JavaConfig { @Override @Comment("The IP address of the Java Edition server.") @DefaultString("127.0.0.1") + @AsteriskSerializer.Asterisk String address(); @Override diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index 489d9d86a9e..acb326d7088 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -40,13 +40,17 @@ import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.configuration.AdvancedConfig; -import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.util.CpuUtils; import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.WebUtils; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.ConfigurationOptions; +import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; +import org.spongepowered.configurate.serialize.SerializationException; import java.io.File; import java.io.IOException; @@ -73,8 +77,8 @@ public class DumpInfo { private final Locale systemLocale; private final String systemEncoding; private final GitInfo gitInfo; - private final GeyserConfig config; - private final AdvancedConfig advancedConfig; + private Object config; + private Object advancedConfig; private final Object2IntMap userPlatforms; private final int connectionAttempts; private final HashInfo hashInfo; @@ -94,8 +98,24 @@ public DumpInfo(GeyserImpl geyser, boolean addLog) { this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); - this.config = geyser.config(); - this.advancedConfig = geyser.config().advanced(); + try { + // Workaround for JsonAdapter not being allowed on methods + ConfigurationOptions options = InterfaceDefaultOptions.addTo(ConfigurationOptions.defaults(), builder -> + builder.addProcessor(AsteriskSerializer.Asterisk.class, String.class, AsteriskSerializer.CONFIGURATE_SERIALIZER)) + .shouldCopyDefaults(false); + + ConfigurationNode configNode = CommentedConfigurationNode.root(options); + configNode.set(geyser.config()); + this.config = configNode.get(geyser.config().getClass()); + + ConfigurationNode advancedConfigNode = CommentedConfigurationNode.root(options); + advancedConfigNode.set(geyser.config().advanced()); + this.advancedConfig = advancedConfigNode.get(AdvancedConfig.class); + } catch (SerializationException e) { + if (geyser.config().debugMode()) { + e.printStackTrace(); + } + } String md5Hash = "unknown"; String sha256Hash = "unknown"; @@ -110,7 +130,7 @@ public DumpInfo(GeyserImpl geyser, boolean addLog) { //noinspection UnstableApiUsage sha256Hash = byteSource.hash(Hashing.sha256()).toString(); } catch (Exception e) { - if (this.config.debugMode()) { + if (geyser.config().debugMode()) { e.printStackTrace(); } } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java index cff46d834e1..853fa6187d7 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserPingInfo.java @@ -25,9 +25,16 @@ package org.geysermc.geyser.ping; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.JsonAdapter; import lombok.Data; import org.checkerframework.checker.nullness.qual.Nullable; +import java.lang.reflect.Type; + /** * The structure of this class and its nested classes are specifically * designed for the format received by {@link GeyserLegacyPingPassthrough}. @@ -36,6 +43,7 @@ public class GeyserPingInfo { @Nullable + @JsonAdapter(DescriptionDeserializer.class) private String description; private Players players; @@ -69,4 +77,14 @@ public Players(int max, int online) { this.online = online; } } + + /** + * So GSON does not complain how we are treating Description - it will be converted to a proper Component later. + */ + private static final class DescriptionDeserializer implements JsonDeserializer { + @Override + public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return json.toString(); + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java index 325eaccae21..b34db2b99c5 100644 --- a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java +++ b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java @@ -29,7 +29,13 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import org.spongepowered.configurate.objectmapping.meta.Processor; +import org.spongepowered.configurate.serialize.SerializationException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Type; import java.net.InetAddress; @@ -47,7 +53,7 @@ public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContex return new JsonPrimitive("***"); } - private boolean isSensitiveIp(String ip) { + private static boolean isSensitiveIp(String ip) { for (String address : NON_SENSITIVE_ADDRESSES) { if (address.equalsIgnoreCase(ip)) { return false; @@ -65,4 +71,21 @@ private boolean isSensitiveIp(String ip) { return true; } + + public static Processor.Factory CONFIGURATE_SERIALIZER = (data, fieldType) -> (value, destination) -> { + if (showSensitive || !isSensitiveIp(value)) { + return; + } + try { + destination.set("***"); + } catch (SerializationException e) { + throw new RuntimeException(e); // Error over silently printing an IP address. + } + }; + + @Target({ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Asterisk { + + } } From 45f8baf49e284bb5d06d89019ec445043bd5f3e0 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:05:33 -0400 Subject: [PATCH 27/42] Remove GeyserPluginBootstrap as it's no longer necessary --- .../bungeecord/GeyserBungeeInjector.java | 6 ++-- .../bungeecord/GeyserBungeePlugin.java | 4 +-- .../platform/mod/GeyserModBootstrap.java | 4 +-- .../platform/mod/GeyserModInjector.java | 3 +- .../platform/spigot/GeyserSpigotInjector.java | 11 +++--- .../platform/spigot/GeyserSpigotPlugin.java | 4 +-- .../velocity/GeyserVelocityInjector.java | 4 +-- .../velocity/GeyserVelocityPlugin.java | 4 +-- .../viaproxy/GeyserViaProxyPlugin.java | 4 +-- .../geyser/GeyserPluginBootstrap.java | 36 ------------------- .../geyser/network/netty/GeyserInjector.java | 8 ++--- 11 files changed, 27 insertions(+), 61 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java index 35d572cbd84..f84702ead27 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeeInjector.java @@ -40,8 +40,8 @@ import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.network.netty.LocalSession; @@ -67,7 +67,7 @@ public GeyserBungeeInjector(Plugin plugin) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { // TODO - allow Geyser to specify its own listener info properties if (proxy.getConfig().getListeners().size() != 1) { throw new UnsupportedOperationException("Geyser does not currently support multiple listeners with injection! " + @@ -191,7 +191,7 @@ public void onProxyReload(ProxyReloadEvent event) { this.bungeeChannels = null; if (this.localChannel != null) { shutdown(); - initializeLocalChannel((GeyserPluginBootstrap) GeyserImpl.getInstance().getBootstrap()); + initializeLocalChannel(GeyserImpl.getInstance().getBootstrap()); } } } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index 9745b0497b5..70a4d082af8 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -34,8 +34,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.FloodgateKeyLoader; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandSourceConverter; @@ -62,7 +62,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -public class GeyserBungeePlugin extends Plugin implements GeyserPluginBootstrap { +public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { private CommandRegistry commandRegistry; private GeyserPluginConfig geyserConfig; diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java index 75bae5acde5..f229c49745c 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java @@ -32,9 +32,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.FloodgateKeyLoader; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.configuration.ConfigLoader; @@ -52,7 +52,7 @@ import java.nio.file.Path; @RequiredArgsConstructor -public abstract class GeyserModBootstrap implements GeyserPluginBootstrap { +public abstract class GeyserModBootstrap implements GeyserBootstrap { @Getter private static GeyserModBootstrap instance; diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java index ccbc2a1e774..4bfc7464a3a 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModInjector.java @@ -38,7 +38,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform; @@ -64,7 +63,7 @@ public GeyserModInjector(MinecraftServer server, GeyserModPlatform platform) { } @Override - protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { ServerConnectionListener connection = this.server.getConnection(); // Find the channel that Minecraft uses to listen to connections diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java index a0ab1c83924..c9500031a89 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java @@ -25,11 +25,13 @@ package org.geysermc.geyser.platform.spigot; -import org.geysermc.geyser.GeyserPluginBootstrap; -import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer; import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.DefaultEventLoopGroup; import io.netty.channel.local.LocalAddress; import io.netty.util.concurrent.DefaultThreadFactory; import org.bukkit.Bukkit; @@ -38,6 +40,7 @@ import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.network.netty.LocalSession; +import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -61,7 +64,7 @@ public GeyserSpigotInjector(boolean isViaVersion) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { Class serverClazz; try { serverClazz = Class.forName("net.minecraft.server.MinecraftServer"); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index f3fbbe7bda9..74364f57e16 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -43,8 +43,8 @@ import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.FloodgateKeyLoader; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.adapters.paper.PaperAdapters; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent; @@ -77,7 +77,7 @@ import java.util.List; import java.util.Objects; -public class GeyserSpigotPlugin extends JavaPlugin implements GeyserPluginBootstrap { +public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { private CommandRegistry commandRegistry; private GeyserPluginConfig geyserConfig; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java index 7039706e5c8..ef56b1bfa10 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityInjector.java @@ -35,7 +35,7 @@ import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.local.LocalAddress; import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.GeyserPluginBootstrap; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; @@ -52,7 +52,7 @@ public GeyserVelocityInjector(ProxyServer proxy) { @Override @SuppressWarnings("unchecked") - protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception { + protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { Field cm = proxy.getClass().getDeclaredField("cm"); cm.setAccessible(true); Object connectionManager = cm.get(proxy); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index a39a7a89121..017087d9077 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -40,8 +40,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.FloodgateKeyLoader; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.CommandRegistry; import org.geysermc.geyser.command.CommandSourceConverter; @@ -67,7 +67,7 @@ import java.util.Optional; @Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC") -public class GeyserVelocityPlugin implements GeyserPluginBootstrap { +public class GeyserVelocityPlugin implements GeyserBootstrap { private final ProxyServer proxyServer; private final PluginContainer container; diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java index 56157c87faf..6030d4f8361 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyPlugin.java @@ -36,9 +36,9 @@ import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; import org.apache.logging.log4j.LogManager; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; -import org.geysermc.geyser.GeyserPluginBootstrap; import org.geysermc.geyser.api.event.EventRegistrar; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.util.PlatformType; @@ -61,7 +61,7 @@ import java.nio.file.Path; import java.util.UUID; -public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserPluginBootstrap, EventRegistrar { +public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, EventRegistrar { private static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser"); diff --git a/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java b/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java deleted file mode 100644 index ed05ec1f31e..00000000000 --- a/core/src/main/java/org/geysermc/geyser/GeyserPluginBootstrap.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2024 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; - -import org.geysermc.geyser.configuration.GeyserPluginConfig; - -/** - * Used in any instance where Geyser is directly attached to a server instance of some sort. - */ -public interface GeyserPluginBootstrap extends GeyserBootstrap { - @Override - GeyserPluginConfig config(); -} diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java index f19f159ba58..d14282e95ab 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserInjector.java @@ -27,7 +27,7 @@ import io.netty.channel.ChannelFuture; import lombok.Getter; -import org.geysermc.geyser.GeyserPluginBootstrap; +import org.geysermc.geyser.GeyserBootstrap; import java.net.SocketAddress; @@ -49,7 +49,7 @@ public abstract class GeyserInjector { /** * @param bootstrap the bootstrap of the Geyser instance. */ - public void initializeLocalChannel(GeyserPluginBootstrap bootstrap) { + public void initializeLocalChannel(GeyserBootstrap bootstrap) { if (!bootstrap.config().advanced().useDirectConnection()) { bootstrap.getGeyserLogger().debug("Disabling direct injection!"); return; @@ -71,9 +71,9 @@ public void initializeLocalChannel(GeyserPluginBootstrap bootstrap) { } /** - * The method to implement that is called by {@link #initializeLocalChannel(GeyserPluginBootstrap)} wrapped around a try/catch. + * The method to implement that is called by {@link #initializeLocalChannel(GeyserBootstrap)} wrapped around a try/catch. */ - protected abstract void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception; + protected abstract void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception; public void shutdown() { if (localChannel != null && localChannel.channel().isOpen()) { From adee6a7929b3cc82c0fb4411b5b59db1b5ffe1be Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:09:06 -0400 Subject: [PATCH 28/42] Remove old config.yml file --- core/src/main/resources/config.yml | 219 ----------------------------- 1 file changed, 219 deletions(-) delete mode 100644 core/src/main/resources/config.yml diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml deleted file mode 100644 index 15d3a20a606..00000000000 --- a/core/src/main/resources/config.yml +++ /dev/null @@ -1,219 +0,0 @@ -# -------------------------------- -# Geyser Configuration File -# -# A bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition. -# -# GitHub: https://github.com/GeyserMC/Geyser -# Discord: https://discord.gg/geysermc -# Wiki: https://wiki.geysermc.org/ -# -# NOTICE: See https://wiki.geysermc.org/geyser/setup/ for the setup guide. Many video tutorials are outdated. -# In most cases, especially with server hosting providers, further hosting-specific configuration is required. -# -------------------------------- - -bedrock: - # The IP address that will listen for connections. - # Generally, you should only uncomment and change this if you want to limit what IPs can connect to your server. - #address: 0.0.0.0 - # The port that will listen for connections - port: 19132 - # Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. - # This option makes the Bedrock port the same as the Java port every time you start the server. - # This option is for the plugin version only. - clone-remote-port: false - # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true - # If either of these are empty, the respective string will default to "Geyser" - motd1: "Geyser" - motd2: "Another Geyser server." - # The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu. - server-name: "Geyser" - # How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but - # the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable. - compression-level: 6 - # The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. - # DO NOT uncomment and change this unless Geyser runs on a different internal port than the one that is used to connect. - # broadcast-port: 19132 - # Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy - # in front of your Geyser instance. - enable-proxy-protocol: false - # A list of allowed PROXY protocol speaking proxy IP addresses/subnets. Only effective when "enable-proxy-protocol" is enabled, and - # should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.). - # Keeping this list empty means there is no IP address whitelist. - # IP addresses, subnets, and links to plain text files are supported. - #proxy-protocol-whitelisted-ips: [ "127.0.0.1", "172.18.0.0/16", "https://example.com/whitelist.txt" ] -remote: - # The IP address of the remote (Java Edition) server - # If it is "auto", for standalone version the remote address will be set to 127.0.0.1, - # for plugin versions, it is recommended to keep this as "auto" so Geyser will automatically configure address, port, and auth-type. - # Leave as "auto" if floodgate is installed. - address: auto - # The port of the remote (Java Edition) server - # For plugin versions, if address has been set to "auto", the port will also follow the server's listening port. - port: 25565 - # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate). - # For plugin versions, it's recommended to keep the `address` field to "auto" so Floodgate support is automatically configured. - # If Floodgate is installed and `address:` is set to "auto", then "auth-type: floodgate" will automatically be used. - auth-type: online - # Whether to enable PROXY protocol or not while connecting to the server. - # This is useful only when: - # 1) Your server supports PROXY protocol (it probably doesn't) - # 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config. - # IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT! - use-proxy-protocol: false - # Forward the hostname that the Bedrock client used to connect over to the Java server - # This is designed to be used for forced hosts on proxies - forward-hostname: false - -# Floodgate uses encryption to ensure use from authorised sources. -# This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity) -# You can ignore this when not using Floodgate. -# If you're using a plugin version of Floodgate on the same server, the key will automatically be picked up from Floodgate. -floodgate-key-file: key.pem - -# For online mode authentication type only. -# Stores a list of Bedrock players that should have their Java Edition account saved after login. -# This saves a token that can be reused to authenticate the player later. This does not save emails or passwords, -# but you should still be cautious when adding to this list and giving others access to this Geyser instance's files. -# Removing a name from this list will delete its cached login information on the next Geyser startup. -# The file that tokens will be saved in is in the same folder as this config, named "saved-refresh-tokens.json". -saved-user-logins: - - ThisExampleUsernameShouldBeLongEnoughToNeverBeAnXboxUsername - - ThisOtherExampleUsernameShouldAlsoBeLongEnough - -# Specify how many seconds to wait while user authorizes Geyser to access their Microsoft account. -# User is allowed to disconnect from the server during this period. -pending-authentication-timeout: 120 - -# Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands. -# Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients. -command-suggestions: true - -# The following three options enable "ping passthrough" - the MOTD, player count and/or protocol name gets retrieved from the Java server. -# Relay the MOTD from the remote server to Bedrock players. -passthrough-motd: true -# Relay the player count and max players from the remote server to Bedrock players. -passthrough-player-counts: true -# Enable LEGACY ping passthrough. There is no need to enable this unless your MOTD or player count does not appear properly. -# This option does nothing on standalone. -legacy-ping-passthrough: false -# How often to ping the remote server, in seconds. Only relevant for standalone or legacy ping passthrough. -# Increase if you are getting BrokenPipe errors. -ping-passthrough-interval: 3 - -# Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate -# ping, it may also cause players to time out more easily. -forward-player-ping: false - -# Maximum amount of players that can connect. This is only visual at this time and does not actually limit player count. -max-players: 100 - -# If debug messages should be sent through console -debug-mode: false - -# Allow a fake cooldown indicator to be sent. Bedrock players otherwise do not see a cooldown as they still use 1.8 combat. -# Please note: if the cooldown is enabled, some users may see a black box during the cooldown sequence, like below: -# https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png -# This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 -# This setting can be set to "title", "actionbar" or "false" -show-cooldown: title - -# Controls if coordinates are shown to players. -show-coordinates: true - -# Whether Bedrock players are blocked from performing their scaffolding-style bridging. -disable-bedrock-scaffolding: false - -# If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind -# There are three options this can be set to: -# disabled - the default/fallback, which doesn't apply this workaround -# no-emotes - emotes will NOT be sent to other Bedrock clients and offhand will be swapped. This effectively disables all emotes from being seen. -# emotes-and-offhand - emotes will be sent to Bedrock clients and offhand will be swapped -emote-offhand-workaround: "disabled" - -# The default locale if we dont have the one the client requested. Uncomment to not use the default system language. -# default-locale: en_us - -# Specify how many days images will be cached to disk to save downloading them from the internet. -# A value of 0 is disabled. (Default: 0) -cache-images: 0 - -# Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices. -allow-custom-skulls: true - -# The maximum number of custom skulls to be displayed per player. Increasing this may decrease performance on weaker devices. -# Setting this to -1 will cause all custom skulls to be displayed regardless of distance or number. -max-visible-custom-skulls: 128 - -# The radius in blocks around the player in which custom skulls are displayed. -custom-skull-render-distance: 32 - -# Whether to add any items and blocks which normally does not exist in Bedrock Edition. -# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching. -# If this is disabled, furnace minecart items will be mapped to hopper minecart items. -# Geyser's block, item, and skull mappings systems will also be disabled. -# This option requires a restart of Geyser in order to change its setting. -add-non-bedrock-items: true - -# Bedrock prevents building and displaying blocks above Y127 in the Nether. -# This config option works around that by changing the Nether dimension ID to the End ID. -# The main downside to this is that the entire Nether will have the same red fog rather than having different fog for each biome. -above-bedrock-nether-building: false - -# Force clients to load all resource packs if there are any. -# If set to false, it allows the user to connect to the server even if they don't -# want to download the resource packs. -force-resource-packs: true - -# Allows Xbox achievements to be unlocked. -xbox-achievements-enabled: false - -# Whether player IP addresses will be logged by the server. -log-player-ip-addresses: true - -# Whether to alert the console and operators that a new Geyser version is available that supports a Bedrock version -# that this Geyser version does not support. It's recommended to keep this option enabled, as many Bedrock platforms -# auto-update. -notify-on-new-bedrock-update: true - -# Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, -# or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item. -unusable-space-block: minecraft:barrier - -# bStats is a stat tracker that is entirely anonymous and tracks only basic information -# about Geyser, such as how many people are online, how many servers are using Geyser, -# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. -# https://bstats.org/plugin/server-implementation/GeyserMC -metrics: - # If metrics should be enabled - enabled: true - # UUID of server, don't change! - uuid: generateduuid - -# ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING! - -# Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle -# a lot of scoreboard packets per second can cause serious lag. -# This option allows you to specify after how many Scoreboard packets per seconds -# the Scoreboard updates will be limited to four updates per second. -scoreboard-packet-threshold: 20 - -# Allow connections from ProxyPass and Waterdog. -# See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP. -enable-proxy-connections: false - -# The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation. -# 1400 is the default. -mtu: 1400 - -# Whether to connect directly into the Java server without creating a TCP connection. -# This should only be disabled if a plugin that interfaces with packets or the network does not work correctly with Geyser. -# If enabled on plugin versions, the remote address and port sections are ignored -# If disabled on plugin versions, expect performance decrease and latency increase -use-direct-connection: true - -# Whether Geyser should attempt to disable compression for Bedrock players. This should be a benefit as there is no need to compress data -# when Java packets aren't being handled over the network. -# This requires use-direct-connection to be true. -disable-compression: true - -config-version: 4 From 869f7a8966aa9432c84543d083df9d71bab0a2e4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:29:29 -0400 Subject: [PATCH 29/42] Update Xbox achievement comment --- .../java/org/geysermc/geyser/configuration/GeyserConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index f3a98db10b3..10e629581de 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -160,7 +160,8 @@ default CooldownUtils.CooldownType showCooldown() { @SuppressWarnings("BooleanMethodIsAlwaysInverted") @Comment(""" Allows Xbox achievements to be unlocked. - THIS DISABLES ALL COMMANDS FROM SUCCESSFULLY RUNNING FOR BEDROCK IN-GAME, as otherwise Bedrock thinks you are cheating.""") + If a player types in an unknown command, they will receive a message that states cheats are disabled. + Otherwise, commands work as expected.""") boolean xboxAchievementsEnabled(); @Comment("Whether player IP addresses will be logged by the server.") From 3448c2fa9b867b5df0e12448010b45195d37c2f1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:57:32 -0400 Subject: [PATCH 30/42] Apply suggestions from code review Co-authored-by: chris --- .../org/geysermc/geyser/configuration/AdvancedConfig.java | 2 +- .../java/org/geysermc/geyser/configuration/GeyserConfig.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index e19b896e04d..b60c8d986b4 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -68,7 +68,7 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o @Comment(""" Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle - a lot of scoreboard packets per second can cause serious lag. + a lot of scoreboard packets per second, this can cause serious lag. This option allows you to specify after how many Scoreboard packets per seconds the Scoreboard updates will be limited to four updates per second.""") @DefaultNumeric(20) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 10e629581de..0f42dea2eb2 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -53,7 +53,7 @@ public interface GeyserConfig { @Comment(""" For online mode authentication type only. - Stores a list of Bedrock players that should have their Java Edition account saved after login. + Stores a list of Bedrock player usernames that should have their Java Edition account saved after login. This saves a token that can be reused to authenticate the player later. This does not save emails or passwords, but you should still be cautious when adding to this list and giving others access to this Geyser instance's files. Removing a name from this list will delete its cached login information on the next Geyser startup. @@ -281,7 +281,7 @@ interface JavaConfig extends RemoteServer { @Override @Comment(""" What type of authentication Bedrock players will be checked against when logging into the Java server. - Can be floodgate (see https://wiki.geysermc.org/floodgate/), online, or offline.""") + Can be "floodgate" (see https://wiki.geysermc.org/floodgate/), "online", or "offline".""") @NonNull default AuthType authType() { return AuthType.ONLINE; From 2ee488d995f68c75bce486ee65d694e3199063ab Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:58:20 -0400 Subject: [PATCH 31/42] No need to remove values anymore --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 6 +++--- .../org/geysermc/geyser/configuration/AdvancedConfig.java | 2 +- .../org/geysermc/geyser/configuration/ConfigLoader.java | 7 ------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index c426015d435..afb1c9cb72e 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -36,13 +36,13 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; +import net.raphimc.minecraftauth.step.msa.StepMsaToken; import org.bstats.MetricsBase; import org.bstats.charts.AdvancedPie; import org.bstats.charts.DrilldownPie; import org.bstats.charts.SimplePie; import org.bstats.charts.SingleLineChart; -import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; -import net.raphimc.minecraftauth.step.msa.StepMsaToken; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -71,8 +71,8 @@ import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.api.util.PlatformType; -import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.command.CommandRegistry; +import org.geysermc.geyser.configuration.GeyserConfig; import org.geysermc.geyser.configuration.GeyserPluginConfig; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.erosion.UnixSocketClientListener; diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index b60c8d986b4..f586b42aec0 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -56,7 +56,7 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o int customSkullRenderDistance(); @Comment(""" - Specify how many days images will be cached to disk to save downloading them from the internet. + Specify how many days player skin images will be cached to disk to save downloading them from the internet. A value of 0 is disabled. (Default: 0)""") int cacheImages(); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 2639672af13..535be88d6fb 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -138,7 +138,6 @@ private T load0(Class configClass) throws IOExceptio CommentedConfigurationNode node = loader.load(); boolean originallyEmpty = !configFile.exists() || node.isNull(); - // Note for Tim? Needed or else Configurate breaks. var migrations = ConfigurationTransformation.versionedBuilder() .versionKey("config-version") // Pre-Configurate @@ -192,12 +191,6 @@ private T load0(Class configClass) throws IOExceptio }) .addAction(path("bedrock", "motd1"), rename("primary-motd")) .addAction(path("bedrock", "motd2"), rename("secondary-motd")) - // Legacy config values - .addAction(path("emote-offhand-workaround"), remove()) - .addAction(path("allow-third-party-capes"), remove()) - .addAction(path("allow-third-party-ears"), remove()) - .addAction(path("general-thread-pool"), remove()) - .addAction(path("cache-chunks"), remove()) .build()) .build(); From a44edb3b62392cb5016fee9aa5981b08242ab48f Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Tue, 24 Sep 2024 21:37:25 +0800 Subject: [PATCH 32/42] Fix: disable bstats relocation on platforms where it is not needed --- bootstrap/mod/fabric/build.gradle.kts | 4 +--- .../geyser/platform/mod/GeyserModBootstrap.java | 1 - .../mod/mixin/client/IntegratedServerMixin.java | 4 ++-- .../platform/standalone/GeyserStandaloneBootstrap.java | 2 -- .../src/main/kotlin/geyser.base-conventions.gradle.kts | 3 +++ .../geysermc/geyser/util/metrics/MetricsPlatform.java | 10 ++++++++++ gradle/libs.versions.toml | 2 +- 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/bootstrap/mod/fabric/build.gradle.kts b/bootstrap/mod/fabric/build.gradle.kts index 734b0622fb8..fc18d0e7446 100644 --- a/bootstrap/mod/fabric/build.gradle.kts +++ b/bootstrap/mod/fabric/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { shadow(projects.core) { isTransitive = false } includeTransitive(projects.core) - // These are NOT transitively included, and instead shadowed + relocated. + // These are NOT transitively included, and instead shadowed (+ relocated, if not under the org.geyser namespace). // Avoids fabric complaining about non-SemVer versioning shadow(libs.protocol.connection) { isTransitive = false } shadow(libs.protocol.common) { isTransitive = false } @@ -46,8 +46,6 @@ tasks.withType { relocate("org.cloudburstmc.netty") relocate("org.cloudburstmc.protocol") -relocate("com.github.steveice10.mc.auth") -platformRelocate("org.bstats") tasks { remapJar { diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java index f229c49745c..879ecc76f5f 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/GeyserModBootstrap.java @@ -199,7 +199,6 @@ public SocketAddress getSocketAddress() { @Override public int getServerPort() { - // TODO test return ((GeyserServerPortGetter) server).geyser$getServerPort(); } diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java index 7a4abd50ea0..8f218105f7f 100644 --- a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/client/IntegratedServerMixin.java @@ -61,8 +61,8 @@ private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, Cal GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode); // Give indication that Geyser is loaded Objects.requireNonNull(this.minecraft.player); - this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start", - this.minecraft.options.languageCode, "localhost", String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false); + this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start.ip_suppressed", + this.minecraft.options.languageCode, String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false); } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java index a3a17bb9963..9a55d5fff2b 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneBootstrap.java @@ -82,8 +82,6 @@ public static void main(String[] args) { ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance } - System.setProperty("bstats.relocatecheck", "false"); - GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); // Set defaults boolean useGuiOpts = bootstrap.useGui; diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 093f0a8c098..04803753b88 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -62,6 +62,9 @@ repositories { name = "viaversion" } + // MinecraftAuth + maven("https://maven.lenni0451.net/snapshots") + // Jitpack for e.g. MCPL maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } diff --git a/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java b/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java index c4f3b47c207..bb582df897c 100644 --- a/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java +++ b/core/src/main/java/org/geysermc/geyser/util/metrics/MetricsPlatform.java @@ -25,6 +25,9 @@ package org.geysermc.geyser.util.metrics; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.api.util.PlatformType; + public interface MetricsPlatform { boolean enabled(); @@ -35,4 +38,11 @@ public interface MetricsPlatform { boolean logSentData(); boolean logResponseStatusText(); + + default boolean disableRelocateCheck() { + PlatformType platformType = GeyserImpl.getInstance().platformType(); + return platformType == PlatformType.FABRIC || + platformType == PlatformType.NEOFORGE || + platformType == PlatformType.STANDALONE; + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f8f68f5ad62..2262f136990 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] base-api = "1.0.1" -bstats = "3.0.2" +bstats = "3.1.0" cumulus = "1.1.2" configurate = "4.2.0-GeyserMC-SNAPSHOT" erosion = "1.1-20240521.000109-3" From 02a361edaa961a331dcaf19b970160e8adcc6151 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Tue, 24 Sep 2024 21:45:37 +0800 Subject: [PATCH 33/42] ensure it builds --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index afb1c9cb72e..6d81b54cfc2 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -492,7 +492,9 @@ private void startInstance() { logger::info, metricsPlatform.logFailedRequests(), metricsPlatform.logSentData(), - metricsPlatform.logResponseStatusText()); + metricsPlatform.logResponseStatusText(), + metricsPlatform.disableRelocateCheck() + ); metrics.addCustomChart(new SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT))); From 0d9b20a20d858c54719aa8691db3ebec221461b8 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:53:31 -0400 Subject: [PATCH 34/42] Update custom unavailable slot comment Co-authored-by: chris --- .../org/geysermc/geyser/configuration/AdvancedConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java index f586b42aec0..de3db005f96 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/AdvancedConfig.java @@ -62,7 +62,9 @@ This should point to the public key generated by Floodgate (BungeeCord, Spigot o @Comment(""" Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative, - or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.""") + or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item. + This config option can be set to any Bedrock item identifier. If you want to set this to a custom item, make sure that you specify the item in the following format: "geyser_custom:" + """) @DefaultString("minecraft:barrier") String unusableSpaceBlock(); From bfd032d6851713688dfa2cb93fe3dc318230d4dd Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:09:18 -0400 Subject: [PATCH 35/42] Update cooldown image --- .../java/org/geysermc/geyser/configuration/GeyserConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 0f42dea2eb2..5dadce9cd50 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -112,7 +112,7 @@ default List savedUserLogins() { @Comment(""" Allow a fake cooldown indicator to be sent. Bedrock players otherwise do not see a cooldown as they still use 1.8 combat. Please note: if the cooldown is enabled, some users may see a black box during the cooldown sequence, like below: - https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png + https://geysermc.org/img/external/cooldown_indicator.png This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 This setting can be set to "title", "actionbar" or "false\"""") default CooldownUtils.CooldownType showCooldown() { From fc10ec265fa55ff70ef770373a7f1f22e6de1048 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:13:36 -0400 Subject: [PATCH 36/42] Logger message for direct-compression still being enabled --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 2 ++ .../java/org/geysermc/geyser/configuration/ConfigLoader.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 6d81b54cfc2..3381878ff1a 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -405,6 +405,8 @@ private void startInstance() { logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); } } + } else if (!config.advanced().useDirectConnection()) { + logger.warning("The use-direct-connection config option is deprecated. Please reach out to us on Discord if there's a reason it needs to be enabled."); } // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves diff --git a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java index 535be88d6fb..68c4c2dd8c3 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/ConfigLoader.java @@ -209,7 +209,7 @@ private T load0(Class configClass) throws IOExceptio // Create the path in a way that Standalone changing the config name will be fine. int extensionIndex = configFile.getName().lastIndexOf("."); - File advancedConfigPath = new File(configFile.getParent(), configFile.getName().substring(0, extensionIndex) + "_advanced" + configFile.getName().substring(extensionIndex)); + File advancedConfigPath = new File(configFile.getParent(), configFile.getName().substring(0, extensionIndex) + "-advanced" + configFile.getName().substring(extensionIndex)); AdvancedConfig advancedConfig = null; if (originallyEmpty || currentVersion != newVersion) { From 8b43498faa1caf90ead41dbb8dea7524bbc4a69f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 25 Sep 2024 19:18:57 -0400 Subject: [PATCH 37/42] oops --- core/src/main/java/org/geysermc/geyser/GeyserImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 3381878ff1a..e9a9b56f963 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -406,7 +406,7 @@ private void startInstance() { } } } else if (!config.advanced().useDirectConnection()) { - logger.warning("The use-direct-connection config option is deprecated. Please reach out to us on Discord if there's a reason it needs to be enabled."); + logger.warning("The use-direct-connection config option is deprecated. Please reach out to us on Discord if there's a reason it needs to be disabled."); } // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves From 0a625627c929028f10d39cafd661d90e9920845b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:07:57 -0400 Subject: [PATCH 38/42] More explicit RuntimeException message Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> --- .../main/java/org/geysermc/geyser/text/AsteriskSerializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java index b34db2b99c5..ec79fb9060c 100644 --- a/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java +++ b/core/src/main/java/org/geysermc/geyser/text/AsteriskSerializer.java @@ -79,7 +79,7 @@ private static boolean isSensitiveIp(String ip) { try { destination.set("***"); } catch (SerializationException e) { - throw new RuntimeException(e); // Error over silently printing an IP address. + throw new RuntimeException("Unable to censor IP address", e); // Error over silently printing an IP address. } }; From c4bed6509c081d8b5e07fb969fb7feabc57abec9 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:28:39 -0400 Subject: [PATCH 39/42] Constant for 'system' locale --- .../java/org/geysermc/geyser/configuration/GeyserConfig.java | 3 ++- core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index 5dadce9cd50..bba928f3e3d 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -32,6 +32,7 @@ import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.AsteriskSerializer; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.CooldownUtils; import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.Field; @@ -128,7 +129,7 @@ default CooldownUtils.CooldownType showCooldown() { @Comment("The default locale if we don't have the one the client requested. If set to \"system\", the system's language will be used.") @NonNull - @DefaultString("system") + @DefaultString(GeyserLocale.SYSTEM_LOCALE) String defaultLocale(); @Comment("Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices.") diff --git a/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java b/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java index ad9e05342c5..acef5cc987f 100644 --- a/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/GeyserLocale.java @@ -41,6 +41,7 @@ import java.util.Properties; public class GeyserLocale { + public static final String SYSTEM_LOCALE = "system"; /** * If we determine the default locale that the user wishes to use, use that locale @@ -81,7 +82,7 @@ public static void init(GeyserBootstrap bootstrap) { */ public static void finalizeDefaultLocale(GeyserImpl geyser) { String newDefaultLocale = geyser.config().defaultLocale(); - if ("system".equals(newDefaultLocale)) { + if (SYSTEM_LOCALE.equals(newDefaultLocale)) { // We want to use the system locale which is already loaded return; } From b2ea5792be3ed103b8a14726f8034bbebd64397a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:39:09 -0400 Subject: [PATCH 40/42] Better config JSON encoding (something is broken with Cloudflare; we'll figure it out --- core/build.gradle.kts | 4 +- .../geyser/command/defaults/DumpCommand.java | 4 +- .../org/geysermc/geyser/dump/DumpInfo.java | 41 +++++++++++++++++-- .../configuration/ConfigLoaderTest.java | 1 - 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index f4f935b9669..aa4ae3feff1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -32,7 +32,9 @@ dependencies { api(libs.bundles.protocol) - api(libs.minecraftauth) + api(libs.minecraftauth) { + exclude("com.google.code.gson", "gson") + } api(libs.mcprotocollib) { exclude("io.netty", "netty-all") exclude("net.raphimc", "MinecraftAuth") diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java index f25c245f940..6a71ca6955d 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/DumpCommand.java @@ -28,7 +28,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.command.GeyserCommand; @@ -37,6 +36,7 @@ import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.WebUtils; import org.incendo.cloud.CommandManager; import org.incendo.cloud.context.CommandContext; @@ -145,7 +145,7 @@ public void execute(CommandContext context) { JsonObject responseNode; try { response = WebUtils.post(DUMP_URL + "documents", dumpData); - responseNode = (JsonObject) new JsonParser().parse(response); + responseNode = JsonUtils.parseJson(response); } catch (IOException e) { source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", source.locale())); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); diff --git a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java index acb326d7088..e4d71850f0d 100644 --- a/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java +++ b/core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java @@ -28,18 +28,22 @@ import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; import com.google.common.io.Files; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import com.google.gson.annotations.SerializedName; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.extension.Extension; -import org.geysermc.geyser.configuration.AdvancedConfig; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.AsteriskSerializer; @@ -106,12 +110,13 @@ public DumpInfo(GeyserImpl geyser, boolean addLog) { ConfigurationNode configNode = CommentedConfigurationNode.root(options); configNode.set(geyser.config()); - this.config = configNode.get(geyser.config().getClass()); + this.config = toGson(configNode); ConfigurationNode advancedConfigNode = CommentedConfigurationNode.root(options); advancedConfigNode.set(geyser.config().advanced()); - this.advancedConfig = advancedConfigNode.get(AdvancedConfig.class); + this.advancedConfig = toGson(advancedConfigNode); } catch (SerializationException e) { + e.printStackTrace(); if (geyser.config().debugMode()) { e.printStackTrace(); } @@ -164,6 +169,36 @@ public DumpInfo(GeyserImpl geyser, boolean addLog) { } } + private JsonElement toGson(ConfigurationNode node) { + if (node.isMap()) { + JsonObject object = new JsonObject(); + node.childrenMap().forEach((key, value) -> { + JsonElement json = toGson(value); + object.add(key.toString(), json); + }); + return object; + } else if (node.isList()) { + JsonArray array = new JsonArray(); + node.childrenList().forEach(childNode -> array.add(toGson(childNode))); + return array; + } else { + return convertRawScalar(node); + } + } + + private JsonElement convertRawScalar(ConfigurationNode node) { + final @Nullable Object value = node.rawScalar(); + if (value == null) { + return JsonNull.INSTANCE; + } else if (value instanceof Number n) { + return new JsonPrimitive(n); + } else if (value instanceof Boolean b) { + return new JsonPrimitive(b); + } else { + return new JsonPrimitive(value.toString()); + } + } + @Getter public static class VersionInfo { private final String name; diff --git a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java index 098dce7dc52..07242786404 100644 --- a/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java +++ b/core/src/test/java/org/geysermc/geyser/configuration/ConfigLoaderTest.java @@ -53,7 +53,6 @@ void testCreateNewConfig() throws Exception { File file = tempDirectory.resolve("config.yml").toFile(); - // Sorry Konicai... forAllConfigs(type -> { new ConfigLoader(file).transformer(n -> this.config1 = n.copy()).load(type); long initialModification = file.lastModified(); From c427630a0136c1a4bccbc8c2778f9757c7318475 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:44:43 -0400 Subject: [PATCH 41/42] Fix broadcast port default --- .../java/org/geysermc/geyser/configuration/GeyserConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index bba928f3e3d..050a511f667 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -216,7 +216,7 @@ interface BedrockConfig extends BedrockListener { @Comment(""" The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. DO NOT change this unless Geyser runs on a different internal port than the one that is used to connect.""") - @DefaultNumeric(19132) + @DefaultNumeric(0) @NumericRange(from = 0, to = 65535) int broadcastPort(); From 8b518837cd1c7c07d32da0a837a35a2ed2906875 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:50:51 -0500 Subject: [PATCH 42/42] Add this file too --- bootstrap/mod/neoforge/build.gradle.kts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bootstrap/mod/neoforge/build.gradle.kts b/bootstrap/mod/neoforge/build.gradle.kts index 23f16d64ef8..4ab005b4fa9 100644 --- a/bootstrap/mod/neoforge/build.gradle.kts +++ b/bootstrap/mod/neoforge/build.gradle.kts @@ -13,9 +13,6 @@ architectury { provided("org.cloudburstmc.math", "api") provided("com.google.errorprone", "error_prone_annotations") -// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version -relocate("com.fasterxml.jackson") - val includeTransitive: Configuration = configurations.getByName("includeTransitive") dependencies { @@ -34,12 +31,6 @@ dependencies { } shadow(projects.core) { isTransitive = false } - // Minecraft (1.21.2+) includes jackson. But an old version! - shadow(libs.jackson.core) { isTransitive = false } - shadow(libs.jackson.databind) { isTransitive = false } - shadow(libs.jackson.dataformat.yaml) { isTransitive = false } - shadow(libs.jackson.annotations) { isTransitive = false } - // Let's shade in our own api shadow(projects.api) { isTransitive = false } @@ -65,11 +56,6 @@ tasks { remapModrinthJar { archiveBaseName.set("geyser-neoforge") } - - shadowJar { - // Without this, jackson's service files are not relocated - mergeServiceFiles() - } } modrinth {