From 44abed52897057ab1b0c0b15249bf736bc41ab5e Mon Sep 17 00:00:00 2001 From: Nae Stormrage Date: Thu, 6 Aug 2026 19:20:19 +0300 Subject: [PATCH 1/2] (feature/docker) Add docker build support and deployment --- .dockerignore | 42 +++++++++++++ .env.example | 6 ++ .gitignore | 7 +++ Dockerfile | 115 ++++++++++++++++++++++++++++++++++++ db-init/init-db.sh | 58 ++++++++++++++++++ docker-compose.yml | 92 +++++++++++++++++++++++++++++ sql/create/create_mysql.sql | 22 +++---- 7 files changed, 332 insertions(+), 10 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 db-init/init-db.sh create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..d19656af --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# ----------------------------------------------------------------------- +# Version Control +# ----------------------------------------------------------------------- +.git +.github +.gitignore +.gitattributes + +# ----------------------------------------------------------------------- +# Environment & Secrets +# ----------------------------------------------------------------------- +.env +.env.example + +# ----------------------------------------------------------------------- +# Client Data & SQL (Mount via volumes instead) +# ----------------------------------------------------------------------- +data/ +sql/ + +# ----------------------------------------------------------------------- +# Documentation +# ----------------------------------------------------------------------- +*.md +AUTHORS +COPYING +INSTALL + +# ----------------------------------------------------------------------- +# Docker Configuration +# ----------------------------------------------------------------------- +docker-compose.yml +Dockerfile +DOCKER.md +.dockerignore + +# ----------------------------------------------------------------------- +# Build Artifacts & Logs +# ----------------------------------------------------------------------- +build/ +bin/ +*.log \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..55b94234 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Copy .env and change variables +MYSQL_ROOT_PASSWORD=changeme_root +MYSQL_PASSWORD=arguscore + +# Path to directory with maps/vmaps/mmaps/dbc/cameras/gt +GAME_DATA_PATH=./data diff --git a/.gitignore b/.gitignore index 05b113fc..22943656 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,10 @@ sql/TDB_hotfixes_735.26972_2025_05_11.sql # Internal maintenance backlog/journal - not part of the shipped codebase ARGUSCORE_FIXES.md CLAUDE.md + +# ----------------------------------------------------------------------- +# Docker +# ----------------------------------------------------------------------- +data/ +.env +configs/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..1e44a298 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,115 @@ +# ========================================================= +# Stage: build — compile worldserver + bnetserver +# ========================================================= +FROM ubuntu:24.04 AS build + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cmake \ + gcc-13 \ + g++-13 \ + ninja-build \ + git \ + ca-certificates \ + libboost-all-dev \ + libssl-dev \ + libmysqlclient-dev \ + libreadline-dev \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 \ + && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100 + +WORKDIR /src +COPY . . + +RUN cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_INSTALL_PREFIX=/opt/arguscore \ + -DSCRIPTS=static \ + -DTOOLS=OFF \ + -DSERVERS=ON \ + && cmake --build build --parallel "$(nproc)" \ + && cmake --install build + +# ========================================================= +# Stage: tdb-download — downloads and extracts TDB (world + hotfixes) +# Completely independent from the build stage, no compilation required. +# ========================================================= +FROM ubuntu:24.04 AS tdb-download + +RUN apt-get update && apt-get install -y --no-install-recommends curl p7zip ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /tdb + +RUN curl -fL -o tdb.7z \ + "https://github.com/The-Legion-Preservation-Project/TrinityCore/releases/download/TDB735.25051/TDB_full_735.26972_2025_05_11.7z" \ + && 7z x tdb.7z -o/tdb/sql + +# ========================================================= +# Stage: runtime-base — shared runtime layer with no default command +# ========================================================= +FROM ubuntu:24.04 AS runtime-base + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libmysqlclient21 \ + mysql-client \ + libssl3 \ + libboost-system1.83.0 \ + libboost-filesystem1.83.0 \ + libboost-program-options1.83.0 \ + libboost-locale1.83.0 \ + libboost-iostreams1.83.0 \ + libreadline8 \ + zlib1g \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && usermod -l argus -d /home/argus -m ubuntu \ + && groupmod -n argus ubuntu + +COPY --from=build /opt/arguscore /opt/arguscore + +RUN mkdir -p /opt/arguscore/data /opt/arguscore/etc \ + && chown -R argus:argus /opt/arguscore + +ENV PATH="/opt/arguscore/bin:${PATH}" +WORKDIR /opt/arguscore +USER argus + +# ========================================================= +# Stage: worldserver — final target for the world service +# ========================================================= +FROM runtime-base AS worldserver + +USER argus + +EXPOSE 8085 +CMD ["worldserver", "-c", "/opt/arguscore/etc/worldserver.conf"] + +# ========================================================= +# Stage: bnetserver — final target for the battle.net service +# ========================================================= +FROM runtime-base AS bnetserver + +EXPOSE 1119 8081 +CMD ["bnetserver", "-c", "/opt/arguscore/etc/bnetserver.conf"] + +# ========================================================= +# Stage: db-init — lightweight target for DB initialization +# Does not use the build stage at all, only copies its script. +# ========================================================= +FROM ubuntu:24.04 AS db-init + +RUN apt-get update && apt-get install -y --no-install-recommends bash mysql-client findutils + +WORKDIR /app +COPY --from=tdb-download /tdb/sql /app/tdb +COPY db-init/init-db.sh /app/init-db.sh +RUN chmod +x /app/init-db.sh + +ENTRYPOINT ["/app/init-db.sh"] \ No newline at end of file diff --git a/db-init/init-db.sh b/db-init/init-db.sh new file mode 100644 index 00000000..353db1bb --- /dev/null +++ b/db-init/init-db.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +MYSQL_HOST="${MYSQL_HOST:-mysql}" +MYSQL_PORT="${MYSQL_PORT:-3306}" +MYSQL_ROOT_USER="${MYSQL_ROOT_USER:-root}" +MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is not set}" + +SQL_DIR="/app" +LOCK_DIR="/etc/db-init" +LOCK_FILE="${LOCK_DIR}/.db_initialized.lock" + +mkdir -p "$LOCK_DIR" + +if [ -f "$LOCK_FILE" ]; then + echo "[db-init] Database is already initialized (lock file exists: ${LOCK_FILE}). Skipping." + exit 0 +fi + +SQL_STEPS=( + "sql/create/create_mysql.sql:" + "sql/base/auth_database.sql:auth" + "sql/base/characters_database.sql:characters" + "tdb/TDB_world_735.26972_2025_05_11.sql:world" + "tdb/TDB_hotfixes_735.26972_2025_05_11.sql:hotfixes" +) + +mysql_root() { + mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASSWORD" "$@" +} + +echo "[db-init] Waiting for MySQL availability at ${MYSQL_HOST}:${MYSQL_PORT}..." +until mysql_root -e "SELECT 1" >/dev/null 2>&1; do + sleep 2 +done +echo "[db-init] MySQL is available." + +for step in "${SQL_STEPS[@]}"; do + rel_path="${step%%:*}" + target_db="${step#*:}" + file="${SQL_DIR}/${rel_path}" + + if [ ! -f "$file" ]; then + echo "[db-init] ERROR: file ${file} not found." >&2 + exit 1 + fi + + if [ -n "$target_db" ]; then + echo "[db-init] Executing ${rel_path} -> database '${target_db}'..." + mysql_root "$target_db" < "$file" + else + echo "[db-init] Executing ${rel_path}..." + mysql_root < "$file" + fi +done + +touch "$LOCK_FILE" +echo "[db-init] Done. All SQL scripts executed and lock file created." \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..43658677 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,92 @@ +services: + mysql: + image: mysql:8.0 + container_name: arguscore-mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} + MYSQL_USER: arguscore + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-arguscore} + MYSQL_DATABASE: arguscore + volumes: + - mysql-data:/var/lib/mysql + ports: + - "3306:3306" + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root}"] + interval: 10s + timeout: 5s + retries: 10 + + db-init: + build: + context: . + dockerfile: Dockerfile + target: db-init + container_name: arguscore-db-init + restart: "no" + depends_on: + mysql: + condition: service_healthy + environment: + MYSQL_HOST: arguscore-mysql + MYSQL_PORT: 3306 + MYSQL_ROOT_USER: root + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} + DB_USER: arguscore + DB_PASSWORD: ${MYSQL_PASSWORD:-arguscore} + volumes: + - db-init-data:/etc/db-init + - ./sql:/app/sql:ro + + bnetserver: + build: + context: . + dockerfile: Dockerfile + target: bnetserver + container_name: arguscore-bnetserver + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + db-init: + condition: service_completed_successfully + volumes: + - ./configs/bnetserver.conf:/opt/arguscore/etc/bnetserver.conf:ro + - bnet-logs:/opt/arguscore/logs + ports: + - "1119:1119" + - "8081:8081" + + worldserver: + build: + context: . + dockerfile: Dockerfile + target: worldserver + container_name: arguscore-worldserver + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + db-init: + condition: service_completed_successfully + volumes: + - ./configs/worldserver.conf:/opt/arguscore/etc/worldserver.conf:ro + - world-logs:/opt/arguscore/logs + - ./sql:/opt/arguscore/sql:ro + - ${GAME_DATA_PATH:-./data}/maps:/opt/arguscore/data/maps:ro + - ${GAME_DATA_PATH:-./data}/vmaps:/opt/arguscore/data/vmaps:ro + - ${GAME_DATA_PATH:-./data}/mmaps:/opt/arguscore/data/mmaps:ro + - ${GAME_DATA_PATH:-./data}/dbc:/opt/arguscore/data/dbc:ro + - ${GAME_DATA_PATH:-./data}/cameras:/opt/arguscore/data/cameras:ro + - ${GAME_DATA_PATH:-./data}/gt:/opt/arguscore/data/gt:ro + ports: + - "8085:8085" + stdin_open: true + tty: true + +volumes: + mysql-data: + db-init_data: + bnet-logs: + world-logs: \ No newline at end of file diff --git a/sql/create/create_mysql.sql b/sql/create/create_mysql.sql index ed977d84..7a033d6f 100644 --- a/sql/create/create_mysql.sql +++ b/sql/create/create_mysql.sql @@ -1,19 +1,21 @@ -CREATE USER 'arguscore'@'localhost' IDENTIFIED BY 'arguscore' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0; +CREATE USER IF NOT EXISTS 'arguscore'@'%' IDENTIFIED BY 'arguscore' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0; -GRANT USAGE ON * . * TO 'arguscore'@'localhost'; +GRANT USAGE ON * . * TO 'arguscore'@'%'; -CREATE DATABASE `world` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `world` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE `characters` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `characters` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE `auth` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `auth` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE `hotfixes` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `hotfixes` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -GRANT ALL PRIVILEGES ON `world` . * TO 'arguscore'@'localhost' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON `world` . * TO 'arguscore'@'%' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON `characters` . * TO 'arguscore'@'localhost' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON `characters` . * TO 'arguscore'@'%' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON `auth` . * TO 'arguscore'@'localhost' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON `auth` . * TO 'arguscore'@'%' WITH GRANT OPTION; -GRANT ALL PRIVILEGES ON `hotfixes` . * TO 'arguscore'@'localhost' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON `hotfixes` . * TO 'arguscore'@'%' WITH GRANT OPTION; + +FLUSH PRIVILEGES; \ No newline at end of file From 84231b1e1db0dbd5cd7ff2c9f9aa1a10157b7cdb Mon Sep 17 00:00:00 2001 From: Nae Stormrage Date: Thu, 6 Aug 2026 19:38:06 +0300 Subject: [PATCH 2/2] (fix/docker) Invalid name volume in docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 43658677..46a3f117 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,6 +87,6 @@ services: volumes: mysql-data: - db-init_data: + db-init-data: bnet-logs: world-logs: \ No newline at end of file