1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Switch config system to Configurate (#5010)

* Start implementing Configurate config system

* More development

* Start migrating to Gson only

* Progress

* Update usage of WebUtils

* Most things now use Gson for JSON

* Allow tests to succeed by using new Gson version

* Use slightly cleaner version for Version deserializer

* Work around older Gson versions without record support

* GeyserCustomSkullConfiguration uses Configurate

* Fix regression in properties get

* New config used in core

* The configuration is gone. Long live the config.

* More changes and migrations away from Jackson

* Improve node ordering when updating configs

* typo

* Better check for ignoring non-configurate configs for considering comment moving

* Ensure metrics UUID is valid

* Initial advanced config

* Remove Jackson; finish config value placements

* Remove duplicate relocate declarations

* Let annotations work

* Renaming to PluginSpecific

* Use global bStats config where possible

* Fix test

* Re-introduce asterisk behavior in configs

* Remove GeyserPluginBootstrap as it's no longer necessary

* Remove old config.yml file

* Update Xbox achievement comment

* Apply suggestions from code review

Co-authored-by: chris <github@onechris.mozmail.com>

* No need to remove values anymore

* Fix: disable bstats relocation on platforms where it is not needed

* ensure it builds

* Update custom unavailable slot comment

Co-authored-by: chris <github@onechris.mozmail.com>

* Update cooldown image

* Logger message for direct-compression still being enabled

* oops

* More explicit RuntimeException message

Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com>

* Constant for 'system' locale

* Better config JSON encoding (something is broken with Cloudflare; we'll figure it out

* Fix broadcast port default

* Add this file too

* Update configurate branch

* fix build

* Fix: Allow using custom config file on Standalone, add relocation comment

* Move config loading to GeyserBootstrap interface

* Add and rename some config options, add section notes (#5390)

* Add and rename some config options, add section notes

* adjust message

* Update core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java

Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz>

* Update core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java

Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz>

* Update ConfigLoader.java

* Update AdvancedConfig.java

* clarify that we're talking about the HAProxy protocol

* rename config option to use-haproxy-protocol

* remove ominous warning sign on xbox auth warning

* adjust wording

---------

Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz>

* Back to one config file

* Some minor touchups

* Configurate: Sectionification (#5904)

* Init: config sections

* Start on adding tests, move migrations over to ConfigMigrations class

* Get rid of auth section again, rename that one config option, fix mtu migration

* Move custom skulls config options to the bottom of the gameplay settings

* Add more tests

* Rename and migrate proxy-protocol-whitelisted-ips to haproxy-protocol-whitelisted-ips

* Add automatic downloading of the GeyserOptionalPack

* Revert "Add automatic downloading of the GeyserOptionalPack"

This reverts commit 65b96208fb.

* Add more invalid config tests

* Warn about emote-offhand-workaround removal

* Add automatic loading of the GeyserOptionalPack (feature/configurate) (#5964)

* Add automatic downloading of the GeyserOptionalPack

* Warn about including the OptionalPack from extensions when Geyser is already including it instead of throwing.

* Copy optional pack instead of downloading

---------

Co-authored-by: onebeastchris <github@onechris.mozmail.com>

* Remove unused variable

* Start warning users not running Java 21

* Update tests, temporarily remove NumericRanges test

* Remove duplicate advanced section from Geyser dump

* Address some "reviews"

* yeet md5 hash from geyser dump

* Add info about number of resource packs / amount of mappings into Geyser dump

* Re-enable invalid config loading test

* Fix: allow-custom-skulls migration

* Fix test

* Add "enable-emotes" configuration option

* Rename "emotes-enabled" to "show-emotes"

* Only enable integrated pack when optional pack isn't present

* Update integrated pack

* Exclude jackson annotations, remove leftover debug print

* Remove one-time config migration warnings as we don't have access to the logger at that stage

* Throw more detailed descriptive error when loading resource packs from the "packs" folder, add another legacy config test

* Fix NeoForge's fun module conflict

* Re-add warning about moved functionality, fix Geyser-ViaProxy

This reverts commit fbadfa574a.

* oops

* Move GeyserLegacyPingPassthrough to separate thread to avoid Standalone command locking issues

---------

Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com>
Co-authored-by: chris <github@onechris.mozmail.com>
Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz>
Co-authored-by: Aurora <auroranova8756@gmail.com>
This commit is contained in:
Camotoy
2025-11-18 11:55:12 -05:00
committed by GitHub
parent 48bf0942e9
commit 765128ce42
157 changed files with 8006 additions and 2670 deletions

View File

@@ -15,11 +15,12 @@ dependencies {
}
platformRelocate("net.md_5.bungee.jni")
platformRelocate("com.fasterxml.jackson")
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("org.bstats")
// These dependencies are already present on the platform
provided(libs.bungeecord.proxy)

View File

@@ -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();
}
}
}
}

View File

@@ -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());
}
}

View File

@@ -157,7 +157,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
listenerInfo.isPingPassthrough(),
listenerInfo.getQueryPort(),
listenerInfo.isQueryEnabled(),
bootstrap.getGeyserConfig().getRemote().isUseProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end
bootstrap.config().advanced().java().useHaproxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end
);
// The field that stores all listeners in BungeeCord
@@ -191,7 +191,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
}
initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression()) {
if (bootstrap.config().advanced().java().disableCompression()) {
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
new GeyserBungeeCompressionDisabler());
}

View File

@@ -69,7 +69,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
try {
event = future.get(100, TimeUnit.MILLISECONDS);
} catch (Throwable cause) {
String address = GeyserImpl.getInstance().getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
String address = GeyserImpl.getInstance().config().logPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
GeyserImpl.getInstance().getLogger().error("Failed to get ping information for " + address, cause);
return null;
}

View File

@@ -33,25 +33,25 @@ import net.md_5.bungee.api.plugin.Plugin;
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.FloodgateKeyLoader;
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.CommandSourceConverter;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.GeyserConfiguration;
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.bungeecord.command.BungeeCommandSource;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
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.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
@@ -61,13 +61,12 @@ import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
private CommandRegistry commandRegistry;
private GeyserBungeeConfiguration geyserConfig;
private GeyserPluginConfig geyserConfig;
private GeyserBungeeInjector geyserInjector;
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
private IGeyserPingPassthrough geyserBungeePingPassthrough;
@@ -102,12 +101,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
System.setProperty("Mcpl.io_uring", "true");
}
if (!this.loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
this.geyser = GeyserImpl.load(this);
this.geyserInjector = new GeyserBungeeInjector(this);
// Registration of listeners occurs only once
@@ -168,16 +166,15 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
public void onGeyserEnable() {
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
}
// 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);
@@ -195,7 +192,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
if (!geyserConfig.motd().integratedPingPassthrough()) {
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
@@ -232,8 +229,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
}
@Override
public GeyserBungeeConfiguration getGeyserConfig() {
return geyserConfig;
public @NonNull PlatformType platformType() {
return PlatformType.BUNGEECORD;
}
@Override
public GeyserPluginConfig config() {
return this.geyserConfig;
}
@Override
@@ -290,11 +292,29 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
@Override
public boolean testFloodgatePluginPresent() {
if (getProxy().getPluginManager().getPlugin("floodgate") != null) {
geyserConfig.loadFloodgate(this);
return true;
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);
}
@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;
}
return false;
}
private Optional<InetSocketAddress> findCompatibleListener() {
@@ -303,20 +323,4 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
.map(info -> (InetSocketAddress) info.getSocketAddress())
.findFirst();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
try {
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);
} catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
}
return true;
}
}

View File

@@ -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(Permissions.CHECK_UPDATE)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));

View File

@@ -17,7 +17,7 @@ dependencies {
shadowBundle(projects.core)
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
shadowBundle(libs.protocol.connection)
shadowBundle(libs.protocol.common)

View File

@@ -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;

View File

@@ -73,7 +73,7 @@ public class GeyserFabricPlatform implements GeyserModPlatform {
Optional<ModContainer> 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;
}

View File

@@ -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")
dependencies {
// See https://github.com/google/guava/issues/6618
modules {
@@ -30,15 +27,12 @@ dependencies {
shadowBundle(project(path = ":mod", configuration = "transformProductionNeoForge"))
shadowBundle(projects.core)
// Minecraft (1.21.2+) includes jackson. But an old version!
shadowBundle(libs.jackson.core)
shadowBundle(libs.jackson.databind)
shadowBundle(libs.jackson.dataformat.yaml)
shadowBundle(libs.jackson.annotations)
// Let's shade in our own api
shadowBundle(projects.api)
// this one is particularly dumb
shadowBundle(libs.configurate.`interface`)
// cannot be shaded, since neoforge will complain if floodgate-neoforge tries to provide this
include(projects.common)
@@ -61,11 +55,6 @@ tasks {
remapModrinthJar {
archiveBaseName.set("geyser-neoforge")
}
shadowJar {
// Without this, jackson's service files are not relocated
mergeServiceFiles()
}
}
modrinth {

View File

@@ -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;

View File

@@ -72,7 +72,7 @@ public class GeyserNeoForgePlatform implements GeyserModPlatform {
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;

View File

@@ -31,11 +31,13 @@ import lombok.Setter;
import net.minecraft.server.MinecraftServer;
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.api.util.PlatformType;
import org.geysermc.geyser.command.CommandRegistry;
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.ping.GeyserLegacyPingPassthrough;
@@ -43,14 +45,10 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
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.UUID;
@RequiredArgsConstructor
public abstract class GeyserModBootstrap implements GeyserBootstrap {
@@ -69,7 +67,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
@Setter
private CommandRegistry commandRegistry;
private GeyserModConfiguration geyserConfig;
private GeyserPluginConfig geyserConfig;
private GeyserModInjector geyserInjector;
private final GeyserModLogger geyserLogger = new GeyserModLogger();
private IGeyserPingPassthrough geyserPingPassthrough;
@@ -80,12 +78,11 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
instance = this;
dataFolder = this.platform.dataFolder(this.platform.configPath());
GeyserLocale.init(this);
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
this.geyser = GeyserImpl.load(this);
}
public void onGeyserEnable() {
@@ -95,16 +92,15 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
}
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
if (!geyserConfig.motd().integratedPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
@@ -145,7 +141,12 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
}
@Override
public GeyserModConfiguration getGeyserConfig() {
public @NonNull PlatformType platformType() {
return this.platform.platformType();
}
@Override
public GeyserPluginConfig config() {
return geyserConfig;
}
@@ -199,12 +200,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
@Override
public int getServerPort() {
if (isServer()) {
return ((GeyserServerPortGetter) server).geyser$getServerPort();
} else {
// Set in the IntegratedServerMixin
return geyserConfig.getRemote().port();
}
return ((GeyserServerPortGetter) server).geyser$getServerPort();
}
public abstract boolean isServer();
@@ -214,31 +210,23 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
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) {
return this.platform.resolveResource(resource);
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
try {
if (!dataFolder.toFile().exists()) {
//noinspection ResultOfMethodCallIgnored
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);
return true;
} catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
}
}
@Override
public @NonNull String getServerPlatform() {
return server.getServerModName();

View File

@@ -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;
}
}

View File

@@ -96,7 +96,7 @@ public class GeyserModInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression()) {
if (bootstrap.config().advanced().java().disableCompression()) {
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
}
}
@@ -125,7 +125,7 @@ public class GeyserModInjector extends GeyserInjector {
childHandler = (ChannelInitializer<Channel>) 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();
}

View File

@@ -34,7 +34,7 @@ import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserModUpdateListener {
public static void onPlayReady(ServerPlayer player) {
// We could just not register the listener, but, this allows config reloading
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
// Should be creating this in the supplier, but we need it for the permission check.
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());

View File

@@ -56,14 +56,13 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
// 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);
// 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);
}
}

View File

@@ -31,17 +31,25 @@ dependencies {
compileOnly(libs.folia.api)
compileOnlyApi(libs.viaversion)
// For 1.16.5/1.17.1
implementation(libs.gson.record.factory) {
isTransitive = false
}
}
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")
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("marcono1234.gson")
platformRelocate("org.bstats")
provided(libs.viaversion)
tasks.withType<Jar> {

View File

@@ -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());
}
}

View File

@@ -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<PluginInfo> plugins;

View File

@@ -25,11 +25,13 @@
package org.geysermc.geyser.platform.spigot;
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
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,8 @@ import org.geysermc.geyser.GeyserBootstrap;
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.MinecraftConstants;
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -123,7 +127,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
if (bootstrap.config().advanced().java().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
}
}
@@ -158,7 +162,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
}
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();
}
@@ -178,8 +182,8 @@ public class GeyserSpigotInjector extends GeyserInjector {
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
MinecraftProtocol protocol = new MinecraftProtocol();
LocalSession session = new LocalSession(this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.getGeyserConfig().getRemote().address());
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.getGeyserConfig().getRemote().port());
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.config().java().address());
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.config().java().port());
session.connect();
}

View File

@@ -38,9 +38,11 @@ import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerLoadEvent;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
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.adapters.paper.PaperAdapters;
@@ -50,7 +52,7 @@ 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.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;
@@ -64,23 +66,20 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotLegacyNativ
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 org.geysermc.geyser.util.metrics.MetricsPlatform;
import org.incendo.cloud.bukkit.BukkitCommandManager;
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;
import java.util.Objects;
import java.util.UUID;
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
private CommandRegistry commandRegistry;
private GeyserSpigotConfiguration geyserConfig;
private GeyserPluginConfig geyserConfig;
private GeyserSpigotInjector geyserInjector;
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
@@ -161,16 +160,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
// no-op
}
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
// We'll disable ourselves later
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// 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
@@ -226,16 +225,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
// Configs are loaded once early - so we can create the logger, then load extensions and finally register
// extension commands in #onEnable. To ensure reloading geyser also reloads the geyser config, this exists
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
Bukkit.getPluginManager().disablePlugin(this);
return;
}
this.geyserLogger.setDebug(this.geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
if (!geyserConfig.motd().integratedPingPassthrough()) {
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
if (ReflectedNames.checkPaperPingEvent()) {
@@ -289,7 +288,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
}
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();
}
@@ -363,8 +362,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
}
@Override
public GeyserSpigotConfiguration getGeyserConfig() {
return geyserConfig;
public @NonNull PlatformType platformType() {
return PlatformType.SPIGOT;
}
@Override
public GeyserPluginConfig config() {
return this.geyserConfig;
}
@Override
@@ -455,32 +459,21 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
@Override
public boolean testFloodgatePluginPresent() {
if (Bukkit.getPluginManager().getPlugin("floodgate") != null) {
geyserConfig.loadFloodgate(this);
return true;
}
return false;
return Bukkit.getPluginManager().getPlugin("floodgate") != null;
}
@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();
}
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);
} catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return false;
}
@Override
public Path getFloodgateKeyPath() {
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate");
Path geyserDataFolder = getDataFolder().toPath();
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
return true;
return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger);
}
@Override
public MetricsPlatform createMetricsPlatform() {
return new SpigotMetrics(this);
}
private void warnInvalidProxySetups(String platform) {

View File

@@ -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(Permissions.CHECK_UPDATE)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));

View File

@@ -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);
}
}

View File

@@ -15,6 +15,8 @@ dependencies {
implementation(libs.bundles.jline)
implementation(libs.bundles.log4j)
implementation(libs.gson.runtime)
}
application {

View File

@@ -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 org.apache.logging.log4j.Level;
@@ -41,35 +36,32 @@ 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.GeyserConfiguration;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import org.geysermc.geyser.configuration.ConfigLoader;
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;
import java.lang.reflect.Method;
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.stream.Collectors;
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private StandaloneCloudCommandManager cloud;
private CommandRegistry commandRegistry;
private GeyserStandaloneConfiguration geyserConfig;
private GeyserConfig geyserConfig;
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserStandaloneGUI gui;
@@ -80,9 +72,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserImpl geyser;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Map<String, String> argsConfigKeys = new HashMap<>();
private static final Map<NodePath, String> argsConfigKeys = new HashMap<>();
public static void main(String[] args) {
if (System.getProperty("io.netty.leakDetection.level") == null) {
@@ -99,8 +89,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
GeyserLocale.init(bootstrap);
List<BeanPropertyDefinition> 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
@@ -132,34 +120,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
// 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));
@@ -189,19 +151,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@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);
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");
}
} catch (IOException ex) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
this.geyserConfig = loadConfig(GeyserRemoteConfig.class);
if (this.geyserConfig == null) {
if (gui == null) {
System.exit(1);
} else {
@@ -209,13 +160,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return;
}
}
geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// 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);
geyser = GeyserImpl.load(this);
boolean reloading = geyser.isReloading();
if (!reloading) {
@@ -245,6 +194,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
geyserLogger.start();
}
@Override
public <T extends GeyserConfig> T loadConfig(Class<T> configClass) {
return new ConfigLoader(this)
.configFile(new File(getConfigFolder().toFile(), configFilename))
.transformer(this::handleArgsConfigOptions)
.load(configClass);
}
/**
* Check using {@link java.awt.GraphicsEnvironment} that we are a headless client
*
@@ -273,8 +230,13 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
}
@Override
public GeyserConfiguration getGeyserConfig() {
return geyserConfig;
public @NonNull PlatformType platformType() {
return PlatformType.STANDALONE;
}
@Override
public GeyserConfig config() {
return this.geyserConfig;
}
@Override
@@ -330,100 +292,47 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
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<BeanPropertyDefinition> getPOJOForClass(Class<?> clazz) {
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
// Introspect the given type
BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
// Find properties
List<BeanPropertyDefinition> properties = beanDescription.findProperties();
// Get the ignored properties
Set<String> 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.advanced().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<? extends Enum>) 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<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
private void handleArgsConfigOptions(CommentedConfigurationNode node) {
for (Map.Entry<NodePath, String> 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;
}
for (Map.Entry<String, String> 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());
}
}
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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -32,6 +32,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.io.IoBuilder;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.command.GeyserCommandSource;
@@ -112,7 +113,12 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
@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

View File

@@ -16,12 +16,13 @@ dependencies {
api(libs.cloud.velocity)
}
platformRelocate("com.fasterxml.jackson")
platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
platformRelocate("org.yaml")
platformRelocate("org.spongepowered")
platformRelocate("org.bstats")
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
// These dependencies are already present on the platform
provided(libs.velocity.api)

View File

@@ -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<PluginInfo> plugins;

View File

@@ -128,7 +128,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
if (bootstrap.config().advanced().java().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
new GeyserVelocityCompressionDisabler());
}

View File

@@ -39,31 +39,31 @@ 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.FloodgateKeyLoader;
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.CommandSourceConverter;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.GeyserConfiguration;
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.VelocityCommandSource;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
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.File;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.file.Path;
import java.nio.file.Paths;
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 {
@@ -71,7 +71,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
private final ProxyServer proxyServer;
private final PluginContainer container;
private final GeyserVelocityLogger geyserLogger;
private GeyserVelocityConfiguration geyserConfig;
private GeyserPluginConfig geyserConfig;
private GeyserVelocityInjector geyserInjector;
private IGeyserPingPassthrough geyserPingPassthrough;
private CommandRegistry commandRegistry;
@@ -106,13 +106,12 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
System.setProperty("Mcpl.io_uring", "true");
}
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
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,16 +138,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
return;
}
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
if (!geyserConfig.motd().integratedPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
@@ -178,7 +176,12 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
}
@Override
public GeyserVelocityConfiguration getGeyserConfig() {
public @NonNull PlatformType platformType() {
return PlatformType.VELOCITY;
}
@Override
public GeyserPluginConfig config() {
return geyserConfig;
}
@@ -250,27 +253,26 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
@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();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
@Override
public Path getFloodgateKeyPath() {
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null;
return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataPath, configFolder, geyserLogger);
}
@Override
public MetricsPlatform createMetricsPlatform() {
try {
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);
} catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
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;
}
return true;
}
}

View File

@@ -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(Permissions.CHECK_UPDATE)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* 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
@@ -25,28 +25,45 @@
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 org.bstats.config.MetricsConfig;
import org.geysermc.geyser.util.metrics.MetricsPlatform;
import java.io.File;
import java.io.IOException;
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 final class VelocityMetrics implements MetricsPlatform {
private final MetricsConfig config;
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null;
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataPath, dataFolder.toPath(), plugin.getGeyserLogger());
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();
}
}

View File

@@ -12,8 +12,9 @@ platformRelocate("net.kyori")
platformRelocate("org.yaml")
platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("org.cloudburstmc.netty")
platformRelocate("org.bstats")
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
// These dependencies are already present on the platform
provided(libs.viaproxy)

View File

@@ -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;
}
}

View File

@@ -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<PluginInfo> plugins;

View File

@@ -26,6 +26,7 @@ package org.geysermc.geyser.platform.viaproxy;
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 class GeyserViaProxyLogger implements GeyserLogger, GeyserCommandSource {
}
}
@Override
public void debug(@Nullable Object object) {
if (this.debug) {
this.logger.debug(ConsoleFormatter.convert(String.valueOf(object)));
}
}
@Override
public void debug(String message, Object... arguments) {
if (this.debug) {

View File

@@ -36,7 +36,9 @@ import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
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.plugins.events.ViaProxyLoadedEvent;
import net.raphimc.viaproxy.plugins.events.types.ITyped;
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;
@@ -47,18 +49,19 @@ import org.geysermc.geyser.api.network.AuthType;
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.GeyserConfiguration;
import org.geysermc.geyser.configuration.ConfigLoader;
import org.geysermc.geyser.configuration.GeyserConfig;
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.spongepowered.configurate.serialize.SerializationException;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.file.Files;
@@ -67,10 +70,10 @@ import java.util.UUID;
public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, 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 geyserConfig;
private GeyserImpl geyser;
private StandaloneCloudCommandManager cloud;
private CommandRegistry commandRegistry;
@@ -79,10 +82,6 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
@Override
public void onEnable() {
ROOT_FOLDER.mkdirs();
GeyserLocale.init(this);
this.onGeyserInitialize();
ViaProxy.EVENT_MANAGER.register(this);
}
@@ -91,6 +90,12 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
this.onGeyserShutdown();
}
@EventHandler
private void onViaProxyLoaded(ViaProxyLoadedEvent event) {
GeyserLocale.init(this);
this.onGeyserInitialize();
}
@EventHandler
private void onConsoleCommand(final ConsoleCommandEvent event) {
final String command = event.getCommand().startsWith("/") ? event.getCommand().substring(1) : event.getCommand();
@@ -119,6 +124,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
if (event.getType() != ITyped.Type.POST || event.isLegacyPassthrough()) {
return;
}
// TODO remove
if (System.getProperty("geyser.viaproxy.disableIpPassthrough") != null) { // Temporary until Configurate branch is merged
return;
}
@@ -147,11 +153,12 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
@Override
public void onGeyserInitialize() {
if (!this.loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this);
this.geyser = GeyserImpl.load(this);
this.geyser.eventBus().register(this, new GeyserServerTransferListener());
LoopbackUtil.checkAndApplyLoopback(this.logger);
}
@@ -164,7 +171,8 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
}
boolean reloading = geyser.isReloading();
if (reloading) {
if (!this.loadConfig()) {
geyserConfig = loadConfig(GeyserPluginConfig.class);
if (geyserConfig == null) {
return;
}
} else {
@@ -185,7 +193,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
}
if (this.config.getRemote().authType() == AuthType.FLOODGATE) {
if (this.geyserConfig.java().authType() == AuthType.FLOODGATE) {
ViaProxy.getConfig().setPassthroughBungeecordPlayerInfo(true);
}
}
@@ -201,8 +209,13 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
}
@Override
public GeyserConfiguration getGeyserConfig() {
return this.config;
public @NonNull PlatformType platformType() {
return PlatformType.VIAPROXY;
}
@Override
public GeyserPluginConfig config() {
return this.geyserConfig;
}
@Override
@@ -259,19 +272,36 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
return false;
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
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);
} 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);
return true;
@Override
public Path getFloodgateKeyPath() {
return new File(ROOT_FOLDER, geyserConfig.advanced().floodgateKeyFile()).toPath();
}
@Override
public <T extends GeyserConfig> T loadConfig(Class<T> configClass) {
T config = new ConfigLoader(this)
.transformer(node -> {
try {
if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) {
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
pingPassthroughInterval.set(15);
}
} catch (SerializationException e) {
throw new RuntimeException(e);
}
})
.configFile(new File(ROOT_FOLDER, "config.yml"))
.load(configClass);
if (config != null) {
this.geyserConfig = (GeyserPluginConfig) config;
config.java().authType(Files.isRegularFile(getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE);
}
return config;
}
}