mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Switched to ConfigUtils and prepared the addition of metrics
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("floodgate.shadow-conventions")
|
||||
id("floodgate.publish-conventions")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -32,29 +30,3 @@ tasks {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
T configInstance;
|
||||
try {
|
||||
// check and update if the config is outdated
|
||||
if (!newConfig) {
|
||||
updater.update(this, defaultConfigName);
|
||||
}
|
||||
// it would also be nice to have sections in versionBuilder so that you don't have to
|
||||
// provide the path all the time
|
||||
|
||||
FloodgateConfig config = ConfigInitializer.initializeFrom(
|
||||
Files.newInputStream(configPath), configClass);
|
||||
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();
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user