diff --git a/src/main/java/net/glowstone/util/config/ServerConfig.java b/src/main/java/net/glowstone/util/config/ServerConfig.java index e60d8512c4..3884ce6f25 100644 --- a/src/main/java/net/glowstone/util/config/ServerConfig.java +++ b/src/main/java/net/glowstone/util/config/ServerConfig.java @@ -66,7 +66,7 @@ public final class ServerConfig implements DynamicallyTypedMap /** * Initialize a new ServerConfig and associated settings. * - * @param directory The config directory, or null for default. + * @param directory The config directory, or null for default. * @param configFile The config file, or null for default. * @param parameters The command-line parameters used as overrides. */ @@ -83,6 +83,8 @@ public ServerConfig(File directory, File configFile, Map parameters "glowstone.yml is the main configuration file for a Glowstone server\n" + "It contains everything from server.properties and bukkit.yml in a\n" + "normal CraftBukkit installation.\n\n" + + "Configuration entries are documented on the wiki: " + + "https://github.com/GlowstoneMC/Glowstone/wiki/Configuration-Guide\n" + "For help, join us on Discord: https://discord.gg/TFJqhsC"); } @@ -103,7 +105,7 @@ public void save() { /** * Change a configuration value at runtime. * - * @param key the config key to write the value to + * @param key the config key to write the value to * @param value value to write to config key * @see ServerConfig#save() */ @@ -290,7 +292,7 @@ private void copyDefaults(String source, File dest) { } try (final InputStream in = resource.openStream(); - final OutputStream out = new FileOutputStream(dest)) { + final OutputStream out = new FileOutputStream(dest)) { byte[] buf = new byte[2048]; int len; while ((len = in.read(buf)) > 0) { @@ -400,63 +402,81 @@ private boolean migrate() { */ public enum Key { // server - SERVER_IP("server.ip", "", Migrate.PROPS, "server-ip"), + SERVER_IP("server.ip", "", Migrate.PROPS, "server-ip", String.class::isInstance), SERVER_PORT("server.port", 25565, Migrate.PROPS, "server-port", Validators.PORT), - SERVER_NAME("server.name", "Glowstone Server", Migrate.PROPS, "server-name"), - LOG_FILE("server.log-file", "logs/log-%D.txt"), - ONLINE_MODE("server.online-mode", true, Migrate.PROPS, "online-mode"), - MAX_PLAYERS("server.max-players", 20, Migrate.PROPS, "max-players", Validators.POSITIVE), - WHITELIST("server.whitelisted", false, Migrate.PROPS, "white-list"), - MOTD("server.motd", "A Glowstone server", Migrate.PROPS, "motd"), + SERVER_NAME("server.name", "Glowstone Server", Migrate.PROPS, "server-name", + String.class::isInstance), + LOG_FILE("server.log-file", "logs/log-%D.txt", String.class::isInstance), + ONLINE_MODE("server.online-mode", true, Migrate.PROPS, "online-mode", + Boolean.class::isInstance), + MAX_PLAYERS("server.max-players", 20, Migrate.PROPS, "max-players", + Validators.typeCheck(Integer.class).and(Validators.POSITIVE)), + WHITELIST("server.whitelisted", false, Migrate.PROPS, "white-list", + Boolean.class::isInstance), + MOTD("server.motd", "A Glowstone server", Migrate.PROPS, "motd", + String.class::isInstance), SHUTDOWN_MESSAGE("server.shutdown-message", "Server shutting down.", Migrate.BUKKIT, - "settings.shutdown-message"), - ALLOW_CLIENT_MODS("server.allow-client-mods", true), + "settings.shutdown-message", String.class::isInstance), + ALLOW_CLIENT_MODS("server.allow-client-mods", true, Boolean.class::isInstance), // console - USE_JLINE("console.use-jline", true), - CONSOLE_PROMPT("console.prompt", "> "), - CONSOLE_DATE("console.date-format", "HH:mm:ss"), - CONSOLE_LOG_DATE("console.log-date-format", "yyyy/MM/dd HH:mm:ss"), + USE_JLINE("console.use-jline", true, Boolean.class::isInstance), + CONSOLE_PROMPT("console.prompt", "> ", String.class::isInstance), + CONSOLE_DATE("console.date-format", "HH:mm:ss", String.class::isInstance), + CONSOLE_LOG_DATE("console.log-date-format", "yyyy/MM/dd HH:mm:ss", + String.class::isInstance), // game props - GAMEMODE("game.gamemode", "SURVIVAL", Migrate.PROPS, "gamemode", Validators - .forEnum(GameMode.class)), - FORCE_GAMEMODE("game.gamemode-force", false, Migrate.PROPS, "force-gamemode"), - DIFFICULTY("game.difficulty", "NORMAL", Migrate.PROPS, "difficulty", Validators - .forEnum(Difficulty.class)), - HARDCORE("game.hardcore", false, Migrate.PROPS, "hardcore"), - PVP_ENABLED("game.pvp", true, Migrate.PROPS, "pvp"), + GAMEMODE("game.gamemode", "SURVIVAL", Migrate.PROPS, "gamemode", + Validators.forEnum(GameMode.class)), + FORCE_GAMEMODE("game.gamemode-force", false, Migrate.PROPS, "force-gamemode", + Boolean.class::isInstance), + DIFFICULTY("game.difficulty", "NORMAL", Migrate.PROPS, "difficulty", + Validators.forEnum(Difficulty.class)), + HARDCORE("game.hardcore", false, Migrate.PROPS, "hardcore", + Boolean.class::isInstance), + PVP_ENABLED("game.pvp", true, Migrate.PROPS, "pvp", + Boolean.class::isInstance), MAX_BUILD_HEIGHT("game.max-build-height", 256, Migrate.PROPS, "max-build-height", - Validators.POSITIVE), + Validators.typeCheck(Integer.class).and(Validators.POSITIVE)), ANNOUNCE_ACHIEVEMENTS("game.announce-achievements", true, Migrate.PROPS, - "announce-player-achievements"), + "announce-player-achievements", Boolean.class::isInstance), // server.properties keys - ALLOW_FLIGHT("game.allow-flight", false, Migrate.PROPS, "allow-flight"), - ENABLE_COMMAND_BLOCK("game.command-blocks", false, Migrate.PROPS, "enable-command-block"), + ALLOW_FLIGHT("game.allow-flight", false, Migrate.PROPS, "allow-flight", + Boolean.class::isInstance), + ENABLE_COMMAND_BLOCK("game.command-blocks", false, Migrate.PROPS, "enable-command-block", + Boolean.class::isInstance), //OP_PERMISSION_LEVEL(null, Migrate.PROPS, "op-permission-level"), - RESOURCE_PACK("game.resource-pack", "", Migrate.PROPS, "resource-pack"), - RESOURCE_PACK_HASH("game.resource-pack-hash", "", Migrate.PROPS, "resource-pack-hash"), - SNOOPER_ENABLED("server.snooper-enabled", false, Migrate.PROPS, "snooper-enabled"), + RESOURCE_PACK("game.resource-pack", "", Migrate.PROPS, "resource-pack", + String.class::isInstance), + RESOURCE_PACK_HASH("game.resource-pack-hash", "", Migrate.PROPS, "resource-pack-hash", + String.class::isInstance), + SNOOPER_ENABLED("server.snooper-enabled", false, Migrate.PROPS, "snooper-enabled", + Boolean.class::isInstance), PREVENT_PROXY("server.prevent-proxy-connections", true, Migrate.PROPS, - "prevent-proxy-connections"), + "prevent-proxy-connections", Boolean.class::isInstance), // critters - SPAWN_MONSTERS("creatures.enable.monsters", true, Migrate.PROPS, "spawn-monsters"), - SPAWN_ANIMALS("creatures.enable.animals", true, Migrate.PROPS, "spawn-animals"), - SPAWN_NPCS("creatures.enable.npcs", true, Migrate.PROPS, "spawn-npcs"), + SPAWN_MONSTERS("creatures.enable.monsters", true, Migrate.PROPS, "spawn-monsters", + Boolean.class::isInstance), + SPAWN_ANIMALS("creatures.enable.animals", true, Migrate.PROPS, "spawn-animals", + Boolean.class::isInstance), + SPAWN_NPCS("creatures.enable.npcs", true, Migrate.PROPS, "spawn-npcs", + Boolean.class::isInstance), MONSTER_LIMIT("creatures.limit.monsters", 70, Migrate.BUKKIT, "spawn-limits.monsters", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), ANIMAL_LIMIT("creatures.limit.animals", 15, Migrate.BUKKIT, "spawn-limits.animals", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), WATER_ANIMAL_LIMIT("creatures.limit.water", 5, Migrate.BUKKIT, - "spawn-limits.water-animals", Validators.ABSOLUTE), + "spawn-limits.water-animals", + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), AMBIENT_LIMIT("creatures.limit.ambient", 15, Migrate.BUKKIT, "spawn-limits.ambient", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), MONSTER_TICKS("creatures.ticks.monsters", 1, Migrate.BUKKIT, "ticks-per.monster-spawns", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), ANIMAL_TICKS("creatures.ticks.animal", 400, Migrate.BUKKIT, "ticks-per.animal-spawns", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), // folders PLUGIN_FOLDER("folders.plugins", "plugins", Validators.PATH), @@ -474,70 +494,98 @@ public enum Key { // advanced CONNECTION_THROTTLE("advanced.connection-throttle", 4000, Migrate.BUKKIT, - "settings.connection-throttle", Validators.ABSOLUTE), + "settings.connection-throttle", + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), //PING_PACKET_LIMIT( // "advanced.ping-packet-limit", 100, Migrate.BUKKIT, "settings.ping-packet-limit"), PLAYER_IDLE_TIMEOUT("advanced.idle-timeout", 0, Migrate.PROPS, "player-idle-timeout", - Validators.ABSOLUTE), + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), WARN_ON_OVERLOAD("advanced.warn-on-overload", true, Migrate.BUKKIT, - "settings.warn-on-overload"), + "settings.warn-on-overload", Boolean.class::isInstance), EXACT_LOGIN_LOCATION("advanced.exact-login-location", false, Migrate.BUKKIT, - "settings.use-exact-login-location"), + "settings.use-exact-login-location", Boolean.class::isInstance), PLUGIN_PROFILING("advanced.plugin-profiling", false, Migrate.BUKKIT, - "settings.plugin-profiling"), + "settings.plugin-profiling", Boolean.class::isInstance), WARNING_STATE("advanced.deprecated-verbose", "false", Migrate.BUKKIT, "settings.deprecated-verbose"), COMPRESSION_THRESHOLD("advanced.compression-threshold", 256, Migrate.PROPS, - "network-compression-threshold", Validators.ABSOLUTE.or((value) -> value == -1)), - PROXY_SUPPORT("advanced.proxy-support", false), - PLAYER_SAMPLE_COUNT("advanced.player-sample-count", 12, Validators.ABSOLUTE), + "network-compression-threshold", + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE) + .or((value) -> value == -1)), + PROXY_SUPPORT("advanced.proxy-support", false, Boolean.class::isInstance), + PLAYER_SAMPLE_COUNT("advanced.player-sample-count", 12, + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), GRAPHICS_COMPUTE("advanced.graphics-compute.enable", false), - GRAPHICS_COMPUTE_ANY_DEVICE("advanced.graphics-compute.use-any-device", false), - REGION_CACHE_SIZE("advanced.region-file.cache-size", 256, Validators.ABSOLUTE), - REGION_COMPRESSION("advanced.region-file.compression", true), - PROFILE_LOOKUP_TIMEOUT("advanced.profile-lookup-timeout", 5, Validators.ABSOLUTE), - LIBRARY_CHECKSUM_VALIDATION("advanced.library-checksum-validation", true), + GRAPHICS_COMPUTE_ANY_DEVICE("advanced.graphics-compute.use-any-device", false, + Boolean.class::isInstance), + REGION_CACHE_SIZE("advanced.region-file.cache-size", 256, + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), + REGION_COMPRESSION("advanced.region-file.compression", true, + Boolean.class::isInstance), + PROFILE_LOOKUP_TIMEOUT("advanced.profile-lookup-timeout", 5, + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), + LIBRARY_CHECKSUM_VALIDATION("advanced.library-checksum-validation", true, + Boolean.class::isInstance), LIBRARY_REPOSITORY_URL("advanced.library-repository-url", - "https://repo.glowstone.net/service/local/repositories/central/content/"), - LIBRARY_DOWNLOAD_ATTEMPTS("advanced.library-download-attempts", 2, Validators.POSITIVE), + "https://repo.glowstone.net/service/local/repositories/central/content/", + String.class::isInstance), + LIBRARY_DOWNLOAD_ATTEMPTS("advanced.library-download-attempts", 2, + Validators.typeCheck(Integer.class).and(Validators.POSITIVE)), SUGGEST_PLAYER_NAMES_WHEN_NULL_TAB_COMPLETIONS( - "advanced.suggest-player-name-when-null-tab-completions", true), + "advanced.suggest-player-name-when-null-tab-completions", true, + Boolean.class::isInstance), // query rcon etc - QUERY_ENABLED("extras.query-enabled", false, Migrate.PROPS, "enable-query"), + QUERY_ENABLED("extras.query-enabled", false, Migrate.PROPS, "enable-query", + Boolean.class::isInstance), QUERY_PORT("extras.query-port", 25614, Migrate.PROPS, "query.port", Validators.PORT), - QUERY_PLUGINS("extras.query-plugins", true, Migrate.BUKKIT, "settings.query-plugins"), - RCON_ENABLED("extras.rcon-enabled", false, Migrate.PROPS, "enable-rcon"), - RCON_PASSWORD("extras.rcon-password", "glowstone", Migrate.PROPS, "rcon.password"), + QUERY_PLUGINS("extras.query-plugins", true, Migrate.BUKKIT, "settings.query-plugins", + Boolean.class::isInstance), + RCON_ENABLED("extras.rcon-enabled", false, Migrate.PROPS, "enable-rcon", + Boolean.class::isInstance), + RCON_PASSWORD("extras.rcon-password", "glowstone", Migrate.PROPS, "rcon.password", + String.class::isInstance), RCON_PORT("extras.rcon-port", 25575, Migrate.PROPS, "rcon.port", Validators.PORT), - RCON_COLORS("extras.rcon-colors", true), + RCON_COLORS("extras.rcon-colors", true, + Boolean.class::isInstance), // level props - LEVEL_NAME("world.name", "world", Migrate.PROPS, "level-name"), + LEVEL_NAME("world.name", "world", Migrate.PROPS, "level-name", + String.class::isInstance), LEVEL_SEED("world.seed", "", Migrate.PROPS, "level-seed"), LEVEL_TYPE("world.level-type", "DEFAULT", Migrate.PROPS, "level-type", Validators .WORLD_TYPE), - SPAWN_RADIUS("world.spawn-radius", 16, Migrate.PROPS, "spawn-protection", Validators - .ABSOLUTE), - VIEW_DISTANCE("world.view-distance", 8, Migrate.PROPS, "view-distance", Validators - .POSITIVE), - GENERATE_STRUCTURES("world.gen-structures", true, Migrate.PROPS, "generate-structures"), - ALLOW_NETHER("world.allow-nether", true, Migrate.PROPS, "allow-nether"), - ALLOW_END("world.allow-end", true, Migrate.BUKKIT, "settings.allow-end"), - PERSIST_SPAWN("world.keep-spawn-loaded", true), - POPULATE_ANCHORED_CHUNKS("world.populate-anchored-chunks", true), - WATER_CLASSIC("world.classic-style-water", false), - DISABLE_GENERATION("world.disable-generation", false), + SPAWN_RADIUS("world.spawn-radius", 16, Migrate.PROPS, "spawn-protection", + Validators.typeCheck(Integer.class).and(Validators.ABSOLUTE)), + VIEW_DISTANCE("world.view-distance", 8, Migrate.PROPS, "view-distance", + Validators.typeCheck(Integer.class).and(Validators.POSITIVE)), + GENERATE_STRUCTURES("world.gen-structures", true, Migrate.PROPS, "generate-structures", + Boolean.class::isInstance), + ALLOW_NETHER("world.allow-nether", true, Migrate.PROPS, "allow-nether", + Boolean.class::isInstance), + ALLOW_END("world.allow-end", true, Migrate.BUKKIT, "settings.allow-end", + Boolean.class::isInstance), + PERSIST_SPAWN("world.keep-spawn-loaded", true, + Boolean.class::isInstance), + POPULATE_ANCHORED_CHUNKS("world.populate-anchored-chunks", true, + Boolean.class::isInstance), + WATER_CLASSIC("world.classic-style-water", false, + Boolean.class::isInstance), + DISABLE_GENERATION("world.disable-generation", false, + Boolean.class::isInstance), // database - DB_DRIVER("database.driver", "org.sqlite.JDBC", Migrate.BUKKIT, "database.driver"), - DB_URL("database.url", "jdbc:sqlite:config/database.db", Migrate.BUKKIT, "database.url"), + DB_DRIVER("database.driver", "org.sqlite.JDBC", Migrate.BUKKIT, "database.driver", + String.class::isInstance), + DB_URL("database.url", "jdbc:sqlite:config/database.db", Migrate.BUKKIT, "database.url", + String.class::isInstance), DB_USERNAME("database.username", "glowstone", Migrate.BUKKIT, "database.username"), DB_PASSWORD("database.password", "nether", Migrate.BUKKIT, "database.password"), - DB_ISOLATION("database.isolation", "SERIALIZABLE", Migrate.BUKKIT, "database.isolation"), + DB_ISOLATION("database.isolation", "SERIALIZABLE", Migrate.BUKKIT, "database.isolation", + String.class::isInstance), // libraries - LIBRARIES("libraries", DEFAULT_LIBRARIES_VALUE),; + LIBRARIES("libraries", DEFAULT_LIBRARIES_VALUE); @Getter private final String path; @@ -590,6 +638,9 @@ static final class EnumPredicate implements Predicate { @Override public boolean test(String value) { + if (!Validators.typeCheck(String.class).test(value)) { + return false; + } if (value == null || value.isEmpty()) { return false; } @@ -606,30 +657,32 @@ static class Validators { /** * Checks if the value is positive (over zero). */ - static final Predicate POSITIVE = (number) -> number > 0; + static final Predicate POSITIVE = (number) -> number.doubleValue() > 0; /** * Checks if the value is zero. */ - static final Predicate ZERO = (number) -> number == 0; + static final Predicate ZERO = (number) -> number.doubleValue() == 0; /** * Checks if the value is greater than (positive) or equal to zero. */ - static final Predicate ABSOLUTE = POSITIVE.or(ZERO); + static final Predicate ABSOLUTE = POSITIVE.or(ZERO); /** * Checks if the value is a valid port number. */ - static final Predicate PORT = POSITIVE.and((number) -> number < 49151); + static final Predicate PORT = typeCheck(Integer.class) + .and(POSITIVE).and((number) -> number < 49152); /** * Checks if the value is a valid {@link WorldType} name. */ - static final Predicate WORLD_TYPE = (value) -> WorldType.getByName(value) != null; + static final Predicate WORLD_TYPE = typeCheck(String.class) + .and((value) -> WorldType.getByName(value) != null); /** * Creates a {@link EnumPredicate} that checks if the value is a member of the given enum * class. * * @param enumClass the enum class - * @param the type of the enum + * @param the type of the enum * @return the predicate */ static EnumPredicate forEnum(Class enumClass) { @@ -641,7 +694,7 @@ static EnumPredicate forEnum(Class enumClass) { * *

Note that the behavior of this predicate may be platform-dependent. */ - static final Predicate PATH = (value) -> { + static final Predicate PATH = Validators.typeCheck(String.class).and((value) -> { try { if (Paths.get(value) == null) { return false; @@ -650,7 +703,18 @@ static EnumPredicate forEnum(Class enumClass) { return false; } return true; - }; + }); + + /** + * Creates a {@link Predicate} that checks if the value is an instance of the + * specified class. + * + * @param expected the expected class. + * @return the predicate + */ + static Predicate typeCheck(Class expected) { + return expected::isInstance; + } } private enum Migrate {