diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000000..29217e86391
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,36 @@
+FROM gradle:8.5.0-jdk17-alpine as builder
+
+WORKDIR /app
+
+COPY ./ /app/
+
+RUN gradle jar --no-daemon
+
+FROM bitnami/git:2.43.0-debian-11-r1 as data
+
+ARG DATA_REPOSITORY=https://gitlab.com/YuukiPS/GC-Resources.git
+ARG DATA_BRANCH=4.0
+
+WORKDIR /app
+
+RUN git clone --branch ${DATA_BRANCH} --depth 1 ${DATA_REPOSITORY}
+
+FROM bitnami/java:17.0.9-11
+
+RUN apt-get update && apt-get install unzip
+
+WORKDIR /app
+
+# Copy built assets
+COPY --from=builder /app/grasscutter-1.7.4.jar /app/grasscutter.jar
+COPY --from=builder /app/keystore.p12 /app/keystore.p12
+
+# Copy the resources
+COPY --from=data /app/GC-Resources/Resources /app/resources/
+
+# Copy startup files
+COPY ./entrypoint.sh /app/
+
+CMD [ "sh", "/app/entrypoint.sh" ]
+
+EXPOSE 80 443 8888 22102
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000000..6b8d08bf3a2
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,30 @@
+version: "3.8"
+services:
+ grasscutter:
+ image: grasscutter:latest
+ build: .
+ ports:
+ - "80:80"
+ - "443:443"
+ - "8080:8080"
+ - "8888:8888"
+ - "22102:22102"
+ environment:
+ DATABASE_INFO_SERVER_CONNECTION_URI: "mongodb://lawnmower:grasscutter@database:27017"
+ DATABASE_INFO_SERVER_COLLECTION: grasscutter
+ DATABASE_INFO_GAME_CONNECTION_URI: "mongodb://lawnmower:grasscutter@database:27017"
+ DATABASE_INFO_GAME_COLLECTION: grasscutter
+
+ stdin_open: true
+
+ database:
+ image: mongo:7.0.4
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: lawnmower
+ MONGO_INITDB_ROOT_PASSWORD: grasscutter
+ MONGO_INITDB_DATABASE: grasscutter
+ volumes:
+ - mongodata:/data/db
+
+volumes:
+ mongodata:
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100755
index 00000000000..41bfceffad3
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,3 @@
+#/bin/sh
+
+java -jar /app/grasscutter.jar
diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java
index 8c3248dd7c2..197126ec065 100644
--- a/src/main/java/emu/grasscutter/Grasscutter.java
+++ b/src/main/java/emu/grasscutter/Grasscutter.java
@@ -77,8 +77,6 @@ public final class Grasscutter {
// Load server configuration.
Grasscutter.loadConfig();
- // Attempt to update configuration.
- ConfigContainer.updateConfig();
Grasscutter.getLogger().info("Loading Grasscutter...");
@@ -238,22 +236,7 @@ public static void loadLanguage() {
/** Attempts to load the configuration from a file. */
public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config.
- if (!configFile.exists()) {
- getLogger().info("config.json could not be found. Generating a default configuration ...");
- config = new ConfigContainer();
- Grasscutter.saveConfig(config);
- return;
- }
-
- // If the file already exists, we attempt to load it.
- try {
- config = JsonUtils.loadToClass(configFile.toPath(), ConfigContainer.class);
- } catch (Exception exception) {
- getLogger()
- .error(
- "There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
- System.exit(1);
- }
+ config = new ConfigContainer();
}
/**
diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java
index fd264e26bb8..e145360a012 100644
--- a/src/main/java/emu/grasscutter/config/ConfigContainer.java
+++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java
@@ -1,127 +1,412 @@
package emu.grasscutter.config;
import ch.qos.logback.classic.Level;
-import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;
-import emu.grasscutter.Grasscutter;
-import emu.grasscutter.utils.*;
+import emu.grasscutter.utils.Crypto;
+import emu.grasscutter.utils.Utils;
import lombok.NoArgsConstructor;
-import java.util.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
-import static emu.grasscutter.Grasscutter.*;
+import static emu.grasscutter.Grasscutter.ServerDebugMode;
+import static emu.grasscutter.Grasscutter.ServerRunMode;
/**
* *when your JVM fails*
*/
public class ConfigContainer {
- /*
- * Configuration changes:
- * Version 5 - 'questing' has been changed from a boolean
- * to a container of options ('questOptions').
- * This field will be removed in future versions.
- * Version 6 - 'questing' has been fully replaced with 'questOptions'.
- * The field for 'legacyResources' has been removed.
- * Version 7 - 'regionKey' is being added for authentication
- * with the new dispatch server.
- * Version 8 - 'server' is being added for enforcing handbook server
- * addresses.
- * Version 9 - 'limits' was added for handbook requests.
- * Version 10 - 'trialCostumes' was added for enabling costumes
- * on trial avatars.
- * Version 11 - 'server.fastRequire' was added for disabling the new
- * Lua script require system if performance is a concern.
- * Version 12 - 'http.startImmediately' was added to control whether the
- * HTTP server should start immediately.
- * Version 13 - 'game.useUniquePacketKey' was added to control whether the
- * encryption key used for packets is a constant or randomly generated.
+ /**
+ * Retrieves the given key from the environment variables.
+ *
+ * When the key is not set it will return the given default value.
+ *
+ * @param key The name of the environment variable
+ * @param defaultValue The default value when the key is not set
+ * @return The value from the environment variable or the default value
*/
- private static int version() {
- return 13;
+ static String getStringFromEnv(String key, String defaultValue) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ return currentValue;
}
/**
- * Attempts to update the server's existing configuration.
+ * Retrieves the given key from the environment variables and tries to parse it as integer.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exists or is not a valid integer
+ * @return The parsed integer or the default value
*/
- public static void updateConfig() {
- try { // Check if the server is using a legacy config.
- var configObject = JsonUtils.loadToClass(Grasscutter.configFile.toPath(), JsonObject.class);
- if (!configObject.has("version")) {
- Grasscutter.getLogger().info("Updating legacy config...");
- Grasscutter.saveConfig(null);
- }
- } catch (Exception ignored) { }
-
- var existing = config.version;
- var latest = version();
-
- if (existing == latest)
- return;
-
- // Create a new configuration instance.
- var updated = new ConfigContainer();
- // Update all configuration fields.
- var fields = ConfigContainer.class.getDeclaredFields();
- Arrays.stream(fields).forEach(field -> {
- try {
- field.set(updated, field.get(config));
- } catch (Exception exception) {
- Grasscutter.getLogger().error("Failed to update a configuration field.", exception);
- }
- }); updated.version = version();
+ static int getIntFromEnv(String key, int defaultValue) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ try {
+ return Integer.parseInt(currentValue, 10);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as float.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist or is not a valid float
+ * @return The parsed float or the default value
+ */
+ static float getFloatFromEnv(String key, float defaultValue) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ try {
+ return Float.parseFloat(currentValue);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as float.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exists or is not a valid bool
+ * @return The parsed boolean or the default value
+ */
+ static boolean getBoolFromEnv(String key, boolean defaultValue) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ return switch (currentValue.trim()) {
+ case "true", "on", "1" -> true;
+ case "false", "off", "0" -> false;
+ default -> defaultValue;
+ };
+ }
+
+ /**
+ * Retrieves the given from the environment variables and tries to parse it as a Set.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * hello,world
+ *
+ * | |- second entry
+ *
+ * |- first entry
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist or is not a valid set
+ * @param separator The separator which will be used for splitting up the string
+ * @return The parsed set or the default value
+ */
+ static Set getStringSetFromEnv(String key, Set defaultValue, String separator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ var parts = currentValue.split(separator);
+
+ return Set.of(parts);
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as a Set.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * 42,1337
+ *
+ * | |- second entry
+ *
+ * |- first entry
+ *
+ * @param key The name of the environment variable
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param separator The separator which will be used for splitting up the environment variable
+ * @return The parsed integer set or the default value
+ */
+ static Set getIntSetFromEnv(String key, Set defaultValue, String separator) {
+ var defaultValues = defaultValue.stream().map(Object::toString).collect(Collectors.toSet());
+ var currentValue = getStringSetFromEnv(key, defaultValues, separator);
+
+ return currentValue.stream().map(entry -> Integer.parseInt(entry, 10)).collect(Collectors.toSet());
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as an enum member.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * @param key The name of the environment variable to parse
+ * @param enumClass The enum class which contains all members
+ * @param defaultValue The default value when the environment variable does not exists or is not a valid enum member
+ * @param The type of the enum member
+ * @return The parsed enum member or the default value
+ */
+ static > T getEnumFromEnv(String key, Class enumClass, T defaultValue) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
- try { // Save configuration and reload.
- Grasscutter.saveConfig(updated);
- Grasscutter.loadConfig();
- } catch (Exception exception) {
- Grasscutter.getLogger().warn("Failed to save the updated configuration.", exception);
+ try {
+ return Enum.valueOf(enumClass, currentValue);
+ } catch (Exception e) {
+ return defaultValue;
}
}
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as string array.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * hello,world
+ *
+ * | |- second entry
+ *
+ * |- first entry
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param separator The separator which will be used for splitting up the string
+ * @return The parsed string array or the default value
+ */
+ static String[] getStringArrayFromEnv(String key, String[] defaultValue, String separator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ return currentValue.split(separator);
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as an int array.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * 1,2
+ *
+ * | |- second entry
+ *
+ * |- first entry
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param separator The separator which will be used for splitting up the string
+ * @return The parsed int array or the default value
+ */
+ static int[] getIntArrayFromEnv(String key, int[] defaultValue, String separator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ return Arrays.stream(currentValue.split(separator)).mapToInt(Integer::parseInt).toArray();
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as a MailItem array.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * 1,1,1|2,1,1
+ *
+ * | | |- Item level
+ *
+ * | |- Item count
+ *
+ * |- Item ID
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param partsSeparator The separator which will be used for splitting up the string into parts
+ * @param valuesSeparator The separator which will be used for splitting up the parts into values
+ * @return The parsed MailItem array or the default value
+ */
+ static emu.grasscutter.game.mail.Mail.MailItem[] getMailItemsFromEnv(String key, emu.grasscutter.game.mail.Mail.MailItem[] defaultValue, String partsSeparator, String valuesSeparator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ var parts = Arrays.stream(currentValue.split(partsSeparator)).map(part -> part.split(valuesSeparator));
+
+ return (emu.grasscutter.game.mail.Mail.MailItem[]) parts.filter(part -> part.length != 3).map(part -> {
+ var itemId = Integer.parseInt(part[0], 10);
+ var itemCount = Integer.parseInt(part[1], 10);
+ var itemLevel = Integer.parseInt(part[2], 10);
+
+ return new emu.grasscutter.game.mail.Mail.MailItem(itemId, itemCount, itemLevel);
+ }).toArray();
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as a MailItem array.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ * VISION_LEVEL_NORMAL,80,20|VISION_LEVEL_LITTLE_REMOTE,16,40
+ *
+ * | | |- Grid width
+ *
+ * | |- Vision range
+ *
+ * |- Name
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param partsSeparator The separator which will be used for splitting up the string into parts
+ * @param valuesSeparator The separator which will be used for splitting up the parts into values
+ * @return The parsed VisionOptions or the default value
+ */
+ static VisionOptions[] getVisionOptionsFromEnv(String key, VisionOptions[] defaultValue, String partsSeparator, String valuesSeparator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ var parts = currentValue.split(partsSeparator);
+ return (VisionOptions[]) Arrays.stream(parts).map(part -> part.split(valuesSeparator)).filter(values -> values.length == 3).map(values -> {
+ var name = values[0];
+ var visionRange = Integer.parseInt(values[1]);
+ var gridWidth = Integer.parseInt(values[2]);
+
+ return new VisionOptions(name, visionRange, gridWidth);
+ }).toArray();
+ }
+
+ /**
+ * Retrieves the given key from the environment variables and tries to parse it as a Region array.
+ *
+ * If the environment variable is not present or the parsing fails then the default value will be returned.
+ *
+ * It expects the following structure:
+ *
+ * my region,my title,127.0.0.1,1337|my second region, my second title,127.0.0.1,42
+ *
+ * | | | |- port
+ *
+ * | | |- address
+ *
+ * | |- title
+ *
+ * |- name
+ *
+ * @param key The name of the environment variable to parse
+ * @param defaultValue The default value when the environment variable does not exist
+ * @param partsSeparator The separator which will be used for splitting up the string into parts
+ * @param valuesSeparator The separator which will be used for splitting up the parts into values
+ * @return The parsed Region list or the default value
+ */
+ static List getRegionsFromEnv(String key, List defaultValue, String partsSeparator, String valuesSeparator) {
+ var currentValue = System.getenv(key);
+
+ if (currentValue == null) {
+ return defaultValue;
+ }
+
+ var parts = currentValue.split(partsSeparator);
+ return Arrays.stream(parts).map(part -> part.split(valuesSeparator)).filter(values -> values.length == 4).map(values -> {
+ var name = values[0];
+ var title = values[1];
+ var address = values[2];
+ var port = Integer.parseInt(values[3]);
+
+ return new Region(name, title, address, port);
+ }).collect(Collectors.toList());
+ }
+
public Structure folderStructure = new Structure();
public Database databaseInfo = new Database();
public Language language = new Language();
public Account account = new Account();
public Server server = new Server();
- // DO NOT. TOUCH. THE VERSION NUMBER.
- public int version = version();
-
/* Option containers. */
public static class Database {
- public DataStore server = new DataStore();
- public DataStore game = new DataStore();
+ public DataStore server = new DataStore("SERVER");
+ public DataStore game = new DataStore("GAME");
public static class DataStore {
- public String connectionUri = "mongodb://localhost:27017";
- public String collection = "grasscutter";
+ public String connectionUri;
+ public String collection;
+
+ public DataStore(String key) {
+ this.connectionUri = getStringFromEnv("DATABASE_INFO_" + key + "_CONNECTION_URI", "mongodb://localhost:27017");
+ this.collection = getStringFromEnv("DATABASE_INFO_" + key + "_COLLECTION", "grasscutter");
+ }
}
}
public static class Structure {
- public String resources = "./resources/";
- public String data = "./data/";
- public String packets = "./packets/";
- public String scripts = "resources:Scripts/";
- public String plugins = "./plugins/";
- public String cache = "./cache/";
+ public String resources = getStringFromEnv("FOLDER_STRUCTURE_RESOURCES", "./resources/");
+ public String data = getStringFromEnv("FOLDER_STRUCTURE_DATA", "./data/");
+ public String packets = getStringFromEnv("FOLDER_STRUCTURE_PACKETS", "./packets/");
+ public String scripts = getStringFromEnv("FOLDER_STRUCTURE_SCRIPTS", "resources:Scripts/");
+ public String plugins = getStringFromEnv("FOLDER_STRUCTURE_PLUGINS", "./plugins/");
+ public String cache = getStringFromEnv("FOLDER_STRUCTURE_CACHE", "./cache/");
// UNUSED (potentially added later?)
// public String dumps = "./dumps/";
}
public static class Server {
- public Set debugWhitelist = Set.of();
- public Set debugBlacklist = Set.of();
- public ServerRunMode runMode = ServerRunMode.HYBRID;
- public boolean logCommands = false;
+ public Set debugWhitelist = getIntSetFromEnv("SERVER_DEBUG_WHITELIST", Set.of(), ",");
+ public Set debugBlacklist = getIntSetFromEnv("SERVER_DEBUG_BLACKLIST", Set.of(), ",");
+ public ServerRunMode runMode = getEnumFromEnv("SERVER_RUN_MODE", ServerRunMode.class, ServerRunMode.HYBRID);
+
+ public boolean logCommands = getBoolFromEnv("SERVER_LOG_COMMANDS", false);
/**
- * If enabled, the 'require' Lua function will load the script's compiled varient into the context. (faster; doesn't work as well)
+ * If enabled, the 'require' Lua function will load the script's compiled variant into the context. (faster; doesn't work as well)
* If disabled, all 'require' calls will be replaced with the referenced script's source. (slower; works better)
*/
- public boolean fastRequire = true;
+ public boolean fastRequire = getBoolFromEnv("SERVER_FAST_REQUIRE", true);
public HTTP http = new HTTP();
public Game game = new Game();
@@ -133,29 +418,29 @@ public static class Server {
public static class Language {
public Locale language = Locale.getDefault();
public Locale fallback = Locale.US;
- public String document = "EN";
+ public String document = getStringFromEnv("LANGUAGE_DOCUMENT", "EN");
}
public static class Account {
- public boolean autoCreate = false;
- public boolean EXPERIMENTAL_RealPassword = false;
- public String[] defaultPermissions = {};
- public int maxPlayer = -1;
+ public boolean autoCreate = getBoolFromEnv("ACCOUNT_AUTO_CREATE", false);
+ public boolean EXPERIMENTAL_RealPassword = getBoolFromEnv("ACCOUNT_EXPERIMENTAL_REAL_PASSWORD", false);
+ public String[] defaultPermissions = getStringArrayFromEnv("ACCOUNT_DEFAULT_PERMISSIONS", new String[]{}, ",");
+ public int maxPlayer = getIntFromEnv("ACCOUNT_MAX_PLAYER", -1);
}
/* Server options. */
public static class HTTP {
/* This starts the HTTP server before the game server. */
- public boolean startImmediately = false;
+ public boolean startImmediately = getBoolFromEnv("SERVER_HTTP_START_IMMEDIATELY", false);
- public String bindAddress = "0.0.0.0";
- public int bindPort = 443;
+ public String bindAddress = getStringFromEnv("SERVER_HTTP_BIND_ADDRESS", "0.0.0.0");
+ public int bindPort = getIntFromEnv("SERVER_HTTP_BIND_PORT", 443);
/* This is the address used in URLs. */
- public String accessAddress = "127.0.0.1";
+ public String accessAddress = getStringFromEnv("SERVER_HTTP_ACCESS_ADDRESS", "127.0.0.1");
/* This is the port used in URLs. */
- public int accessPort = 0;
+ public int accessPort = getIntFromEnv("SERVER_HTTP_ACCESS_PORT", 0);
public Encryption encryption = new Encryption();
public Policies policies = new Policies();
@@ -163,66 +448,65 @@ public static class HTTP {
}
public static class Game {
- public String bindAddress = "0.0.0.0";
- public int bindPort = 22102;
+ public String bindAddress = getStringFromEnv("SERVER_GAME_BIND_ADDRESS", "0.0.0.0");
+ public int bindPort = getIntFromEnv("SERVER_GAME_BIND_PORT", 22102);
/* This is the address used in the default region. */
- public String accessAddress = "127.0.0.1";
+ public String accessAddress = getStringFromEnv("SERVER_GAME_ACCESS_ADDRESS", "127.0.0.1");
/* This is the port used in the default region. */
- public int accessPort = 0;
+ public int accessPort = getIntFromEnv("SERVER_GAME_ACCESS_PORT", 0);
/* Enabling this will generate a unique packet encryption key for each player. */
- public boolean useUniquePacketKey = true;
+ public boolean useUniquePacketKey = getBoolFromEnv("SERVER_GAME_USE_UNIQUE_PACKET_KEY", true);
/* Entities within a certain range will be loaded for the player */
- public int loadEntitiesForPlayerRange = 300;
+ public int loadEntitiesForPlayerRange = getIntFromEnv("SERVER_GAME_LOAD_ENTITIES_FOR_PLAYER_RANGE", 300);
/* Start in 'unstable-quests', Lua scripts will be enabled by default. */
- public boolean enableScriptInBigWorld = true;
- public boolean enableConsole = true;
+ public boolean enableScriptInBigWorld = getBoolFromEnv("SERVER_GAME_ENABLE_SCRIPT_IN_BIG_WORLD", true);
+ public boolean enableConsole = getBoolFromEnv("SERVER_GAME_ENABLE_CONSOLE", true);
/* Kcp internal work interval (milliseconds) */
- public int kcpInterval = 20;
+ public int kcpInterval = getIntFromEnv("SERVER_GAME_KCP_INTERVAL", 20);
/* Controls whether packets should be logged in console or not */
- public ServerDebugMode logPackets = ServerDebugMode.NONE;
+ public ServerDebugMode logPackets = getEnumFromEnv("SERVER_GAME_LOG_PACKETS", ServerDebugMode.class, ServerDebugMode.NONE);
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
- public boolean isShowPacketPayload = false;
+ public boolean isShowPacketPayload = getBoolFromEnv("SERVER_GAME_IS_SHOW_PACKET_PAYLOAD", false);
/* Show annoying loop packets or no */
- public boolean isShowLoopPackets = false;
+ public boolean isShowLoopPackets = getBoolFromEnv("SERVER_GAME_IS_SHOW_LOOP_PACKETS", false);
- public boolean cacheSceneEntitiesEveryRun = false;
+ public boolean cacheSceneEntitiesEveryRun = getBoolFromEnv("SERVER_GAME_CACHE_SCENE_ENTITIES_EVERY_RUN", false);
public GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions();
public ConsoleAccount serverAccount = new ConsoleAccount();
- public VisionOptions[] visionOptions = new VisionOptions[] {
- new VisionOptions("VISION_LEVEL_NORMAL" , 80 , 20),
- new VisionOptions("VISION_LEVEL_LITTLE_REMOTE" , 16 , 40),
- new VisionOptions("VISION_LEVEL_REMOTE" , 1000 , 250),
- new VisionOptions("VISION_LEVEL_SUPER" , 4000 , 1000),
- new VisionOptions("VISION_LEVEL_NEARBY" , 40 , 20),
- new VisionOptions("VISION_LEVEL_SUPER_NEARBY" , 20 , 20)
- };
+ public VisionOptions[] visionOptions = getVisionOptionsFromEnv("SERVER_GAME_VISION_OPTIONS", new VisionOptions[]{
+ new VisionOptions("VISION_LEVEL_NORMAL", 80, 20),
+ new VisionOptions("VISION_LEVEL_LITTLE_REMOTE", 16, 40),
+ new VisionOptions("VISION_LEVEL_REMOTE", 1000, 250),
+ new VisionOptions("VISION_LEVEL_SUPER", 4000, 1000),
+ new VisionOptions("VISION_LEVEL_NEARBY", 40, 20),
+ new VisionOptions("VISION_LEVEL_SUPER_NEARBY", 20, 20)
+ }, "|", ",");
}
/* Data containers. */
public static class Dispatch {
/* An array of servers. */
- public List regions = List.of();
+ public List regions = getRegionsFromEnv("SERVER_DISPATCH_REGIONS", List.of(), "|", ",");
/* The URL used to make HTTP requests to the dispatch server. */
- public String dispatchUrl = "ws://127.0.0.1:1111";
+ public String dispatchUrl = getStringFromEnv("SERVER_DISPATCH_DISPATCH_URL", "ws://127.0.0.1:1111");
/* A unique key used for encryption. */
- public byte[] encryptionKey = Crypto.createSessionKey(32);
+ public byte[] encryptionKey = Utils.base64Decode(getStringFromEnv("SERVER_DISPATCH_ENCRYPTION_KEY", Utils.base64Encode(Crypto.createSessionKey(32))));
/* A unique key used for authentication. */
- public String dispatchKey = Utils.base64Encode(
- Crypto.createSessionKey(32));
+ public String dispatchKey = getStringFromEnv("SERVER_DISPATCH_DISPATCH_KEY", Utils.base64Encode(Crypto.createSessionKey(32)));
- public String defaultName = "Grasscutter";
+ public String defaultName = getStringFromEnv("SERVER_DISPATCH_DEFAULT_NAME", "Grasscutter");
/* Controls whether http requests should be logged in console or not */
- public ServerDebugMode logRequests = ServerDebugMode.NONE;
+ public ServerDebugMode logRequests = getEnumFromEnv("SERVER_DISPATCH_SERVER_DEBUG_MODE", ServerDebugMode.class, ServerDebugMode.NONE);
}
/* Debug options container, used when jar launch argument is -debug | -debugall and override default values
@@ -236,46 +520,46 @@ public static class DebugMode {
public Level servicesLoggersLevel = Level.INFO;
/* Controls whether packets should be logged in console or not */
- public ServerDebugMode logPackets = ServerDebugMode.ALL;
+ public ServerDebugMode logPackets = getEnumFromEnv("SERVER_DEBUG_MODE_LOG_PACKETS", ServerDebugMode.class, ServerDebugMode.ALL);
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
- public boolean isShowPacketPayload = false;
+ public boolean isShowPacketPayload = getBoolFromEnv("SERVER_DEBUG_MODE_IS_SHOW_PACKET_PAYLOAD", false);
/* Show annoying loop packets or no */
- public boolean isShowLoopPackets = false;
+ public boolean isShowLoopPackets = getBoolFromEnv("SERVER_DEBUG_MODE_IS_SHOW_LOOP_PACKETS", false);
/* Controls whether http requests should be logged in console or not */
- public ServerDebugMode logRequests = ServerDebugMode.ALL;
+ public ServerDebugMode logRequests = getEnumFromEnv("SERVER_DEBUG_MODE_LOG_REQUESTS", ServerDebugMode.class, ServerDebugMode.ALL);
}
public static class Encryption {
- public boolean useEncryption = true;
+ public boolean useEncryption = getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_ENCRYPTION", true);
/* Should 'https' be appended to URLs? */
- public boolean useInRouting = true;
- public String keystore = "./keystore.p12";
- public String keystorePassword = "123456";
+ public boolean useInRouting = getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_IN_ROUTING", true);
+ public String keystore = getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE", "./keystore.p12");
+ public String keystorePassword = getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE_PASSWORD", "123456");
}
public static class Policies {
public Policies.CORS cors = new Policies.CORS();
public static class CORS {
- public boolean enabled = true;
- public String[] allowedOrigins = new String[]{"*"};
+ public boolean enabled = getBoolFromEnv("SERVER_HTTP_POLICIES_CORS_ENABLED", true);
+ public String[] allowedOrigins = getStringArrayFromEnv("SERVER_HTTP_POLICIES_ALLOWED_ORIGINS", new String[]{"*"}, ",");
}
}
public static class GameOptions {
public InventoryLimits inventoryLimits = new InventoryLimits();
public AvatarLimits avatarLimits = new AvatarLimits();
- public int sceneEntityLimit = 1000; // Unenforced. TODO: Implement.
+ public int sceneEntityLimit = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_SCENE_ENTITY_LIMIT", 1000); // Unenforced. TODO: Implement.
- public boolean watchGachaConfig = false;
- public boolean enableShopItems = true;
- public boolean staminaUsage = true;
- public boolean energyUsage = true;
- public boolean fishhookTeleport = true;
- public boolean trialCostumes = false;
+ public boolean watchGachaConfig = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_WATCH_GACHA_CONFIG", false);
+ public boolean enableShopItems = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENABLE_SHOP_ITEMS", true);
+ public boolean staminaUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_STAMINA_USAGE", true);
+ public boolean energyUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENERGY_USAGE", true);
+ public boolean fishhookTeleport = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_FISHHOOK_TELEPORT", true);
+ public boolean trialCostumes = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_TRIAL_COSTUMES", false);
@SerializedName(value = "questing", alternate = "questOptions")
public Questing questing = new Questing();
@@ -285,63 +569,63 @@ public static class GameOptions {
public HandbookOptions handbook = new HandbookOptions();
public static class InventoryLimits {
- public int weapons = 2000;
- public int relics = 2000;
- public int materials = 2000;
- public int furniture = 2000;
- public int all = 30000;
+ public int weapons = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_WEAPONS", 2000);
+ public int relics = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_RELICS", 2000);
+ public int materials = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_MATERIALS", 2000);
+ public int furniture = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_FURNITURE", 2000);
+ public int all = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_ALL", 30000);
}
public static class AvatarLimits {
- public int singlePlayerTeam = 4;
- public int multiplayerTeam = 4;
+ public int singlePlayerTeam = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_SINGLE_PLAYER_TEAM", 4);
+ public int multiplayerTeam = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_MULTIPLAYER_TEAM", 4);
}
public static class Rates {
- public float adventureExp = 1.0f;
- public float mora = 1.0f;
- public float leyLines = 1.0f;
+ public float adventureExp = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_ADVENTURE_EXP", 1.0f);
+ public float mora = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_MORA", 1.0f);
+ public float leyLines = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_LEY_LINES", 1.0f);
}
public static class ResinOptions {
- public boolean resinUsage = false;
- public int cap = 160;
- public int rechargeTime = 480;
+ public boolean resinUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RESIN_USAGE", false);
+ public int cap = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_CAP", 160);
+ public int rechargeTime = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RECHARGE_TIME", 480);
}
public static class Questing {
/* Should questing behavior be used? */
- public boolean enabled = true;
+ public boolean enabled = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_QUESTING_ENABLED", true);
}
public static class HandbookOptions {
- public boolean enable = false;
- public boolean allowCommands = true;
+ public boolean enable = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_ENABLE", false);
+ public boolean allowCommands = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_ALLOW_COMMANDS", true);
public Limits limits = new Limits();
public Server server = new Server();
public static class Limits {
/* Are rate limits checked? */
- public boolean enabled = false;
+ public boolean enabled = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_LIMITS_ENABLED", false);
/* The time for limits to expire. */
- public int interval = 3;
+ public int interval = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_LIMITS_INTERVAL", 3);
/* The maximum amount of normal requests. */
- public int maxRequests = 10;
+ public int maxRequests = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_LIMITS_MAX_REQUESTS", 10);
/* The maximum amount of entities to be spawned in one request. */
- public int maxEntities = 25;
+ public int maxEntities = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_LIMITS_MAX_ENTITIES", 25);
}
public static class Server {
/* Are the server settings sent to the handbook? */
- public boolean enforced = false;
+ public boolean enforced = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_CONFIG_SERVER_ENFORCED", false);
/* The default server address for the handbook's authentication. */
- public String address = "127.0.0.1";
+ public String address = getStringFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_CONFIG_SERVER_ADDRESS", "127.0.0.1");
/* The default server port for the handbook's authentication. */
- public int port = 443;
+ public int port = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_CONFIG_SERVER_PORT", 443);
/* Should the defaults be enforced? */
- public boolean canChange = true;
+ public boolean canChange = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_CONFIG_SERVER_CAN_CHANGE", true);
}
}
}
@@ -359,40 +643,37 @@ public VisionOptions(String name, int visionRange, int gridWidth) {
}
public static class JoinOptions {
- public int[] welcomeEmotes = {2007, 1002, 4010};
- public String welcomeMessage = "Welcome to a Grasscutter server.";
+ public int[] welcomeEmotes = getIntArrayFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_EMOTES", new int[]{2007, 1002, 4010}, ",");
+ public String welcomeMessage = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MESSAGE", "Welcome to a Grasscutter server.");
public JoinOptions.Mail welcomeMail = new JoinOptions.Mail();
public static class Mail {
- public String title = "Welcome to Grasscutter!";
- public String content = """
- Hi there!\r
- First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r
- \r
- Check out our:\r
-
- """;
- public String sender = "Lawnmower";
- public emu.grasscutter.game.mail.Mail.MailItem[] items = {
- new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1),
- new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1)
- };
+ public String title = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_TITLE", "Welcome to Grasscutter!");
+ public String content = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_CONTENT", """
+ Hi there!\r
+ First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r
+ \r
+ Check out our:\r
+
+ """);
+ public String sender = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_SENDER", "Lawnmower");
+ public emu.grasscutter.game.mail.Mail.MailItem[] items = getMailItemsFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_ITEMS", new emu.grasscutter.game.mail.Mail.MailItem[]{new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1), new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1)}, "|", ",");
}
}
public static class ConsoleAccount {
- public int avatarId = 10000007;
- public int nameCardId = 210001;
- public int adventureRank = 1;
- public int worldLevel = 0;
+ public int avatarId = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_AVATAR_ID", 10000007);
+ public int nameCardId = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_NAME_CARD_ID", 210001);
+ public int adventureRank = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_ADVENTURE_RANK", 1);
+ public int worldLevel = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_WORLD_LEVEL", 0);
- public String nickName = "Server";
- public String signature = "Welcome to Grasscutter!";
+ public String nickName = getStringFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_NICK_NAME", "Server");
+ public String signature = getStringFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_SIGNATURE", "Welcome to Grasscutter!");
}
public static class Files {
- public String indexFile = "./index.html";
- public String errorFile = "./404.html";
+ public String indexFile = getStringFromEnv("SERVER_HTTP_FILES_INDEX_FILE", "./index.html");
+ public String errorFile = getStringFromEnv("SERVER_HTTP_FILES_ERROR_FILE", "./404.html");
}
/* Objects. */
@@ -404,14 +685,11 @@ public static class Region {
public String Ip = "127.0.0.1";
public int Port = 22102;
- public Region(
- String name, String title,
- String address, int port
- ) {
+ public Region(String name, String title, String address, int port) {
this.Name = name;
this.Title = title;
this.Ip = address;
- this.Port = port;
+ this.Port = port;
}
}
}