Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
115 changes: 115 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
58 changes: 58 additions & 0 deletions db-init/init-db.sh
Original file line number Diff line number Diff line change
@@ -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."
92 changes: 92 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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:
22 changes: 12 additions & 10 deletions sql/create/create_mysql.sql
Original file line number Diff line number Diff line change
@@ -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;