1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 06:49:24 +00:00

Switched to ConfigUtils and prepared the addition of metrics

This commit is contained in:
Tim203
2022-03-11 23:35:59 +01:00
parent 7a9642ef6a
commit 440e20f5ea
17 changed files with 120 additions and 691 deletions

View File

@@ -26,6 +26,7 @@
object Versions {
const val geyserVersion = "2.0.0-SNAPSHOT"
const val cumulusVersion = "1.0-SNAPSHOT"
const val configUtilsVersion = "1.0-SNAPSHOT"
const val spigotVersion = "1.13-R0.1-SNAPSHOT"
const val fastutilVersion = "8.5.3"
const val lombokVersion = "1.18.20"
@@ -33,6 +34,7 @@ object Versions {
const val nettyVersion = "4.1.49.Final"
const val snakeyamlVersion = "1.28"
const val cloudVersion = "1.5.0"
const val bstatsVersion = "3.0.0"
const val javaWebsocketVersion = "1.5.2"

View File

@@ -36,7 +36,7 @@ fun Project.isSnapshot(): Boolean =
fun Project.fullVersion(): String {
var version = version.toString()
if (version.endsWith("-SNAPSHOT")) {
version += " (b${buildNumberAsString()}-${lastCommitHash()}}"
version += " (b${buildNumberAsString()}-${lastCommitHash()})"
}
return version
}

View File

@@ -1,5 +1,5 @@
plugins {
id("floodgate.shadow-conventions")
id("floodgate.publish-conventions")
}
tasks {

View File

@@ -0,0 +1,31 @@
plugins {
id("floodgate.shadow-conventions")
id("com.jfrog.artifactory")
id("maven-publish")
}
publishing {
publications.create<MavenPublication>("mavenJava") {
groupId = project.group as String
artifactId = "floodgate-" + project.name
version = project.version as String
artifact(tasks["shadowJar"])
artifact(tasks["sourcesJar"])
}
}
artifactory {
publish {
repository {
setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
setMavenCompatible(true)
}
defaults {
publishConfigs("archives")
setPublishArtifacts(true)
setPublishPom(true)
setPublishIvy(false)
}
}
}

View File

@@ -3,8 +3,6 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
id("floodgate.base-conventions")
id("com.github.johnrengelman.shadow")
id("com.jfrog.artifactory")
id("maven-publish")
}
tasks {
@@ -31,30 +29,4 @@ tasks {
named("build") {
dependsOn(shadowJar)
}
}
publishing {
publications.create<MavenPublication>("mavenJava") {
groupId = project.group as String
artifactId = "floodgate-" + project.name
version = project.version as String
artifact(tasks["shadowJar"])
artifact(tasks["sourcesJar"])
}
}
artifactory {
publish {
repository {
setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
setMavenCompatible(true)
}
defaults {
publishConfigs("archives")
setPublishArtifacts(true)
setPublishPom(true)
setPublishIvy(false)
}
}
}

View File

@@ -44,7 +44,7 @@ subprojects {
plugins.apply("floodgate.database-conventions")
} else {
when (this) {
in platforms -> plugins.apply("floodgate.shadow-conventions")
in platforms -> plugins.apply("floodgate.publish-conventions")
else -> plugins.apply("floodgate.base-conventions")
}
}

View File

@@ -2,10 +2,12 @@ import net.kyori.blossom.BlossomExtension
plugins {
id("net.kyori.blossom")
id("floodgate.shadow-conventions")
}
dependencies {
api(projects.api)
api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion)
api("com.google.inject", "guice", Versions.guiceVersion)
api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion)
@@ -13,12 +15,15 @@ dependencies {
api("org.java-websocket", "Java-WebSocket", Versions.javaWebsocketVersion)
api("cloud.commandframework", "cloud-core", Versions.cloudVersion)
api("org.yaml", "snakeyaml", Versions.snakeyamlVersion)
api("org.bstats", "bstats-base", Versions.bstatsVersion)
}
// present on all platforms
provided("io.netty", "netty-transport", Versions.nettyVersion)
provided("io.netty", "netty-codec", Versions.nettyVersion)
relocate("org.bstats")
configure<BlossomExtension> {
val constantsFile = "src/main/java/org/geysermc/floodgate/util/Constants.java"
replaceToken("\${branch}", branchName(), constantsFile)

View File

@@ -25,15 +25,22 @@
package org.geysermc.floodgate.config;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.util.UUID;
import lombok.Getter;
import org.geysermc.configutils.loader.callback.CallbackResult;
import org.geysermc.configutils.loader.callback.GenericPostInitializeCallback;
import org.geysermc.floodgate.config.loader.ConfigLoader;
/**
* 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.
*/
@Getter
public class FloodgateConfig {
public class FloodgateConfig implements GenericPostInitializeCallback<ConfigLoader> {
private String keyFileName;
private String usernamePrefix;
private boolean replaceSpaces;
@@ -42,22 +49,36 @@ public class FloodgateConfig {
private DisconnectMessages disconnect;
private PlayerLinkConfig playerLink;
private MetricsConfig metrics;
private boolean debug;
private int configVersion;
private Key key;
public void setKey(Key key) {
if (this.key == null) {
this.key = key;
}
}
public boolean isProxy() {
return this instanceof ProxyFloodgateConfig;
}
@Override
public CallbackResult postInitialize(ConfigLoader loader) {
Path keyPath = loader.getDataFolder().resolve(getKeyFileName());
// don't assume that the key always exists with the existence of a config
if (!Files.exists(keyPath)) {
loader.generateKey(keyPath);
}
try {
Key floodgateKey = loader.getKeyProducer().produceFrom(keyPath);
loader.getCipher().init(floodgateKey);
key = floodgateKey;
} catch (IOException exception) {
return CallbackResult.failed(exception.getMessage());
}
return CallbackResult.ok();
}
@Getter
public static class DisconnectMessages {
private String invalidKey;
@@ -68,10 +89,16 @@ public class FloodgateConfig {
public static class PlayerLinkConfig {
private boolean enabled;
private boolean requireLink;
private boolean enableOwnLinking = false;
private boolean enableOwnLinking;
private boolean allowed;
private long linkCodeTimeout;
private String type;
private boolean enableGlobalLinking;
}
@Getter
public static class MetricsConfig {
private boolean enabled;
private UUID uuid;
}
}

View File

@@ -1,109 +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/Floodgate
*/
package org.geysermc.floodgate.config.loader;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Map;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.introspector.FieldProperty;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.introspector.PropertyUtils;
public class ConfigInitializer {
private static final Yaml YAML;
static {
Constructor constructor =
new CustomClassLoaderConstructor(ConfigInitializer.class.getClassLoader());
constructor.setPropertyUtils(new PropertyUtils() {
@Override
protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
Map<String, Property> properties = new LinkedHashMap<>();
getPropertiesFromClass(type, FloodgateConfig.class, properties);
return properties;
}
private void getPropertiesFromClass(
Class<?> type,
Class<?> stopAfter,
Map<String, Property> propertyMap) {
Class<?> current = type;
while (!Object.class.equals(current)) {
for (Field field : current.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
String correctName = getCorrectName(field.getName());
// children should override parents
propertyMap.putIfAbsent(correctName, new FieldProperty(field));
}
if (field.getClass().getSuperclass().equals(current)) {
getPropertiesFromClass(field.getClass(), field.getClass(), propertyMap);
}
}
if (current.equals(stopAfter)) {
return;
}
current = type.getSuperclass();
}
}
private String getCorrectName(String name) {
// convert sendFloodgateData to send-floodgate-data,
// which is the style of writing config fields
StringBuilder propertyBuilder = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char current = name.charAt(i);
if (Character.isUpperCase(current)) {
propertyBuilder.append('-').append(Character.toLowerCase(current));
} else {
propertyBuilder.append(current);
}
}
return propertyBuilder.toString();
}
});
constructor.getPropertyUtils().setSkipMissingProperties(true);
YAML = new Yaml(constructor);
}
public static <T extends FloodgateConfig> T initializeFrom(
InputStream dataStream,
Class<T> configClass) {
return YAML.loadAs(dataStream, configClass);
}
}

View File

@@ -25,24 +25,27 @@
package org.geysermc.floodgate.config.loader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Key;
import java.util.UUID;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.geysermc.configutils.ConfigUtilities;
import org.geysermc.configutils.file.codec.PathFileCodec;
import org.geysermc.configutils.file.template.ResourceTemplateReader;
import org.geysermc.configutils.updater.change.Changes;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
import org.geysermc.floodgate.config.updater.ConfigUpdater;
import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.crypto.KeyProducer;
@Getter
@RequiredArgsConstructor
public final class ConfigLoader {
private final Path dataFolder;
private final Class<? extends FloodgateConfig> configClass;
private final DefaultConfigHandler configCreator;
private final ConfigUpdater updater;
private final KeyProducer keyProducer;
private final FloodgateCipher cipher;
@@ -51,61 +54,42 @@ public final class ConfigLoader {
@SuppressWarnings("unchecked")
public <T extends FloodgateConfig> T load() {
Path configPath = dataFolder.resolve("config.yml");
String defaultConfigName = "config.yml";
boolean proxy = ProxyFloodgateConfig.class.isAssignableFrom(configClass);
if (proxy) {
defaultConfigName = "proxy-" + defaultConfigName;
String templateFile = "config.yml";
if (ProxyFloodgateConfig.class.isAssignableFrom(configClass)) {
templateFile = "proxy-" + templateFile;
}
boolean newConfig = !Files.exists(configPath);
if (newConfig) {
try {
configCreator.createDefaultConfig(defaultConfigName, configPath);
} catch (Exception exception) {
logger.error("Error while creating config", exception);
}
}
//todo old Floodgate logged a message when version = 0 and it generated a new key.
// Might be nice to allow you to run a function for a specific version.
// 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(dataFolder))
.configFile("config.yml")
.templateReader(ResourceTemplateReader.of(getClass()))
.template(templateFile)
.changes(Changes.builder()
.version(1, Changes.versionBuilder()
.keyRenamed("player-link.enable", "player-link.enabled")
.keyRenamed("player-link.allow-linking", "player-link.allowed"))
.version(2, Changes.versionBuilder()
.keyRenamed("player-link.use-global-linking", "player-link.enable-global-linking"))
.build())
.definePlaceholder("metrics.uuid", UUID::randomUUID)
.postInitializeCallbackArgument(this)
.build();
T configInstance;
try {
// check and update if the config is outdated
if (!newConfig) {
updater.update(this, defaultConfigName);
}
FloodgateConfig config = ConfigInitializer.initializeFrom(
Files.newInputStream(configPath), configClass);
try {
configInstance = (T) config;
} catch (ClassCastException exception) {
logger.error("Failed to cast config file to required class.", exception);
throw new RuntimeException(exception);
}
} catch (Exception exception) {
logger.error("Error while loading config", exception);
return (T) utilities.executeOn(configClass);
} catch (Throwable throwable) {
throw new RuntimeException(
"Failed to load the config! Try to delete the config file", exception);
"Failed to load the config! Try to delete the config file if this error persists",
throwable
);
}
Path keyPath = dataFolder.resolve(configInstance.getKeyFileName());
// don't assume that the key always exists with the existence of a config
if (!Files.exists(keyPath)) {
generateKey(keyPath);
}
try {
Key key = keyProducer.produceFrom(keyPath);
cipher.init(key);
configInstance.setKey(key);
} catch (IOException exception) {
logger.error("Error while reading the key", exception);
throw new RuntimeException("Failed to read the key!", exception);
}
return configInstance;
}
public void generateKey(Path keyPath) {

View File

@@ -1,159 +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/Floodgate
*/
package org.geysermc.floodgate.config.loader;
import static org.geysermc.floodgate.util.MessageFormatter.format;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.geysermc.floodgate.util.Utils;
public class DefaultConfigHandler {
public void createDefaultConfig(String defaultConfigLocation, Path configPath) throws IOException {
List<String> configLines = loadDefaultConfig(defaultConfigLocation);
// writing the new config file
Files.write(configPath, configLines);
}
public List<String> loadDefaultConfig(String defaultConfigLocation)
throws IOException {
List<String> lines = Utils.readAllLines(defaultConfigLocation);
List<String> configLines = new ArrayList<>();
String parentConfig = null;
List<String> parentLines = null;
int lastInsertLine = -1;
int tempAddAfter = -1;
for (String line : lines) {
// >>(space) or >>|
if (line.startsWith(">>")) {
if (line.length() >= 3) {
// define parent file
if (line.charAt(2) == ' ') {
if (tempAddAfter != -1) {
throw new IllegalStateException(
"Cannot define new parent without closing the current section");
}
parentConfig = line.substring(3);
parentLines = null;
lastInsertLine = -1;
continue;
}
// define start / end of insert section
if (line.charAt(2) == '|') {
// end section
if (line.length() == 3) {
if (tempAddAfter == -1) {
throw new IllegalStateException("Cannot close an unclosed section");
}
lastInsertLine = tempAddAfter;
tempAddAfter = -1;
continue;
}
// start insert section
if (parentConfig == null) {
throw new IllegalStateException(
"Cannot start insert section without providing a parent");
}
if (tempAddAfter != -1) {
throw new IllegalStateException(
"Cannot start section with an unclosed section");
}
// note that addAfter starts counting from 1
int addAfter = Integer.parseInt(line.substring(4)) - 1;
if (lastInsertLine > -1 && addAfter < lastInsertLine) {
throw new IllegalStateException(format(
"Cannot add the same lines twice {} {}",
addAfter, lastInsertLine
));
}
// as you can see by this implementation
// we don't support parent files in parent files
if (lastInsertLine == -1) {
parentLines = Utils.readAllLines(parentConfig);
for (int i = 0; i <= addAfter; i++) {
configLines.add(parentLines.get(i));
}
} else {
for (int i = lastInsertLine; i <= addAfter; i++) {
configLines.add(parentLines.get(i));
}
}
tempAddAfter = addAfter;
continue;
}
if (line.charAt(2) == '*') {
if (parentConfig == null) {
throw new IllegalStateException(
"Cannot write rest of the parent without providing a parent");
}
if (tempAddAfter != -1) {
throw new IllegalStateException(
"Cannot write rest of the parent config while an insert section is still open");
}
if (lastInsertLine == -1) {
parentLines = Utils.readAllLines(parentConfig);
configLines.addAll(parentLines);
continue;
}
// the lastInsertLine has already been printed, so we won't print it twice
for (int i = lastInsertLine + 1; i < parentLines.size(); i++) {
configLines.add(parentLines.get(i));
}
continue;
}
throw new IllegalStateException(
"The use of >>" + line.charAt(2) + " is unknown");
}
throw new IllegalStateException("Unable do something with just >>");
}
// everything else: comments and key/value lines will be added
configLines.add(line);
}
return configLines;
}
}

View File

@@ -1,182 +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/Floodgate
*/
package org.geysermc.floodgate.config.updater;
import com.google.common.base.Ascii;
import com.google.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.loader.DefaultConfigHandler;
public final class ConfigFileUpdater {
@Inject private FloodgateLogger logger;
@Inject private DefaultConfigHandler defaultConfigHandler;
/**
* Simple config file updater. Please note that all the keys should be unique and that this
* system wasn't made for complex configurations.
*
* @param configLocation the location of the Floodgate config
* @param currentVersion the key value map of the current config
* @param renames name changes introduced in this version. new (key) to old
* (value)
* @param defaultConfigLocation the location of the default Floodgate config
* @throws IOException if an I/O error occurs
*/
public void update(
Path configLocation,
Map<String, Object> currentVersion,
Map<String, String> renames,
String defaultConfigLocation)
throws IOException {
List<String> notFound = new ArrayList<>();
List<String> newConfig = defaultConfigHandler.loadDefaultConfig(defaultConfigLocation);
String spaces = "";
Map<String, Object> map = null;
String line;
for (int i = 0; i < newConfig.size(); i++) {
line = newConfig.get(i);
// we don't have to check comments or empty lines
if (line.isEmpty() || line.charAt(0) == '#') {
continue;
}
StringBuilder currentSpaces = new StringBuilder();
while (line.charAt(currentSpaces.length()) == Ascii.SPACE) {
currentSpaces.append(Ascii.SPACE);
}
// end of subcategory
if (!spaces.isEmpty() && currentSpaces.length() < spaces.length()) {
// we can assume this since we don't allow subcategories of subcategories
spaces = "";
map = null;
}
// ignore comments
if (line.charAt(currentSpaces.length()) == '#') {
continue;
}
int splitIndex = line.indexOf(':');
// if the line has a 'key: value' structure
if (splitIndex != -1) {
// start of a subcategory
if (line.length() == splitIndex + 1) {
if (currentSpaces.length() > 0) {
throw new IllegalStateException(
"Config too complex! I can't understand subcategories of a subcategory");
}
spaces = " ";
//todo allow rename of subcategory?
//noinspection unchecked
map = (Map<String, Object>) currentVersion.get(line.substring(0, splitIndex));
continue;
}
String name = line.substring(spaces.length(), splitIndex);
// don't change the config-version to the old value!
if (name.equals("config-version")) {
continue;
}
// allow multiple renames
String tempName;
String oldName = name;
do {
tempName = oldName;
oldName = renames.getOrDefault(oldName, oldName);
} while (!oldName.equals(tempName));
Object value;
if (map != null) {
value = map.get(oldName);
} else {
value = currentVersion.get(spaces + oldName);
}
// use default value if the key doesn't exist in the current version
if (value == null) {
notFound.add(name);
continue;
}
if (value instanceof String) {
String v = (String) value;
if (!v.startsWith("\"") || !v.endsWith("\"")) {
value = "\"" + value + "\"";
}
//todo this doesn't update {0} {1} to {} {} e.g.
}
logger.debug(name + " has been changed to " + value);
newConfig.set(i, spaces + name + ": " + value);
}
}
Files.deleteIfExists(configLocation.getParent().resolve("config-old.yml"));
Files.copy(configLocation, configLocation.getParent().resolve("config-old.yml"));
Files.write(configLocation, newConfig);
logger.info("Successfully updated the config file! " +
"Your old config has been moved to config-old.yml");
if (!notFound.isEmpty()) {
StringBuilder messageBuilder = new StringBuilder(
"Please note that the following keys we not found in the old config and " +
"are now using the default Floodgate config value. Missing/new keys: ");
boolean first = true;
for (String value : notFound) {
if (!first) {
messageBuilder.append(", ");
}
String renamed = renames.get(value);
if (renamed != null) {
messageBuilder.append(renamed).append(" to ");
}
messageBuilder.append(value);
first = false;
}
logger.info(messageBuilder.toString());
}
}
}

View File

@@ -1,122 +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/Floodgate
*/
package org.geysermc.floodgate.config.updater;
import static com.google.common.base.Preconditions.checkArgument;
import static org.geysermc.floodgate.util.MessageFormatter.format;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.loader.ConfigLoader;
import org.yaml.snakeyaml.Yaml;
@RequiredArgsConstructor
public final class ConfigUpdater {
private static final int CONFIG_VERSION = 2;
private final Path dataFolder;
private final ConfigFileUpdater fileUpdater;
private final FloodgateLogger logger;
public void update(ConfigLoader loader, String defaultConfigLocation) {
Path configLocation = dataFolder.resolve("config.yml");
Map<String, Object> config;
try (BufferedReader configReader = Files.newBufferedReader(configLocation)) {
config = new Yaml().load(configReader);
} catch (IOException exception) {
logger.error("Error while opening the config file", exception);
throw new RuntimeException("Failed to update config", exception);
}
// new name -> old name
Map<String, String> renames = new HashMap<>();
int version = 0; // pre-rewrite is the default config version
Object versionElement = config.get("config-version");
// only rewrite configs have a config-version
if (versionElement == null) {
logger.warn("We've detected a pre-rewrite config file, please note that Floodgate " +
"doesn't not work properly if you don't update your Floodgate key used on " +
"all your servers (including Geyser). We'll try to update your Floodgate " +
"config now and we'll also generate a new Floodgate key for you, but if " +
"you're running a network or if you're running a Spigot server with " +
"Geyser Standalone please update as you'll no longer be able to connect.");
renames.put("enabled", "enable");
renames.put("allowed", "allow-linking");
// relocate the old key so that they can restore it if it was a new key
Path keyFilePath = dataFolder.resolve((String) config.get("key-file-name"));
if (Files.exists(keyFilePath)) {
try {
Files.copy(keyFilePath, dataFolder.resolve("old-key.pem"));
} catch (IOException exception) {
throw new RuntimeException(
"Failed to relocate the old key to make place for a new key",
exception);
}
}
loader.generateKey(keyFilePath);
} else {
// get (and verify) the config version
checkArgument(
versionElement instanceof Integer,
"Config version should be an integer. Did someone mess with the config?"
);
version = (int) versionElement;
checkArgument(
version > 0 && version <= CONFIG_VERSION,
format("Config is newer then possible on this version! Expected {}, got {}",
CONFIG_VERSION, version));
}
// config is already up-to-date
if (version == CONFIG_VERSION) {
return;
}
if (version < 2) {
// renamed 'use-global-linking' to 'enable-global-linking'
// and added 'enable-own-linking'
renames.put("enable-global-linking", "use-global-linking");
}
try {
fileUpdater.update(configLocation, config, renames, defaultConfigLocation);
} catch (IOException exception) {
logger.error("Error while updating the config file", exception);
throw new RuntimeException("Failed to update config", exception);
}
}
}

View File

@@ -43,9 +43,6 @@ import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.config.FloodgateConfigHolder;
import org.geysermc.floodgate.config.loader.ConfigLoader;
import org.geysermc.floodgate.config.loader.DefaultConfigHandler;
import org.geysermc.floodgate.config.updater.ConfigFileUpdater;
import org.geysermc.floodgate.config.updater.ConfigUpdater;
import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping;
@@ -105,27 +102,10 @@ public class CommonModule extends AbstractModule {
@Singleton
public ConfigLoader configLoader(
@Named("configClass") Class<? extends FloodgateConfig> configClass,
DefaultConfigHandler defaultConfigHandler,
ConfigUpdater configUpdater,
KeyProducer producer,
FloodgateCipher cipher,
FloodgateLogger logger) {
return new ConfigLoader(dataDirectory, configClass, defaultConfigHandler, configUpdater,
producer, cipher, logger);
}
@Provides
@Singleton
public DefaultConfigHandler defaultConfigCreator() {
return new DefaultConfigHandler();
}
@Provides
@Singleton
public ConfigUpdater configUpdater(
ConfigFileUpdater configFileUpdater,
FloodgateLogger logger) {
return new ConfigUpdater(dataDirectory, configFileUpdater, logger);
return new ConfigLoader(dataDirectory, configClass, producer, cipher, logger);
}
@Provides

View File

@@ -58,5 +58,9 @@ player-link:
# you have limited internet access.
enable-global-linking: true
metrics:
enabled: true
uuid: ${metrics.uuid}
# Do not change this
config-version: 2
config-version: 3

View File

@@ -10,10 +10,6 @@
<!-- UnusedPrivateMethod -->
<exclude-pattern>.*/CommonPlayerLink.*</exclude-pattern>
<!-- NullAssignment -->
<exclude-pattern>.*/DefaultConfigHandler.*</exclude-pattern>
<!-- NullAssignment -->
<exclude-pattern>.*/ConfigFileUpdater.*</exclude-pattern>
<!-- RedundantFieldInitializer -->
<exclude-pattern>.*/FloodgateConfig.*</exclude-pattern>
<!-- CloseResource, there is no shutdown event and it has to load classes on the fly -->

View File

@@ -5,9 +5,9 @@ var gsonVersion = "2.8.5"
dependencies {
api(projects.core)
implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion)
// hack to make pre 1.12 work
implementation("com.google.guava", "guava", guavaVersion)
implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion)
}
relocate("com.google.inject")