3 Commits

Author SHA1 Message Date
72f0c15108 Use a Docker ARG for the data repository 2023-12-15 23:57:08 +01:00
d5b5e93522 Removed config generation from Docker
Since the ConfigContainer now uses environment variables by default, there is
no need for the config generation script which does the same before launching
the Docker container.
2023-12-15 23:43:52 +01:00
60e713f4ff Refactor ConfigContainer to use environment variables
BREAKING CHANGE:
This will make the config.json obsolete!
2023-12-15 23:41:53 +01:00
5 changed files with 352 additions and 434 deletions

View File

@ -8,11 +8,12 @@ RUN gradle jar --no-daemon
FROM bitnami/git:2.43.0-debian-11-r1 as data 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 ARG DATA_BRANCH=4.0
WORKDIR /app WORKDIR /app
RUN git clone --branch ${DATA_BRANCH} --depth 1 https://gitlab.com/YuukiPS/GC-Resources.git RUN git clone --branch ${DATA_BRANCH} --depth 1 ${DATA_REPOSITORY}
FROM bitnami/java:21.0.1-12 FROM bitnami/java:21.0.1-12
@ -20,9 +21,6 @@ RUN apt-get update && apt-get install unzip
WORKDIR /app WORKDIR /app
# Install bun for generating the configuration file
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.0.0"
# Copy built assets # Copy built assets
COPY --from=builder /app/grasscutter-1.7.4.jar /app/grasscutter.jar COPY --from=builder /app/grasscutter-1.7.4.jar /app/grasscutter.jar
COPY --from=builder /app/keystore.p12 /app/keystore.p12 COPY --from=builder /app/keystore.p12 /app/keystore.p12
@ -31,7 +29,7 @@ COPY --from=builder /app/keystore.p12 /app/keystore.p12
COPY --from=data /app/GC-Resources/Resources /app/resources/ COPY --from=data /app/GC-Resources/Resources /app/resources/
# Copy startup files # Copy startup files
COPY ./entrypoint.sh ./generate-config.ts /app/ COPY ./entrypoint.sh /app/
CMD [ "sh", "/app/entrypoint.sh" ] CMD [ "sh", "/app/entrypoint.sh" ]

View File

@ -1,5 +1,3 @@
#/bin/sh #/bin/sh
$HOME/.bun/bin/bun run /app/generate-config.ts java -jar /app/grasscutter.jar
java -jar /app/grasscutter.jar

View File

@ -1,228 +0,0 @@
import { writeFileSync } from "fs";
const configToSave = {
folderStructure: {
resources: getStringFromEnv("FOLDER_STRUCTURE_RESOURCES", "./resources/"),
data: getStringFromEnv("FOLDER_STRUCTURE_DATA", "./data/"),
packets: getStringFromEnv("FOLDER_STRUCTURE_PACKETS", "./packets/"),
scripts: getStringFromEnv("FOLDER_STRUCTURE_SCRIPTS", "./resources/Scripts/"),
plugins: getStringFromEnv("FOLDER_STRUCTURE_PLUGINS", "./plugins/"),
},
databaseInfo: {
server: {
connectionUri: getStringFromEnv("DATABASE_INFO_SERVER_CONNECTION_URI", "mongodb://localhost:27017"),
collection: getStringFromEnv("DATABASE_INFO_SERVER_COLLECTION", "grasscutter"),
},
game: {
connectionUri: getStringFromEnv("DATABASE_INFO_GAME_CONNECTION_URI", "mongodb://localhost:27017"),
collection: getStringFromEnv("DATABASE_INFO_GAME_COLLECTION", "grasscutter"),
},
},
language: {
language: getStringFromEnv("LANGUAGE_LANGUAGE", "en_US"),
fallback: getStringFromEnv("LANGUAGE_FALLBACK", "en_US"),
document: getStringFromEnv("LANGUAGE_DOCUMENT", "EN"),
},
account: {
autoCreate: getBoolFromEnv("ACCOUNT_AUTO_CREATE", false),
EXPERIMENTAL_RealPassword: getBoolFromEnv("ACCOUNT_EXPERIMENTAL_REAL_PASSWORD", false),
defaultPermissions: getStringArrayFromEnv("ACCOUNT_DEFAULT_PERMISSIONS", []),
maxPlayer: getIntFromEnv("ACCOUNT_MAX_PLAYER", -1),
},
server: {
debugWhitelist: getStringArrayFromEnv("SERVER_DEBUG_WHITELIST", []),
debugBlacklist: getStringArrayFromEnv("SERVER_DEBUG_BLACKLIST", []),
runMode: getStringFromEnv("SERVER_RUN_MODE", "HYBRID"),
logCommands: getBoolFromEnv("SERVER_LOG_COMMANDS", false),
http: {
bindAddress: getStringFromEnv("SERVER_HTTP_BIND_ADDRESS", "0.0.0.0"),
bindPort: getIntFromEnv("SERVER_HTTP_BIND_PORT", 443),
accessAddress: getStringFromEnv("SERVER_HTTP_ACCESS_ADDRESS", "127.0.0.1"),
accessPort: getIntFromEnv("SERVER_HTTP_ACCESS_PORT", 0),
encryption: {
useEncryption: getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_ENCRYPTION", true),
useInRouting: getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_IN_ROUTING", true),
keystore: getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE", "./keystore.p12"),
keystorePassword: getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE_PASSWORD", "123456"),
},
policies: {
cors: {
enabled: getBoolFromEnv("SERVER_HTTP_POLICIES_CORS_ENABLED", false),
allowedOrigins: getStringArrayFromEnv("SERVER_HTTP_POLICIES_CORS_ALLOWED_ORIGINS", ["*"]),
},
},
files: {
indexFile: getStringFromEnv("SERVER_HTTP_FILES_INDEX_FILE", "./index.html"),
errorFile: getStringFromEnv("SERVER_HTTP_FILES_ERROR_FILE", "./404.html"),
},
},
game: {
bindAddress: getStringFromEnv("SERVER_GAME_BIND_ADDRESS", "0.0.0.0"),
bindPort: getIntFromEnv("SERVER_GAME_BIND_PORT", 22102),
accessAddress: getStringFromEnv("SERVER_GAME_ACCESS_ADDRESS", "127.0.0.1"),
accessPort: getIntFromEnv("SERVER_GAME_ACCESS_PORT", 0),
loadEntitiesForPlayerRange: getIntFromEnv("SERVER_GAME_LOAD_ENTITIES_FOR_PLAYER_RANGE", 100),
enableScriptInBigWorld: getBoolFromEnv("SERVER_GAME_ENABLE_SCRIPT_IN_BIG_WORLD", false),
enableConsole: getBoolFromEnv("SERVER_GAME_ENABLE_CONSOLE", true),
kcpInterval: getIntFromEnv("SERVER_GAME_KCP_INTERVAL", 20),
logPackets: getStringFromEnv("SERVER_GAME_LOG_PACKETS", "NONE"),
gameOptions: {
inventoryLimits: {
weapons: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_WEAPONS", 2000),
relics: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_RELICS", 2000),
materials: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_MATERIALS", 2000),
furniture: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_FURNITURE", 2000),
all: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_ALL", 30000),
},
avatarLimits: {
singlePlayerTeam: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_SINGLE_PLAYER_TEAM", 4),
multiplayerTeam: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_MULTIPLAYER_TEAM", 4),
},
sceneEntityLimit: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_SCENE_ENTITY_LIMIT", 1000),
watchGachaConfig: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_WATCH_GACHA_CONFIG", false),
enableShopItems: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENABLE_SHOP_ITEMS", true),
staminaUsage: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_STAMINA_USAGE", true),
energyUsage: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENERGY_USAGE", true),
fishhookTeleport: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_FISHHOOK_TELEPORT", true),
resinOptions: {
resinUsage: getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RESIN_USAGE", false),
cap: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_CAP", 160),
rechargeTime: getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RECHARGE_TIME", 480),
},
rates: {
adventureExp: getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_ADVENTURE_EXP", 1.0),
mora: getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_MORA", 1.0),
leyLines: getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_LEY_LINES", 1.0),
},
},
joinOptions: {
welcomeEmotes: [2007, 1002, 4010],
welcomeMessage: getStringFromEnv(
"SERVER_GAME_JOIN_OPTIONS_WELCOME_MESSAGE",
"Welcome to a Grasscutter server."
),
welcomeMail: {
title: getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_TITLE", "Welcome to Grasscutter!"),
content: getStringFromEnv(
"SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_CONTENT",
'Hi there!\r\nFirst of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r\n\r\nCheck out our:\r\n\u003ctype\u003d"browser" text\u003d"Discord" href\u003d"https://discord.gg/T5vZU6UyeG"/\u003e\n'
),
sender: getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_SENDER", "Lawnmower"),
items: getItemsFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_ITEMS", [
{
itemId: 13509,
itemCount: 1,
itemLevel: 1,
},
{
itemId: 201,
itemCount: 99999,
itemLevel: 1,
},
]),
},
},
serverAccount: {
avatarId: getIntFromEnv("SERVER_GAME_SERVER_ACCOUNT_AVATAR_ID", 10000007),
nameCardId: getIntFromEnv("SERVER_GAME_SERVER_ACCOUNT_NAME_CARD_ID", 210001),
adventureRank: getIntFromEnv("SERVER_GAME_SERVER_ACCOUNT_ADVENTURE_RANK", 1),
worldLevel: getIntFromEnv("SERVER_GAME_SERVER_ACCOUNT_WORLD_LEVEL", 0),
nickName: getStringFromEnv("SERVER_GAME_SERVER_ACCOUNT_NICK_NAME", "Server"),
signature: getStringFromEnv("SERVER_GAME_SERVER_ACCOUNT_SIGNATURE", "Welcome to Grasscutter!"),
},
},
dispatch: {
regions: getStringArrayFromEnv("SERVER_DISPATCH_REGIONS", []),
defaultName: getStringFromEnv("SERVER_DISPATCH_DEFAULT_NAME", "Grasscutter"),
logRequests: getStringFromEnv("SERVER_DISPATCH_LOG_REQUESTS", "NONE"),
},
},
version: 4,
};
writeFileSync("./config.json", JSON.stringify(configToSave, null, 4));
function getStringFromEnv(key: string, defaultValue: string): string {
return process.env[key] || defaultValue;
}
function getBoolFromEnv(key: string, defaultValue: boolean): boolean {
switch (process.env[key]) {
case "true":
case "on":
case "1":
return true;
case "false":
case "off":
case "0":
return false;
default:
return defaultValue;
}
}
function getIntFromEnv(key: string, defaultValue: number): number {
const currentValue = process.env[key];
if (currentValue === undefined || currentValue === null) {
return defaultValue;
}
try {
return parseInt(currentValue, 10);
} catch (error) {
return defaultValue;
}
}
function getFloatFromEnv(key: string, defaultValue: number): number {
const currentValue = process.env[key];
if (currentValue === undefined || currentValue === null) {
return defaultValue;
}
try {
return parseFloat(currentValue);
} catch (error) {
return defaultValue;
}
}
function getStringArrayFromEnv(key: string, defaultValue: string[], separator: string = ","): string[] {
const currentValue = process.env[key];
if (currentValue === undefined || currentValue === null) {
return defaultValue;
}
return currentValue.split(separator);
}
type ItemInfo = {
itemId: number;
itemCount: number;
itemLevel: number;
};
function getItemsFromEnv(key: string, defaultValue: ItemInfo[]): ItemInfo[] {
const currentValue = process.env[key];
if (currentValue === undefined || currentValue === null) {
return defaultValue;
}
const parts = currentValue.split("|");
return parts.map((part: string) => {
const [rawItemId, rawItemCount, rawItemLevel] = part.split(",");
return {
itemId: parseInt(rawItemId, 10),
itemCount: parseInt(rawItemCount, 10),
itemLevel: parseInt(rawItemLevel, 10),
};
});
}

View File

@ -77,8 +77,6 @@ public final class Grasscutter {
// Load server configuration. // Load server configuration.
Grasscutter.loadConfig(); Grasscutter.loadConfig();
// Attempt to update configuration.
ConfigContainer.updateConfig();
Grasscutter.getLogger().info("Loading Grasscutter..."); Grasscutter.getLogger().info("Loading Grasscutter...");
@ -238,22 +236,7 @@ public final class Grasscutter {
/** Attempts to load the configuration from a file. */ /** Attempts to load the configuration from a file. */
public static void loadConfig() { public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config. // Check if config.json exists. If not, we generate a new config.
if (!configFile.exists()) { config = new ConfigContainer();
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);
}
} }
/** /**

View File

@ -1,81 +1,257 @@
package emu.grasscutter.config; package emu.grasscutter.config;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.*; import emu.grasscutter.utils.Utils;
import lombok.NoArgsConstructor; 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* * *when your JVM fails*
*/ */
public class ConfigContainer { public class ConfigContainer {
/* /**
* Configuration changes: * Retrieves the given key from the environment variables.
* Version 5 - 'questing' has been changed from a boolean * <p>
* to a container of options ('questOptions'). * When the key is not set it will return the given default value.
* This field will be removed in future versions. *
* Version 6 - 'questing' has been fully replaced with 'questOptions'. * @param key The name of the environment variable
* The field for 'legacyResources' has been removed. * @param defaultValue The default value when the key is not set
* Version 7 - 'regionKey' is being added for authentication * @return The value from the environment variable or the default value
* 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.
*/ */
private static int version() { static String getStringFromEnv(String key, String defaultValue) {
return 13; 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.
* <p>
* 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() { static int getIntFromEnv(String key, int defaultValue) {
try { // Check if the server is using a legacy config. var currentValue = System.getenv(key);
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; if (currentValue == null) {
var latest = version(); return defaultValue;
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();
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 Integer.parseInt(currentValue, 10);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Retrieves the given key from the environment variables and tries to parse it as float.
* <p>
* 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.
* <p>
* 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<String>.
* <p>
* 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 set
* @param separator The separator which will be used for splitting up the string
* @return The parsed set or the default value
*/
static Set<String> getStringSetFromEnv(String key, Set<String> 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 string array.
* <p>
* 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
* @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<Integer> getIntSetFromEnv(String key, Set<Integer> 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.
* <p>
* 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 <T> The type of the enum member
* @return The parsed enum member or the default value
*/
static <T extends Enum<T>> T getEnumFromEnv(String key, Class<T> enumClass, T defaultValue) {
var currentValue = System.getenv(key);
if (currentValue == null) {
return defaultValue;
}
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.
* <p>
* 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
* @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);
}
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();
}
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();
}
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();
}
static List<Region> getRegionsFromEnv(String key, List<Region> 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 Structure folderStructure = new Structure();
@ -84,9 +260,6 @@ public class ConfigContainer {
public Account account = new Account(); public Account account = new Account();
public Server server = new Server(); public Server server = new Server();
// DO NOT. TOUCH. THE VERSION NUMBER.
public int version = version();
/* Option containers. */ /* Option containers. */
public static class Database { public static class Database {
@ -100,28 +273,29 @@ public class ConfigContainer {
} }
public static class Structure { public static class Structure {
public String resources = "./resources/"; public String resources = getStringFromEnv("FOLDER_STRUCTURE_RESOURCES", "./resources/");
public String data = "./data/"; public String data = getStringFromEnv("FOLDER_STRUCTURE_DATA", "./data/");
public String packets = "./packets/"; public String packets = getStringFromEnv("FOLDER_STRUCTURE_PACKETS", "./packets/");
public String scripts = "resources:Scripts/"; public String scripts = getStringFromEnv("FOLDER_STRUCTURE_SCRIPTS", "resources:Scripts/");
public String plugins = "./plugins/"; public String plugins = getStringFromEnv("FOLDER_STRUCTURE_PLUGINS", "./plugins/");
public String cache = "./cache/"; public String cache = getStringFromEnv("FOLDER_STRUCTURE_CACHE", "./cache/");
// UNUSED (potentially added later?) // UNUSED (potentially added later?)
// public String dumps = "./dumps/"; // public String dumps = "./dumps/";
} }
public static class Server { public static class Server {
public Set<Integer> debugWhitelist = Set.of(); public Set<Integer> debugWhitelist = getIntSetFromEnv("SERVER_DEBUG_WHITELIST", Set.of(), ",");
public Set<Integer> debugBlacklist = Set.of(); public Set<Integer> debugBlacklist = getIntSetFromEnv("SERVER_DEBUG_BLACKLIST", Set.of(), ",");
public ServerRunMode runMode = ServerRunMode.HYBRID; public ServerRunMode runMode = getEnumFromEnv("SERVER_RUN_MODE", ServerRunMode.class, ServerRunMode.HYBRID);
public boolean logCommands = false;
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 varient 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) * 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 HTTP http = new HTTP();
public Game game = new Game(); public Game game = new Game();
@ -133,29 +307,29 @@ public class ConfigContainer {
public static class Language { public static class Language {
public Locale language = Locale.getDefault(); public Locale language = Locale.getDefault();
public Locale fallback = Locale.US; public Locale fallback = Locale.US;
public String document = "EN"; public String document = getStringFromEnv("LANGUAGE_DOCUMENT", "EN");
} }
public static class Account { public static class Account {
public boolean autoCreate = false; public boolean autoCreate = getBoolFromEnv("ACCOUNT_AUTO_CREATE", false);
public boolean EXPERIMENTAL_RealPassword = false; public boolean EXPERIMENTAL_RealPassword = getBoolFromEnv("ACCOUNT_EXPERIMENTAL_REAL_PASSWORD", false);
public String[] defaultPermissions = {}; public String[] defaultPermissions = getStringArrayFromEnv("ACCOUNT_DEFAULT_PERMISSIONS", new String[]{}, ",");
public int maxPlayer = -1; public int maxPlayer = getIntFromEnv("ACCOUNT_MAX_PLAYER", -1);
} }
/* Server options. */ /* Server options. */
public static class HTTP { public static class HTTP {
/* This starts the HTTP server before the game server. */ /* 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 String bindAddress = getStringFromEnv("SERVER_HTTP_BIND_ADDRESS", "0.0.0.0");
public int bindPort = 443; public int bindPort = getIntFromEnv("SERVER_HTTP_BIND_PORT", 443);
/* This is the address used in URLs. */ /* 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. */ /* 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 Encryption encryption = new Encryption();
public Policies policies = new Policies(); public Policies policies = new Policies();
@ -163,66 +337,65 @@ public class ConfigContainer {
} }
public static class Game { public static class Game {
public String bindAddress = "0.0.0.0"; public String bindAddress = getStringFromEnv("SERVER_GAME_BIND_ADDRESS", "0.0.0.0");
public int bindPort = 22102; public int bindPort = getIntFromEnv("SERVER_GAME_BIND_PORT", 22102);
/* This is the address used in the default region. */ /* 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. */ /* 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. */ /* 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 */ /* 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. */ /* Start in 'unstable-quests', Lua scripts will be enabled by default. */
public boolean enableScriptInBigWorld = true; public boolean enableScriptInBigWorld = getBoolFromEnv("SERVER_GAME_ENABLE_SCRIPT_IN_BIG_WORLD", true);
public boolean enableConsole = true; public boolean enableConsole = getBoolFromEnv("SERVER_GAME_ENABLE_CONSOLE", true);
/* Kcp internal work interval (milliseconds) */ /* 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 */ /* 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) */ /* 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 */ /* 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 GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions(); public JoinOptions joinOptions = new JoinOptions();
public ConsoleAccount serverAccount = new ConsoleAccount(); public ConsoleAccount serverAccount = new ConsoleAccount();
public VisionOptions[] visionOptions = new VisionOptions[] { public VisionOptions[] visionOptions = getVisionOptionsFromEnv("SERVER_GAME_VISION_OPTIONS", new VisionOptions[]{
new VisionOptions("VISION_LEVEL_NORMAL" , 80 , 20), new VisionOptions("VISION_LEVEL_NORMAL", 80, 20),
new VisionOptions("VISION_LEVEL_LITTLE_REMOTE" , 16 , 40), new VisionOptions("VISION_LEVEL_LITTLE_REMOTE", 16, 40),
new VisionOptions("VISION_LEVEL_REMOTE" , 1000 , 250), new VisionOptions("VISION_LEVEL_REMOTE", 1000, 250),
new VisionOptions("VISION_LEVEL_SUPER" , 4000 , 1000), new VisionOptions("VISION_LEVEL_SUPER", 4000, 1000),
new VisionOptions("VISION_LEVEL_NEARBY" , 40 , 20), new VisionOptions("VISION_LEVEL_NEARBY", 40, 20),
new VisionOptions("VISION_LEVEL_SUPER_NEARBY" , 20 , 20) new VisionOptions("VISION_LEVEL_SUPER_NEARBY", 20, 20)
}; }, "|", ",");
} }
/* Data containers. */ /* Data containers. */
public static class Dispatch { public static class Dispatch {
/* An array of servers. */ /* An array of servers. */
public List<Region> regions = List.of(); public List<Region> regions = getRegionsFromEnv("SERVER_DISPATCH_REGIONS", List.of(), "|", ",");
/* The URL used to make HTTP requests to the dispatch server. */ /* 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. */ /* 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. */ /* A unique key used for authentication. */
public String dispatchKey = Utils.base64Encode( public String dispatchKey = getStringFromEnv("SERVER_DISPATCH_DISPATCH_KEY", Utils.base64Encode(Crypto.createSessionKey(32)));
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 */ /* 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 /* Debug options container, used when jar launch argument is -debug | -debugall and override default values
@ -236,46 +409,46 @@ public class ConfigContainer {
public Level servicesLoggersLevel = Level.INFO; public Level servicesLoggersLevel = Level.INFO;
/* Controls whether packets should be logged in console or not */ /* 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) */ /* 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 */ /* 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 */ /* 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 static class Encryption {
public boolean useEncryption = true; public boolean useEncryption = getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_ENCRYPTION", true);
/* Should 'https' be appended to URLs? */ /* Should 'https' be appended to URLs? */
public boolean useInRouting = true; public boolean useInRouting = getBoolFromEnv("SERVER_HTTP_ENCRYPTION_USE_IN_ROUTING", true);
public String keystore = "./keystore.p12"; public String keystore = getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE", "./keystore.p12");
public String keystorePassword = "123456"; public String keystorePassword = getStringFromEnv("SERVER_HTTP_ENCRYPTION_KEYSTORE_PASSWORD", "123456");
} }
public static class Policies { public static class Policies {
public Policies.CORS cors = new Policies.CORS(); public Policies.CORS cors = new Policies.CORS();
public static class CORS { public static class CORS {
public boolean enabled = true; public boolean enabled = getBoolFromEnv("SERVER_HTTP_POLICIES_CORS_ENABLED", true);
public String[] allowedOrigins = new String[]{"*"}; public String[] allowedOrigins = getStringArrayFromEnv("SERVER_HTTP_POLICIES_ALLOWED_ORIGINS", new String[]{"*"}, ",");
} }
} }
public static class GameOptions { public static class GameOptions {
public InventoryLimits inventoryLimits = new InventoryLimits(); public InventoryLimits inventoryLimits = new InventoryLimits();
public AvatarLimits avatarLimits = new AvatarLimits(); 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 watchGachaConfig = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_WATCH_GACHA_CONFIG", false);
public boolean enableShopItems = true; public boolean enableShopItems = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENABLE_SHOP_ITEMS", true);
public boolean staminaUsage = true; public boolean staminaUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_STAMINA_USAGE", true);
public boolean energyUsage = true; public boolean energyUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_ENERGY_USAGE", true);
public boolean fishhookTeleport = true; public boolean fishhookTeleport = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_FISHHOOK_TELEPORT", true);
public boolean trialCostumes = false; public boolean trialCostumes = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_TRIAL_COSTUMES", false);
@SerializedName(value = "questing", alternate = "questOptions") @SerializedName(value = "questing", alternate = "questOptions")
public Questing questing = new Questing(); public Questing questing = new Questing();
@ -285,63 +458,63 @@ public class ConfigContainer {
public HandbookOptions handbook = new HandbookOptions(); public HandbookOptions handbook = new HandbookOptions();
public static class InventoryLimits { public static class InventoryLimits {
public int weapons = 2000; public int weapons = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_WEAPONS", 2000);
public int relics = 2000; public int relics = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_RELICS", 2000);
public int materials = 2000; public int materials = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_MATERIALS", 2000);
public int furniture = 2000; public int furniture = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_FURNITURE", 2000);
public int all = 30000; public int all = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_INVENTORY_LIMITS_ALL", 30000);
} }
public static class AvatarLimits { public static class AvatarLimits {
public int singlePlayerTeam = 4; public int singlePlayerTeam = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_SINGLE_PLAYER_TEAM", 4);
public int multiplayerTeam = 4; public int multiplayerTeam = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_AVATAR_LIMITS_MULTIPLAYER_TEAM", 4);
} }
public static class Rates { public static class Rates {
public float adventureExp = 1.0f; public float adventureExp = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_ADVENTURE_EXP", 1.0f);
public float mora = 1.0f; public float mora = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_MORA", 1.0f);
public float leyLines = 1.0f; public float leyLines = getFloatFromEnv("SERVER_GAME_GAME_OPTIONS_RATES_LEY_LINES", 1.0f);
} }
public static class ResinOptions { public static class ResinOptions {
public boolean resinUsage = false; public boolean resinUsage = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RESIN_USAGE", false);
public int cap = 160; public int cap = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_CAP", 160);
public int rechargeTime = 480; public int rechargeTime = getIntFromEnv("SERVER_GAME_GAME_OPTIONS_RESIN_OPTIONS_RECHARGE_TIME", 480);
} }
public static class Questing { public static class Questing {
/* Should questing behavior be used? */ /* 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 static class HandbookOptions {
public boolean enable = false; public boolean enable = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_ENABLE", false);
public boolean allowCommands = true; public boolean allowCommands = getBoolFromEnv("SERVER_GAME_GAME_OPTIONS_HANDBOOK_OPTIONS_ALLOW_COMMANDS", true);
public Limits limits = new Limits(); public Limits limits = new Limits();
public Server server = new Server(); public Server server = new Server();
public static class Limits { public static class Limits {
/* Are rate limits checked? */ /* 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. */ /* 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. */ /* 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. */ /* 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 { public static class Server {
/* Are the server settings sent to the handbook? */ /* 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. */ /* 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. */ /* 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? */ /* 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 +532,37 @@ public class ConfigContainer {
} }
public static class JoinOptions { public static class JoinOptions {
public int[] welcomeEmotes = {2007, 1002, 4010}; public int[] welcomeEmotes = getIntArrayFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_EMOTES", new int[]{2007, 1002, 4010}, ",");
public String welcomeMessage = "Welcome to a Grasscutter server."; public String welcomeMessage = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MESSAGE", "Welcome to a Grasscutter server.");
public JoinOptions.Mail welcomeMail = new JoinOptions.Mail(); public JoinOptions.Mail welcomeMail = new JoinOptions.Mail();
public static class Mail { public static class Mail {
public String title = "Welcome to Grasscutter!"; public String title = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_TITLE", "Welcome to Grasscutter!");
public String content = """ public String content = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_CONTENT", """
Hi there!\r 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 First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r
\r \r
Check out our:\r Check out our:\r
<type="browser" text="Discord" href="https://discord.gg/T5vZU6UyeG"/> <type="browser" text="Discord" href="https://discord.gg/T5vZU6UyeG"/>
"""; """);
public String sender = "Lawnmower"; public String sender = getStringFromEnv("SERVER_GAME_JOIN_OPTIONS_WELCOME_MAIL_SENDER", "Lawnmower");
public emu.grasscutter.game.mail.Mail.MailItem[] items = { 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)}, "|", ",");
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 static class ConsoleAccount {
public int avatarId = 10000007; public int avatarId = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_AVATAR_ID", 10000007);
public int nameCardId = 210001; public int nameCardId = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_NAME_CARD_ID", 210001);
public int adventureRank = 1; public int adventureRank = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_ADVENTURE_RANK", 1);
public int worldLevel = 0; public int worldLevel = getIntFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_WORLD_LEVEL", 0);
public String nickName = "Server"; public String nickName = getStringFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_NICK_NAME", "Server");
public String signature = "Welcome to Grasscutter!"; public String signature = getStringFromEnv("SERVER_GAME_CONSOLE_ACCOUNT_SIGNATURE", "Welcome to Grasscutter!");
} }
public static class Files { public static class Files {
public String indexFile = "./index.html"; public String indexFile = getStringFromEnv("SERVER_HTTP_FILES_INDEX_FILE", "./index.html");
public String errorFile = "./404.html"; public String errorFile = getStringFromEnv("SERVER_HTTP_FILES_ERROR_FILE", "./404.html");
} }
/* Objects. */ /* Objects. */
@ -404,14 +574,11 @@ public class ConfigContainer {
public String Ip = "127.0.0.1"; public String Ip = "127.0.0.1";
public int Port = 22102; public int Port = 22102;
public Region( public Region(String name, String title, String address, int port) {
String name, String title,
String address, int port
) {
this.Name = name; this.Name = name;
this.Title = title; this.Title = title;
this.Ip = address; this.Ip = address;
this.Port = port; this.Port = port;
} }
} }
} }