mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-08-04 12:41:49 +00:00
Compare commits
1 Commits
developmen
...
e1e0bb6928
Author | SHA1 | Date | |
---|---|---|---|
e1e0bb6928 |
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
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_BRANCH=4.0
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN git clone --branch ${DATA_BRANCH} --depth 1 https://gitlab.com/YuukiPS/GC-Resources.git
|
||||||
|
|
||||||
|
FROM bitnami/java:21.0.1-12
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install unzip
|
||||||
|
|
||||||
|
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 --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 ./generate-config.ts /app/
|
||||||
|
|
||||||
|
CMD [ "sh", "/app/entrypoint.sh" ]
|
||||||
|
|
||||||
|
EXPOSE 80 443 8888 22102
|
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@ -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:
|
5
entrypoint.sh
Executable file
5
entrypoint.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#/bin/sh
|
||||||
|
|
||||||
|
$HOME/.bun/bin/bun run /app/generate-config.ts
|
||||||
|
|
||||||
|
java -jar /app/grasscutter.jar
|
228
generate-config.ts
Normal file
228
generate-config.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
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),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user