From 2521b7fb18c38fa3c389a33298d462fb9d9ffcd9 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Mon, 16 Mar 2026 22:25:56 +0800 Subject: [PATCH 1/6] Store DB data in a volume --- database/docker-db-data/.gitignore | 3 --- docker-compose.yml | 8 +++++--- 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 database/docker-db-data/.gitignore diff --git a/database/docker-db-data/.gitignore b/database/docker-db-data/.gitignore deleted file mode 100644 index e7a210ec7dc..00000000000 --- a/database/docker-db-data/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -*/ -!.gitignore \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a872b6dc839..826bac5582f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: maplestory: build: . @@ -19,7 +18,7 @@ services: - ./wz:/opt/server/wz environment: DB_HOST: "db" ## Remember if this is present it will OVERRIDE the host in the config.yaml, if you put here anything other than db, you'll need to change the config.yaml jdbc string to port 3307, and not port 3306 - + db: image: mysql:8.4.0 environment: @@ -29,4 +28,7 @@ services: ports: - "3307:3306" volumes: - - ./database/docker-db-data:/var/lib/mysql + - db-data:/var/lib/mysql + +volumes: + db-data: From 3347db70916a0c5b12d496c099408a40faa31d9f Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Tue, 17 Mar 2026 03:16:10 +0800 Subject: [PATCH 2/6] Back up DB data once an hour --- .dockerignore | 4 ++-- database/backups/.gitignore | 2 ++ docker-compose.yml | 17 +++++++++++++++++ docker/db-backup/scripts/auto_backup | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 database/backups/.gitignore create mode 100755 docker/db-backup/scripts/auto_backup diff --git a/.dockerignore b/.dockerignore index 389407f6ea7..d70491c1cce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,5 +9,5 @@ handbook sql tools -# Created by the db when using docker-compose, large and causes rebuild issues if sent to the context. -docker-db-data +# Database backups +database/backups diff --git a/database/backups/.gitignore b/database/backups/.gitignore new file mode 100644 index 00000000000..d6b7ef32c84 --- /dev/null +++ b/database/backups/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker-compose.yml b/docker-compose.yml index 826bac5582f..33eb7320ae0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,5 +30,22 @@ services: volumes: - db-data:/var/lib/mysql + db-backup: + image: percona/percona-xtrabackup:8.4 + init: true + depends_on: + - db + volumes: + - ./docker/db-backup/scripts:/scripts + - db-data:/var/lib/mysql:ro + - ./database/backups:/backup + user: root + environment: + DB_HOST: db + DB_USER: root + DB_PASSWORD: + command: + - /scripts/auto_backup + volumes: db-data: diff --git a/docker/db-backup/scripts/auto_backup b/docker/db-backup/scripts/auto_backup new file mode 100755 index 00000000000..74455344be7 --- /dev/null +++ b/docker/db-backup/scripts/auto_backup @@ -0,0 +1,18 @@ +#!/bin/bash + +while true; do + echo "$(date +'%F %T.%3N') Starting backup process..." + + xtrabackup --backup --compress --compress-threads=4 \ + --host="${DB_HOST}" --user="${DB_USER}" --password="${DB_PASSWORD}" \ + --target-dir="/backup/$(date +%Y%m%d%H%M)" + + if [[ $? == 0 ]]; then + echo "$(date +'%F %T.%3N') Backup completed successfully." + else + echo "$(date +'%F %T.%3N') ERROR: Backup failed!" >&2 + fi + + echo "$(date +'%F %T.%3N') Sleeping for 1 hour..." + sleep 1h +done From a39f82624e248c09a7611171e4ecf220cb55ee5d Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Thu, 19 Mar 2026 15:37:10 +0800 Subject: [PATCH 3/6] README: Reorganize contents 1. Extract the part of Docker as an independent section (Quick start). 2. Getting started => Local setup. And make it focus on server part. 3. Extract the part of "Client" and "Getting into the game" as independent sections. --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 366ab226247..2b841283a73 100644 --- a/README.md +++ b/README.md @@ -48,15 +48,57 @@ The project follows the [semantic versioning](https://semver.org/) scheme using * *General changes or improvements* are treated as MINOR: 1.__2__.3 -> 1.__3.0__ * *Major changes* are treated as MAJOR: __1__.2.3 -> __2.0.0__ -## Getting started -Follow along as I go through the steps to play the game on your local computer from start to finish. I won't go into extreme detail, so if you don't have prior experience with Java or git, you might struggle. +## Quick start + +### Prerequisite + +1. [Docker](https://www.docker.com) +2. [Docker Compose](https://docs.docker.com/compose/install) + +### Start services + +To start all services, run: + +```bash +docker compose up -d +``` + +Then you can use + +```bash +docker compose logs -f maplestory +``` + +to check the logs of Cosmic server. + +Once the server is ready, you will see a message like: + +```log +07:24:20.269 [main] INFO server.Server - Cosmic is now online after 14547 ms. +``` + +### Stop services + +```bash +docker compose down +``` + +### Rebuild + +You must rebuild images after any code changes: + +```bash +docker compose build +``` + +## Local setup +You can also run Cosmic on your actual machine. We will set up the following: - Database - the database is used by the server to store game data such as accounts, characters and inventory items. - Server - the server is the "brain" and routes network traffic between the clients. -- Client - the client is the application used to _play the game_, i.e. MapleStory.exe. -### 1 - Database +### 1 - Database You will start by installing the database server and database client. Then you will connect to the server with the client to create a new database schema. #### Steps @@ -88,11 +130,6 @@ You will start by cloning the repository, then configure the database properties Below, I list other ways of running the server which are completely optional. -#### Docker -Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`. - -Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`. - #### Jar Another option is to start the server from a terminal by running a jar file. You first need to build the jar file from source which requires [Maven](https://maven.apache.org/). Fortunately, [Maven Wrapper](https://maven.apache.org/wrapper/) is provided so you don't have to install Maven separately. @@ -102,15 +139,17 @@ To run the jar, a ``launch.bat`` file is provided for convenience. Simply double Alternatively, run the jar file from the terminal. Just remember to provide the `wz-path` system property pointing to your wz directory. -### 3 - Client -The client files are located in a separate repository: https://github.com/P0nk/Cosmic-client +## Client +Client is the application used to _play the game_, i.e. MapleStory.exe. + +The files are located in a separate repository: https://github.com/P0nk/Cosmic-client Follow the installation guide in the README. -### 4 - Getting into the game -You have successfully started the client, and you're looking at the login screen. +## Getting into the game +You have successfully started the client, and you're looking at the login screen. -#### Logging in +### Logging in At this point, you can log in to the admin account using the following credentials: * Username: "admin" * Password: "admin" @@ -119,7 +158,7 @@ At this point, you can log in to the admin account using the following credentia You can also create a new regular account by typing in your desired username & password and attempting to log in. This "automatic registration" feature lets you create new accounts to play around with. It is enabled by default (see _config.yaml_). -#### Entering the game +### Entering the game Create a new character as you normally would, and then select it to enter the game. Hooray, finally we're in! If you log in to the "Admin" character, you'll notice that the character looks almost invisible. This is hide mode, which is enabled by default when you log in to a GM character. You won't be visible to normal players and no mobs will move if you're alone on the map. Toggle hide mode on or off by typing "@hide" in the in-game chat. From 67d1fa5bca21b3ced78e3b2fa109e5c83e921ae4 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Fri, 20 Mar 2026 02:35:04 +0800 Subject: [PATCH 4/6] Add scripts for restoring database --- README.md | 16 ++++++++++ docker/db-backup/scripts/restore | 52 ++++++++++++++++++++++++++++++++ tools/restore_db | 22 ++++++++++++++ 3 files changed, 90 insertions(+) create mode 100755 docker/db-backup/scripts/restore create mode 100755 tools/restore_db diff --git a/README.md b/README.md index 2b841283a73..ae6b4e7c021 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,14 @@ To start all services, run: docker compose up -d ``` +It starts the following services: + +* **maplestory**: The main part of Cosmic server. +* **db**: The database for Cosmic server. +* **db-backup**: Database backup service. + * Performs an initial database backup on startup, followed by hourly backups. + * You can find the backups in [database/backups](./database/backup). + Then you can use ```bash @@ -83,6 +91,14 @@ Once the server is ready, you will see a message like: docker compose down ``` +### Restore database + +To restore the database using a backup, run: + +```bash +./tools/restore_db +``` + ### Rebuild You must rebuild images after any code changes: diff --git a/docker/db-backup/scripts/restore b/docker/db-backup/scripts/restore new file mode 100755 index 00000000000..50a9ca1aff2 --- /dev/null +++ b/docker/db-backup/scripts/restore @@ -0,0 +1,52 @@ +#!/bin/bash + +readonly TARGET_DIR="/tmp/db_backup" +readonly DATA_DIR="/var/lib/mysql" + +validate_args() { + if [[ -z "$1" ]]; then + echo "Usage: $0 " + exit 1 + fi +} + +assert_dir() { + if [[ ! -d "$1" ]]; then + echo "Error: Directory '$1' does not exist." + exit 1 + fi +} + +main() { + set -e + + validate_args "$@" + + backup_dir="$1" + assert_dir "${backup_dir}" + + echo "Restoring database using ${backup_dir}..." + + echo "Removing existing database data..." + rm -rf ${DATA_DIR}/* + + # The backup might become unusable if the preparation is interrupted. + # So we use a copy instead of original backup to recover the database. + cp -R ${backup_dir} ${TARGET_DIR} + + echo "Decompressing the backup..." + xtrabackup --decompress --target-dir="${TARGET_DIR}" + + echo "Preparing the backup..." + xtrabackup --prepare --target-dir="${TARGET_DIR}" + + echo "Moving files..." + xtrabackup --move-back --target-dir="${TARGET_DIR}" --datadir="${DATA_DIR}" + + echo "Changing the ownership of database files..." + chown -R mysql:mysql ${DATA_DIR} + + echo "Restoration completed successfully." +} + +main "$@" diff --git a/tools/restore_db b/tools/restore_db new file mode 100755 index 00000000000..3d943f41286 --- /dev/null +++ b/tools/restore_db @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +if [[ -z "$1" ]]; then + echo "Usage: $0 " + exit 1 +fi + +read -p "This will stop all services and overwrite the current database. \ +Continue? (y/n) " -n 1 -r +echo +if [[ ! "$REPLY" == "y" ]]; then + echo "Restoration is cancelled." + exit 1 +fi + +echo "Stopping all services..." +docker compose down + +docker compose run --no-deps -it --rm -v db-data:/var/lib/mysql \ + -v $(realpath $1):/to_restore db-backup /scripts/restore /to_restore From 5218dc2a9da6a32b5e61b5e80007d7c79bccc400 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Fri, 20 Mar 2026 22:23:23 +0800 Subject: [PATCH 5/6] Remove DB backups older than 7 days automatically --- README.md | 1 + docker/db-backup/scripts/auto_backup | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae6b4e7c021..c0f98ea7ba4 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ It starts the following services: * **db**: The database for Cosmic server. * **db-backup**: Database backup service. * Performs an initial database backup on startup, followed by hourly backups. + * Backups older than 7 days are removed automatically. * You can find the backups in [database/backups](./database/backup). Then you can use diff --git a/docker/db-backup/scripts/auto_backup b/docker/db-backup/scripts/auto_backup index 74455344be7..2b429ae7eff 100755 --- a/docker/db-backup/scripts/auto_backup +++ b/docker/db-backup/scripts/auto_backup @@ -9,8 +9,11 @@ while true; do if [[ $? == 0 ]]; then echo "$(date +'%F %T.%3N') Backup completed successfully." + + echo "$(date +'%F %T.%3N') Removing backups older than 7 days..." + find "/backup" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} + else - echo "$(date +'%F %T.%3N') ERROR: Backup failed!" >&2 + echo "$(date +'%F %T.%3N') ERROR: Backup failed! Cleanup is skipped." >&2 fi echo "$(date +'%F %T.%3N') Sleeping for 1 hour..." From 266f250b401b44db617669dcc52e01a49e9d4430 Mon Sep 17 00:00:00 2001 From: HarkuLi Date: Fri, 20 Mar 2026 22:26:25 +0800 Subject: [PATCH 6/6] Add healthcheck for database --- docker-compose.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 33eb7320ae0..c8a6f0a85ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,8 @@ services: maplestory: build: . depends_on: - - db + db: + condition: service_healthy ports: # Login server - "8484:8484" @@ -29,12 +30,18 @@ services: - "3307:3306" volumes: - db-data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping"] + timeout: 5s + interval: 5s + retries: 3 db-backup: image: percona/percona-xtrabackup:8.4 init: true depends_on: - - db + db: + condition: service_healthy volumes: - ./docker/db-backup/scripts:/scripts - db-data:/var/lib/mysql:ro