forked from GeyserMC/Geyser
Add support for passing config options as arguments (#1506)
This commit is contained in:
parent
c7654ff98c
commit
2d95302b10
2 changed files with 149 additions and 4 deletions
|
@ -25,6 +25,11 @@
|
||||||
|
|
||||||
package org.geysermc.platform.standalone;
|
package org.geysermc.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 lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
@ -36,6 +41,7 @@ import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||||
import org.geysermc.connector.command.CommandManager;
|
import org.geysermc.connector.command.CommandManager;
|
||||||
import org.geysermc.connector.common.PlatformType;
|
import org.geysermc.connector.common.PlatformType;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
|
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||||
import org.geysermc.connector.dump.BootstrapDumpInfo;
|
import org.geysermc.connector.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||||
|
@ -50,7 +56,8 @@ import java.lang.reflect.Method;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.UUID;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
|
|
||||||
|
@ -67,6 +74,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
|
|
||||||
private GeyserConnector connector;
|
private GeyserConnector connector;
|
||||||
|
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
private static final Map<String, String> argsConfigKeys = new HashMap<>();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
|
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
|
||||||
|
@ -74,6 +84,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
boolean useGuiOpts = bootstrap.useGui;
|
boolean useGuiOpts = bootstrap.useGui;
|
||||||
String configFilenameOpt = bootstrap.configFilename;
|
String configFilenameOpt = bootstrap.configFilename;
|
||||||
|
|
||||||
|
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
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
|
// 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
|
// Optionally, you can force the use of a GUI or no GUI by specifying args
|
||||||
|
@ -106,8 +118,43 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui"));
|
System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui"));
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
String badArgMsg = LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised");
|
// We have likely added a config option argument
|
||||||
System.err.println(MessageFormat.format(badArgMsg, arg));
|
if (arg.startsWith("--")) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +195,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
try {
|
try {
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
|
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
|
||||||
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
|
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
|
||||||
|
|
||||||
|
handleArgsConfigOptions();
|
||||||
|
|
||||||
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
|
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
|
||||||
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
|
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
|
||||||
geyserConfig.getRemote().setAddress("127.0.0.1");
|
geyserConfig.getRemote().setAddress("127.0.0.1");
|
||||||
|
@ -223,4 +273,99 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
public BootstrapDumpInfo getDumpInfo() {
|
public BootstrapDumpInfo getDumpInfo() {
|
||||||
return new GeyserStandaloneDumpInfo(this);
|
return new GeyserStandaloneDumpInfo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
.findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();
|
||||||
|
|
||||||
|
// Filter properties removing the ignored ones
|
||||||
|
return properties.stream()
|
||||||
|
.filter(property -> !ignoredProperties.contains(property.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
|
||||||
|
Object parsedValue = value;
|
||||||
|
|
||||||
|
// Change the values type if needed
|
||||||
|
if (int.class.equals(property.getRawPrimaryType())) {
|
||||||
|
parsedValue = Integer.valueOf((String) parsedValue);
|
||||||
|
} else if (boolean.class.equals(property.getRawPrimaryType())) {
|
||||||
|
parsedValue = Boolean.valueOf((String) parsedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force the value to be set
|
||||||
|
AnnotatedField field = property.getField();
|
||||||
|
field.fixAccess(true);
|
||||||
|
field.setValue(parentObject, parsedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the loaded {@link GeyserStandaloneConfiguration} 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);
|
||||||
|
|
||||||
|
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(LanguageUtils.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(LanguageUtils.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit b7ef31bd9c45aa3a0735883764c231f30cb55bfa
|
Subproject commit bf4b0b7103193154dd0b06e0459dc375c753069a
|
Loading…
Reference in a new issue