mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-24 01:09:19 +00:00
Initial version of moving to from ConfigUtils to Configurate
This commit is contained in:
@@ -9,8 +9,9 @@ dependencies {
|
||||
api(libs.base.api)
|
||||
compileOnlyApi(projects.isolation)
|
||||
|
||||
annotationProcessor(libs.config.utils.ap)
|
||||
api(libs.config.utils)
|
||||
annotationProcessor(libs.configurate.`interface`.ap)
|
||||
api(libs.configurate.`interface`)
|
||||
implementation(libs.configurate.yaml)
|
||||
|
||||
annotationProcessor(libs.database.utils.ap)
|
||||
api(libs.database.utils)
|
||||
|
||||
@@ -26,38 +26,36 @@
|
||||
package org.geysermc.floodgate.core.config;
|
||||
|
||||
import io.micronaut.context.env.PropertySource;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.geysermc.configutils.node.codec.strategy.object.ProxyEmbodimentStrategy;
|
||||
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.util.NamingSchemes;
|
||||
|
||||
public final class ConfigAsPropertySource {
|
||||
private ConfigAsPropertySource() {}
|
||||
|
||||
public static PropertySource toPropertySource(FloodgateConfig config) {
|
||||
Objects.requireNonNull(config);
|
||||
return PropertySource.of(flatten(Map.of("config", config)));
|
||||
public static PropertySource toPropertySource(ConfigurationNode rootNode) {
|
||||
Objects.requireNonNull(rootNode);
|
||||
|
||||
var root = CommentedConfigurationNode.root();
|
||||
root.node("config").from(rootNode.copy());
|
||||
|
||||
return PropertySource.of(flatten(root));
|
||||
}
|
||||
|
||||
private static Map<String, Object> flatten(Map<String, Object> map) {
|
||||
return flatten0(Map.of("", map).get(""));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> flatten0(Map<String, Object> map) {
|
||||
private static Map<String, Object> flatten(ConfigurationNode node) {
|
||||
var result = new HashMap<String, Object>();
|
||||
map.forEach((key, value) -> {
|
||||
if (Proxy.isProxyClass(value.getClass())) {
|
||||
value = new ProxyEmbodimentStrategy().disembody(value);
|
||||
}
|
||||
if (value instanceof Map<?,?>) {
|
||||
flatten0((Map<String, Object>) value).forEach(
|
||||
(key1, value1) -> result.put(key + "." + key1, value1)
|
||||
);
|
||||
node.childrenMap().values().forEach(value -> {
|
||||
// we expect the properties in camelCase
|
||||
var key = NamingSchemes.CAMEL_CASE.coerce(value.key().toString());
|
||||
|
||||
if (value.isMap()) {
|
||||
flatten(value).forEach((childKey, childValue) -> result.put(key + "." + childKey, childValue));
|
||||
return;
|
||||
}
|
||||
result.put(key, value);
|
||||
result.put(key, value.raw());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,17 @@
|
||||
|
||||
package org.geysermc.floodgate.core.config;
|
||||
|
||||
import static org.spongepowered.configurate.NodePath.path;
|
||||
|
||||
import io.micronaut.context.ApplicationContext;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import org.geysermc.configutils.ConfigUtilities;
|
||||
import org.geysermc.configutils.file.codec.PathFileCodec;
|
||||
import org.geysermc.configutils.updater.change.Changes;
|
||||
import org.geysermc.floodgate.core.util.Constants;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions;
|
||||
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
public final class ConfigLoader {
|
||||
private ConfigLoader() {}
|
||||
@@ -39,33 +44,38 @@ public final class ConfigLoader {
|
||||
public static <T extends FloodgateConfig> T load(Path dataDirectory, boolean isProxy, ApplicationContext context) {
|
||||
var configClass = isProxy ? ProxyFloodgateConfig.class : FloodgateConfig.class;
|
||||
|
||||
// it would also be nice to have sections in versionBuilder so that you don't have to
|
||||
// provide the path all the time
|
||||
|
||||
ConfigUtilities utilities =
|
||||
ConfigUtilities.builder()
|
||||
.fileCodec(PathFileCodec.of(dataDirectory))
|
||||
.configFile("config.yml")
|
||||
.changes(Changes.builder()
|
||||
.version(1, Changes.versionBuilder()
|
||||
.keyRenamed("playerLink.enable", "playerLink.enabled")
|
||||
.keyRenamed("playerLink.allowLinking", "playerLink.allowed"))
|
||||
.version(2, Changes.versionBuilder()
|
||||
.keyRenamed("playerLink.useGlobalLinking", "playerLink.enableGlobalLinking"))
|
||||
.version(3, Changes.versionBuilder()
|
||||
.keyRenamed("playerLink.type", "database.type"))
|
||||
.build())
|
||||
.definePlaceholder("metrics.uuid", UUID::randomUUID)
|
||||
.postInitializeCallbackArgument(dataDirectory)
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
T config;
|
||||
try {
|
||||
config = (T) utilities.executeOn(configClass);
|
||||
} catch (Throwable throwable) {
|
||||
var loader = YamlConfigurationLoader.builder()
|
||||
.path(dataDirectory.resolve("config.yml"))
|
||||
.defaultOptions(InterfaceDefaultOptions.get())
|
||||
.build();
|
||||
|
||||
node = loader.load();
|
||||
// temp fix for node.virtual() being broken
|
||||
var virtual = !Files.exists(dataDirectory.resolve("config.yml"));
|
||||
|
||||
var migrations = ConfigurationTransformation.versionedBuilder()
|
||||
.addVersion(Constants.CONFIG_VERSION, twoToThree())
|
||||
.addVersion(2, oneToTwo())
|
||||
.addVersion(1, zeroToOne())
|
||||
.build();
|
||||
|
||||
var startVersion = migrations.version(node);
|
||||
migrations.apply(node);
|
||||
var endVersion = migrations.version(node);
|
||||
|
||||
config = (T) node.get(configClass);
|
||||
|
||||
// save default config or save migrated config
|
||||
if (virtual || startVersion != endVersion) {
|
||||
loader.save(node);
|
||||
}
|
||||
} catch (ConfigurateException exception) {
|
||||
throw new RuntimeException(
|
||||
"Failed to load the config! Try to delete the config file if this error persists",
|
||||
throwable
|
||||
exception
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,7 +83,34 @@ public final class ConfigLoader {
|
||||
context.registerSingleton(config);
|
||||
context.registerSingleton(FloodgateConfig.class, config);
|
||||
// make @Requires etc. work
|
||||
context.getEnvironment().addPropertySource(ConfigAsPropertySource.toPropertySource(config));
|
||||
context.getEnvironment().addPropertySource(ConfigAsPropertySource.toPropertySource(node));
|
||||
return config;
|
||||
}
|
||||
|
||||
private static ConfigurationTransformation zeroToOne() {
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(path("playerLink", "enable"), (path, value) -> {
|
||||
return new Object[]{"playerLink", "enabled"};
|
||||
})
|
||||
.addAction(path("playerLink", "allowLinking"), (path, value) -> {
|
||||
return new Object[]{"playerLink", "allowed"};
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private static ConfigurationTransformation oneToTwo() {
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(path("playerLink", "useGlobalLinking"), (path, value) -> {
|
||||
return new Object[]{"playerLink", "enableGlobalLinking"};
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private static ConfigurationTransformation twoToThree() {
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(path("playerLink", "type"), (path, value) -> {
|
||||
return new Object[]{"database", "type"};
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,43 +25,45 @@
|
||||
|
||||
package org.geysermc.floodgate.core.config;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.Key;
|
||||
import org.geysermc.configutils.loader.callback.CallbackResult;
|
||||
import org.geysermc.configutils.loader.callback.GenericPostInitializeCallback;
|
||||
import org.geysermc.configutils.node.meta.Comment;
|
||||
import org.geysermc.configutils.node.meta.ConfigSection;
|
||||
import org.geysermc.configutils.node.meta.ConfigVersion;
|
||||
import org.geysermc.configutils.node.meta.Defaults.DefaultBoolean;
|
||||
import org.geysermc.configutils.node.meta.Defaults.DefaultNumeric;
|
||||
import org.geysermc.configutils.node.meta.Defaults.DefaultString;
|
||||
import org.geysermc.configutils.node.meta.Exclude;
|
||||
import org.geysermc.configutils.node.meta.Hidden;
|
||||
import org.geysermc.configutils.node.meta.Placeholder;
|
||||
import java.util.UUID;
|
||||
import org.geysermc.floodgate.core.util.Constants;
|
||||
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.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 org.spongepowered.configurate.objectmapping.meta.PostProcess;
|
||||
|
||||
/**
|
||||
* The global Floodgate configuration file used in every platform. Some platforms have their own
|
||||
* addition to the global configuration like {@link ProxyFloodgateConfig} for the proxies.
|
||||
*/
|
||||
@ConfigVersion(3)
|
||||
public interface FloodgateConfig extends GenericPostInitializeCallback<Path> {
|
||||
@ConfigSerializable
|
||||
public interface FloodgateConfig {
|
||||
@Exclude
|
||||
default boolean proxy() {
|
||||
return this instanceof ProxyFloodgateConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
default CallbackResult postInitialize(Path dataDirectory) {
|
||||
Path keyPath = dataDirectory.resolve(keyFileName());
|
||||
|
||||
// don't assume that the key always exists with the existence of a config
|
||||
if (!Files.exists(keyPath)) {
|
||||
// TODO improve message and also link to article about corrupted keys/WinSCP/FTP
|
||||
// Like, where is key.pem?
|
||||
// TODO don't be so noisy with error. It makes it hard to understand what the error is.
|
||||
return CallbackResult.failed("Floodgate requires a key file! " +
|
||||
"Copy your key file from Geyser (key.pem) and paste it into " + keyPath);
|
||||
}
|
||||
@PostProcess
|
||||
default void postInitialise() {
|
||||
//todo add postInitialize with argument
|
||||
// @PostProcess
|
||||
// default void postInitialize(Path dataDirectory) throws SerializationException {
|
||||
// Path keyPath = dataDirectory.resolve(keyFileName());
|
||||
//
|
||||
// // don't assume that the key always exists with the existence of a config
|
||||
// if (!Files.exists(keyPath)) {
|
||||
// // TODO improve message and also link to article about corrupted keys/WinSCP/FTP
|
||||
// // Like, where is key.pem?
|
||||
// // TODO don't be so noisy with error. It makes it hard to understand what the error is.
|
||||
// throw new SerializationException("Floodgate requires a key file! " +
|
||||
// "Copy your key file from Geyser (key.pem) and paste it into " + keyPath);
|
||||
// }
|
||||
|
||||
//todo remove key file name config option
|
||||
|
||||
@@ -71,25 +73,29 @@ public interface FloodgateConfig extends GenericPostInitializeCallback<Path> {
|
||||
if (usernamePrefix().length() >= 16) {
|
||||
usernamePrefix(".");
|
||||
}
|
||||
|
||||
return CallbackResult.ok();
|
||||
}
|
||||
|
||||
@Comment
|
||||
@Comment("""
|
||||
In Floodgate bedrock player data is send encrypted.
|
||||
The following value should point to the key Floodgate generated.
|
||||
The public key should be used for the Geyser(s) and the private key for the Floodgate(s)""")
|
||||
@DefaultString("key.pem")
|
||||
String keyFileName();
|
||||
|
||||
@Comment
|
||||
@Comment("""
|
||||
Floodgate prepends a prefix to bedrock usernames to avoid conflicts
|
||||
However, certain conflicts can cause issues with some plugins so this prefix is configurable using the property below
|
||||
It is recommended to use a prefix that does not contain alphanumerical to avoid the possibility of duplicate usernames.""")
|
||||
@DefaultString(".")
|
||||
String usernamePrefix();
|
||||
|
||||
void usernamePrefix(String usernamePrefix);
|
||||
|
||||
@Comment
|
||||
@Comment("Should spaces be replaced with '_' in bedrock usernames?")
|
||||
@DefaultBoolean(true)
|
||||
boolean replaceSpaces();
|
||||
|
||||
@Comment
|
||||
@Comment("The default locale for Floodgate. By default, Floodgate uses the system locale")
|
||||
@DefaultString("system")
|
||||
String defaultLocale();
|
||||
|
||||
@@ -97,78 +103,103 @@ public interface FloodgateConfig extends GenericPostInitializeCallback<Path> {
|
||||
|
||||
DatabaseConfig database();
|
||||
|
||||
@Comment
|
||||
@Comment("Configuration for player linking")
|
||||
PlayerLinkConfig playerLink();
|
||||
|
||||
MetricsConfig metrics();
|
||||
|
||||
default int version() {
|
||||
return Constants.CONFIG_VERSION;
|
||||
}
|
||||
|
||||
@Hidden
|
||||
@DefaultBoolean
|
||||
boolean debug();
|
||||
|
||||
@Exclude Key key();
|
||||
@Field Key key();
|
||||
|
||||
void key(Key key);
|
||||
@Field void key(Key key);
|
||||
|
||||
@Exclude String rawUsernamePrefix();
|
||||
@Field String rawUsernamePrefix();
|
||||
|
||||
void rawUsernamePrefix(String usernamePrefix);
|
||||
@Field void rawUsernamePrefix(String usernamePrefix);
|
||||
|
||||
@ConfigSection
|
||||
@ConfigSerializable
|
||||
interface DisconnectMessages {
|
||||
@Comment
|
||||
@Comment("The disconnect message Geyser users should get when connecting\n" +
|
||||
"to the server with an invalid key")
|
||||
@DefaultString("Please connect through the official Geyser")
|
||||
String invalidKey();
|
||||
|
||||
@Comment
|
||||
@Comment("The disconnect message Geyser users should get when connecting\n" +
|
||||
"to the server with the correct key but not with the correct data format")
|
||||
@DefaultString("Expected {} arguments, got {}. Is Geyser up-to-date?")
|
||||
String invalidArgumentsLength();
|
||||
}
|
||||
|
||||
@ConfigSection
|
||||
@ConfigSerializable
|
||||
interface DatabaseConfig {
|
||||
@Comment
|
||||
@Comment("Whether a database can be used (required for for example local linking)")
|
||||
@DefaultBoolean(true)
|
||||
boolean enabled();
|
||||
|
||||
@Comment
|
||||
@Comment("The database type you want to use.")
|
||||
@DefaultString("h2")
|
||||
String type();
|
||||
}
|
||||
|
||||
@ConfigSection
|
||||
@ConfigSerializable
|
||||
interface PlayerLinkConfig {
|
||||
@Comment
|
||||
@Comment("Whether to enable the linking system. Turning this off will prevent\n" +
|
||||
"players from using the linking feature even if they are already linked.")
|
||||
@DefaultBoolean(true)
|
||||
boolean enabled();
|
||||
|
||||
@Comment
|
||||
@Comment("Whether to require a linked account in order to be able to join the server.")
|
||||
@DefaultBoolean
|
||||
boolean requireLink();
|
||||
|
||||
@Comment
|
||||
@Comment("""
|
||||
Set the following option to true when you want to host your own linking database.
|
||||
-> This can work in addition to global linking.
|
||||
Note that you have to enable the database in the database section as well.
|
||||
""")
|
||||
@DefaultBoolean
|
||||
boolean enableOwnLinking();
|
||||
|
||||
@Comment
|
||||
@Comment("""
|
||||
The following two options only apply when 'enable-own-linking' is set to 'true'
|
||||
|
||||
Whether to allow the use of /linkaccount and /unlinkaccount
|
||||
You can also use allow specific people to use the commands using the
|
||||
permissions floodgate.command.linkaccount and floodgate.command.unlinkaccount.
|
||||
This is only for linking, already connected people will stay connected""")
|
||||
@DefaultBoolean(true)
|
||||
boolean allowed();
|
||||
|
||||
@Comment
|
||||
@Comment("The amount of seconds until a link code expires.")
|
||||
@DefaultNumeric(300)
|
||||
long linkCodeTimeout();
|
||||
|
||||
@Comment
|
||||
@Comment("""
|
||||
Whether to enable global linking. Global Linking is a central server where people can link their
|
||||
accounts (Java and Bedrock) and join on servers that have Global Linking enabled. The goal of
|
||||
Global Linking is to make linking easier by not having to link your accounts on every server.
|
||||
-> Your server-specific linking database will have priority over global linking.
|
||||
Global Linking should normally only be disabled when you don't have internet access or when
|
||||
you have limited internet access.
|
||||
""")
|
||||
@DefaultBoolean(true)
|
||||
boolean enableGlobalLinking();
|
||||
}
|
||||
|
||||
@ConfigSection
|
||||
@ConfigSerializable
|
||||
interface MetricsConfig {
|
||||
@DefaultBoolean(true)
|
||||
boolean enabled();
|
||||
|
||||
@Placeholder
|
||||
String uuid();
|
||||
default UUID uuid() {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,17 +25,19 @@
|
||||
|
||||
package org.geysermc.floodgate.core.config;
|
||||
|
||||
import org.geysermc.configutils.node.meta.Comment;
|
||||
import org.geysermc.configutils.node.meta.ConfigVersion;
|
||||
import org.geysermc.configutils.node.meta.Defaults.DefaultBoolean;
|
||||
import org.geysermc.configutils.node.meta.Inherit;
|
||||
import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
|
||||
/**
|
||||
* The Floodgate configuration used by proxy platforms, currently Velocity and Bungeecord.
|
||||
* The Floodgate configuration used by proxy platforms, currently Velocity and BungeeCord.
|
||||
*/
|
||||
@Inherit(ConfigVersion.class)
|
||||
@ConfigSerializable
|
||||
public interface ProxyFloodgateConfig extends FloodgateConfig {
|
||||
@Comment
|
||||
@Comment("""
|
||||
Should the proxy send the bedrock player data to the servers it is connecting to?
|
||||
This requires Floodgate to be installed on the servers.
|
||||
You'll get kicked if you don't use the plugin. The default value is false because of it""")
|
||||
@DefaultBoolean
|
||||
boolean sendFloodgateData();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class Metrics {
|
||||
|
||||
metricsBase = new MetricsBase(
|
||||
"server-implementation",
|
||||
metricsConfig.uuid(),
|
||||
metricsConfig.uuid().toString(),
|
||||
Constants.METRICS_ID,
|
||||
metricsConfig.enabled(),
|
||||
this::appendPlatformData,
|
||||
|
||||
@@ -32,6 +32,8 @@ public final class Constants {
|
||||
public static final String GIT_BRANCH = "@branch@";
|
||||
public static final int METRICS_ID = 14649;
|
||||
|
||||
public static final int CONFIG_VERSION = 3;
|
||||
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
|
||||
public static final boolean DEBUG_MODE = false;
|
||||
|
||||
@@ -11,7 +11,7 @@ gson = "2.8.5"
|
||||
|
||||
# core
|
||||
base-api = "feature-floodgate-merge-1.1.0-SNAPSHOT"
|
||||
config-utils = "development-2.0-SNAPSHOT"
|
||||
configurate = "4.2.0-GeyserMC-SNAPSHOT"
|
||||
database-utils = "1.0-SNAPSHOT"
|
||||
fastutil = "8.5.3"
|
||||
java-websocket = "1.5.2"
|
||||
@@ -47,8 +47,9 @@ netty-transport = { module = "io.netty:netty-transport", version.ref = "netty" }
|
||||
|
||||
# core
|
||||
base-api = { module = "org.geysermc.api:base-api", version.ref = "base-api" }
|
||||
config-utils = { module = "org.geysermc.configutils:core", version.ref = "config-utils" }
|
||||
config-utils-ap = { module = "org.geysermc.configutils:ap", version.ref = "config-utils" }
|
||||
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" }
|
||||
database-utils = { module = "org.geysermc.databaseutils:core", version.ref = "database-utils" }
|
||||
database-utils-sql = { module = "org.geysermc.databaseutils:database-sql", version.ref = "database-utils" }
|
||||
database-utils-mongo = { module = "org.geysermc.databaseutils:database-mongo", version.ref = "database-utils" }
|
||||
|
||||
Reference in New Issue
Block a user