Compare commits

..

No commits in common. "135d6d6ef88e143bdfeb2b2712f7af216159600a" and "9c71f50aa845fb8eceee36594f4c984216e7e6ec" have entirely different histories.

6 changed files with 106 additions and 366 deletions

View File

@ -1,19 +1,18 @@
################################################################################
# Docker images
################################################################################
GAME_IMAGE=git.luisbizarro.com/metin2/server:latest
WEB_IMAGE=git.luisbizarro.com/metin2/web:latest
DB_IMAGE=git.luisbizarro.com/metin2/database:latest
GAME_IMAGE=git.old-metin2.com/metin2/server:latest
WEB_IMAGE=git.old-metin2.com/metin2/web:latest
################################################################################
# Database settings
# MariaDB settings
################################################################################
DATABASE_HOST=database
DATABASE_USER=metin2
DATABASE_PASSWORD=metin2
DATABASE_PORT=5432
DATABASE_EXTERNAL_PORT=5432
DATABASE_DB=metin2
MARIADB_HOST=mariadb
MARIADB_USER=root
MARIADB_PASSWORD=metin2
MARIADB_PORT=3306
MARIADB_EXTERNAL_PORT=3306
MARIADB_DB=metin2
################################################################################
# Web app settings
@ -41,7 +40,7 @@ WEB_MAIL_FROM_NAME=Metin2
################################################################################
# Global settings
TEST_SERVER=false
TEST_SERVER=0
# DBCache settings
DB_ADDR=db
@ -49,33 +48,4 @@ DB_PORT=15000
# Game settings
PUBLIC_IP=127.0.0.1
ADMIN_PAGE_IP=127.0.0.1
ADMIN_PAGE_PASSWORD=
IS_TEST_SERVER=false
IS_SPEED_SERVER=false
VIEW_RANGE=9000
USER_LIMIT=850
PASSES_PER_SEC=25
CLIENT_HEART_FPS=10
ITEM_ID_RANGE=10000001 20000000
EMOTE_DUMP_DELAY=900
PLAYER_CACHE_FLUSH_SECONDS=1800
ITEM_CACHE_FLUSH_SECONDS=1800
ITEM_PRICELIST_CACHE_FLUSH_SECONDS=1800
CACHE_FLUSH_LIMIT_PER_SECOND=10
PLAYER_DELETE_LEVEL_LIMIT=90
PLAYER_DELETE_LEVEL_LIMIT_LOWER=15
SAVE_EVENT_SECOND_CYCLE=180
PING_EVENT_SECOND_CYCLE=180
LOG_LEVEL=2 # TRACE 0, DEBUG 1, INFO 2, WARN 3, ERROR 4, CRITICAL 5, OFF 6
# Redis settings
REDIS_HOST=redis
REDIS_PASSWORD=metin2
REDIS_EXTERNAL_PORT=6379
GAME_MAX_LEVEL=105

View File

@ -14,7 +14,9 @@ sure that you have a compatible Linux + Docker environment - for more
information, check out the [compatibility matrix](https://git.old-metin2.com/metin2/deploy#compatibility-matrix).
### Architecture description
The PostgreSQL database is currently pinned on version 18..
The MySQL database is currently pinned on version 5.5. We're using the
[biarms/mysql](https://github.com/biarms/mysql) project in order to provide ARM
compatibility for such an old version of MySQL.
The game cores (`db`, `auth`, `game-*`) are ran by using pre-built images
containing the server binaries and game files, provided in the
@ -91,7 +93,7 @@ need a database and website, but want to run the server in some other way (for
example, in an IDE), you can just bring up only the services you need:
```shell
docker compose up -d database web
docker compose up -d mysql web
```
## Compatibility matrix

View File

@ -0,0 +1 @@
CREATE DATABASE metin2 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

View File

@ -1,23 +0,0 @@
FROM postgres:18
# Install build dependencies and redis-tools
RUN apt-get update && apt-get install -y \
build-essential \
git \
postgresql-server-dev-18 \
libhiredis-dev \
ca-certificates \
redis-tools \
&& rm -rf /var/lib/apt/lists/*
# Clone and build redis_fdw
RUN git clone https://github.com/pg-redis-fdw/redis_fdw.git /tmp/redis_fdw && \
cd /tmp/redis_fdw && \
make USE_PGXS=1 && \
make USE_PGXS=1 install && \
rm -rf /tmp/redis_fdw
# Clean up build dependencies to reduce image size (but keep redis-tools)
RUN apt-get update && \
apt-get purge -y --auto-remove build-essential git postgresql-server-dev-18 && \
rm -rf /var/lib/apt/lists/*

View File

@ -1,212 +0,0 @@
#!/bin/bash
set -e
# Function to log errors
log_error() {
echo "ERROR: $1" >&2
}
log_info() {
echo "INFO: $1"
}
# Trap errors and log them
trap 'log_error "Script failed at line $LINENO"' ERR
log_info "Starting Redis FDW initialization..."
log_info "Redis connection details:"
log_info " Host: ${REDIS_HOST:-redis}"
log_info " Port: 6379"
log_info " Password: $(if [ -n "$REDIS_PASSWORD" ]; then echo "[SET]"; else echo "[NOT SET]"; fi)"
# Wait for Redis to be ready
log_info "Waiting for Redis to be ready..."
REDIS_READY=false
MAX_ATTEMPTS=30
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
if redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" ping 2>/dev/null | grep -q PONG; then
REDIS_READY=true
log_info "Redis is ready after $ATTEMPT attempts"
break
fi
ATTEMPT=$((ATTEMPT + 1))
log_info "Redis is unavailable - attempt $ATTEMPT/$MAX_ATTEMPTS"
sleep 2
done
if [ "$REDIS_READY" = false ]; then
log_error "Redis failed to become ready after $MAX_ATTEMPTS attempts"
log_error "Check if Redis container is running and accessible at ${REDIS_HOST:-redis}:6379"
exit 1
fi
# Get Redis info
log_info "Fetching Redis server information..."
REDIS_VERSION=$(redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" INFO server 2>/dev/null | grep "redis_version" | cut -d: -f2 | tr -d '\r' || echo "unknown")
REDIS_MODE=$(redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" INFO server 2>/dev/null | grep "redis_mode" | cut -d: -f2 | tr -d '\r' || echo "unknown")
REDIS_UPTIME=$(redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" INFO server 2>/dev/null | grep "uptime_in_seconds" | cut -d: -f2 | tr -d '\r' || echo "unknown")
TOTAL_KEYS=$(redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" DBSIZE 2>/dev/null | awk '{print $2}' || echo "0")
log_info "Redis server info:"
log_info " Version: $REDIS_VERSION"
log_info " Mode: $REDIS_MODE"
log_info " Uptime: $REDIS_UPTIME seconds"
log_info " Total keys in DB 0: $TOTAL_KEYS"
# Test Redis connectivity with a test key
log_info "Testing Redis write/read operations..."
if redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" SET fdw_test_key "fdw_init_$(date +%s)" EX 60 >/dev/null 2>&1; then
TEST_VALUE=$(redis-cli -h ${REDIS_HOST:-redis} -a "${REDIS_PASSWORD}" GET fdw_test_key 2>/dev/null)
log_info "Redis write/read test successful (value: $TEST_VALUE)"
else
log_error "Redis write/read test failed"
exit 1
fi
log_info "Setting up redis_fdw extension..."
log_info "PostgreSQL connection:"
log_info " User: $POSTGRES_USER"
log_info " Database: $POSTGRES_DB"
# Run SQL setup as postgres user with error handling
if psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL; then
-- Create redis_fdw extension
DO \$\$
BEGIN
CREATE EXTENSION IF NOT EXISTS redis_fdw;
RAISE NOTICE 'redis_fdw extension created/verified';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create redis_fdw extension: %', SQLERRM;
END;
\$\$;
-- Create foreign server
DO \$\$
BEGIN
DROP SERVER IF EXISTS redis_server CASCADE;
CREATE SERVER redis_server
FOREIGN DATA WRAPPER redis_fdw
OPTIONS (
address '${REDIS_HOST:-redis}',
port '6379'
);
RAISE NOTICE 'Foreign server redis_server created (host: ${REDIS_HOST:-redis}, port: 6379)';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create foreign server: %', SQLERRM;
END;
\$\$;
-- Create user mapping
DO \$\$
BEGIN
CREATE USER MAPPING FOR CURRENT_USER
SERVER redis_server
OPTIONS (password '${REDIS_PASSWORD}');
RAISE NOTICE 'User mapping created for CURRENT_USER';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create user mapping: %', SQLERRM;
END;
\$\$;
-- Create foreign table for Redis hashes
DO \$\$
BEGIN
DROP FOREIGN TABLE IF EXISTS redis_hashes;
CREATE FOREIGN TABLE redis_hashes (
key TEXT,
value TEXT
)
SERVER redis_server
OPTIONS (
database '0',
tabletype 'hash'
);
RAISE NOTICE 'Foreign table redis_hashes created (database: 0, tabletype: hash)';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create redis_hashes: %', SQLERRM;
END;
\$\$;
-- Create foreign table for Redis lists
DO \$\$
BEGIN
DROP FOREIGN TABLE IF EXISTS redis_lists;
CREATE FOREIGN TABLE redis_lists (
key TEXT,
value TEXT
)
SERVER redis_server
OPTIONS (
database '0',
tabletype 'list'
);
RAISE NOTICE 'Foreign table redis_lists created (database: 0, tabletype: list)';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create redis_lists: %', SQLERRM;
END;
\$\$;
-- Create foreign table for Redis sets
DO \$\$
BEGIN
DROP FOREIGN TABLE IF EXISTS redis_sets;
CREATE FOREIGN TABLE redis_sets (
key TEXT,
value TEXT
)
SERVER redis_server
OPTIONS (
database '0',
tabletype 'set'
);
RAISE NOTICE 'Foreign table redis_sets created (database: 0, tabletype: set)';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create redis_sets: %', SQLERRM;
END;
\$\$;
-- Create foreign table for Redis sorted sets
DO \$\$
BEGIN
DROP FOREIGN TABLE IF EXISTS redis_zsets;
CREATE FOREIGN TABLE redis_zsets (
key TEXT,
value TEXT
)
SERVER redis_server
OPTIONS (
database '0',
tabletype 'zset'
);
RAISE NOTICE 'Foreign table redis_zsets created (database: 0, tabletype: zset)';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to create redis_zsets: %', SQLERRM;
END;
\$\$;
-- Grant permissions
DO \$\$
BEGIN
GRANT SELECT ON redis_hashes TO PUBLIC;
GRANT SELECT ON redis_lists TO PUBLIC;
GRANT SELECT ON redis_sets TO PUBLIC;
GRANT SELECT ON redis_zsets TO PUBLIC;
RAISE NOTICE 'Permissions granted on all Redis foreign tables';
EXCEPTION WHEN OTHERS THEN
RAISE EXCEPTION 'Failed to grant permissions: %', SQLERRM;
END;
\$\$;
-- Final verification
SELECT 'Redis FDW setup completed successfully!' as status;
EOSQL
log_info "Redis FDW SQL setup completed successfully"
else
log_error "Redis FDW SQL setup failed with exit code $?"
exit 1
fi
log_info "Redis FDW initialization complete"

View File

@ -1,78 +1,88 @@
x-environment: &common-environment
MARIADB_HOST: ${MARIADB_HOST}
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
MARIADB_PORT: ${MARIADB_PORT}
MARIADB_DB: ${MARIADB_DB}
TEST_SERVER: ${TEST_SERVER}
DB_ADDR: ${DB_ADDR}
DB_PORT: ${DB_PORT}
PUBLIC_IP: ${PUBLIC_IP}
GAME_MAX_LEVEL: ${GAME_MAX_LEVEL}
WEB_APP_URL: ${WEB_APP_URL}
WEB_APP_KEY: ${WEB_APP_KEY}
name: Metin2
services:
# Database
database:
image: ${DB_IMAGE}
# MariaDB Database
mariadb:
image: mariadb:lts
restart: always
environment:
POSTGRES_USER: ${DATABASE_USER:-root}
POSTGRES_DB: ${DATABASE_DB:-metin2}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-rootpassword}
REDIS_HOST: ${REDIS_HOST:-redis}
REDIS_PASSWORD: ${REDIS_PASSWORD}
# Password for root access
MARIADB_ROOT_PASSWORD: ${MARIADB_PASSWORD}
ports:
- "${DATABASE_EXTERNAL_PORT:-5432}:${DATABASE_PORT:-5432}"
- "${MARIADB_EXTERNAL_PORT}:${MARIADB_PORT}"
expose:
- "${DATABASE_PORT:-5432}"
- ${MARIADB_PORT}
volumes:
- ./storage/database:/var/lib/postgresql
- ./assets/postgres/db-init:/docker-entrypoint-initdb.d:ro
- ./storage/database/:/var/lib/mysql/
- ./assets/db-init/:/docker-entrypoint-initdb.d/:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USER:-root}"]
test:
[
"CMD",
"healthcheck.sh",
"--su-mysql",
"--connect",
"--innodb_initialized",
]
interval: 10s
timeout: 5s
retries: 5
networks:
- metin2
depends_on:
redis:
condition: service_healthy
pgbouncer:
image: docker.io/bitnamilegacy/pgbouncer:latest
# Web management system
web:
image: ${WEB_IMAGE}
restart: always
environment:
POSTGRESQL_HOST: database
POSTGRESQL_PORT: ${DATABASE_PORT:-5432}
POSTGRESQL_USERNAME: ${DATABASE_USER:-root}
POSTGRESQL_PASSWORD: ${DATABASE_PASSWORD:-rootpassword}
POSTGRESQL_DATABASE: ${DATABASE_DB:-metin2}
PGBOUNCER_DATABASE: ${DATABASE_DB:-metin2}
PGBOUNCER_PORT: 6432
PGBOUNCER_POOL_MODE: transaction
PGBOUNCER_MAX_CLIENT_CONN: 500
PGBOUNCER_DEFAULT_POOL_SIZE: 20
PGBOUNCER_MIN_POOL_SIZE: 5
PGBOUNCER_RESERVE_POOL_SIZE: 3
PGBOUNCER_MAX_DB_CONNECTIONS: 50
PGBOUNCER_QUERY_TIMEOUT: 30
PGBOUNCER_IDLE_TRANSACTION_TIMEOUT: 60
PGBOUNCER_SERVER_IDLE_TIMEOUT: 600
PGBOUNCER_STATS_USERS: ${DATABASE_USER:-root}
PGBOUNCER_IGNORE_STARTUP_PARAMETERS: extra_float_digits
ports:
- "${PGBOUNCER_EXTERNAL_PORT:-6432}:6432"
expose:
- "6432"
depends_on:
database:
condition: service_healthy
networks:
- metin2
# Application config
APP_NAME: ${WEB_APP_NAME}
APP_ENV: ${WEB_APP_ENV}
APP_KEY: ${WEB_APP_KEY}
APP_URL: ${WEB_APP_URL}
redis:
image: redis:7
restart: always
# Database credentials
DB_HOST: ${MARIADB_HOST}
DB_PORT: ${MARIADB_PORT}
DB_DATABASE: ${MARIADB_DB}
DB_USERNAME: ${MARIADB_USER}
DB_PASSWORD: ${MARIADB_PASSWORD}
# E-mail config
MAIL_MAILER: ${WEB_MAIL_MAILER}
MAIL_HOST: ${WEB_MAIL_HOST}
MAIL_PORT: ${WEB_MAIL_PORT}
MAIL_USERNAME: ${WEB_MAIL_USERNAME}
MAIL_PASSWORD: ${WEB_MAIL_PASSWORD}
MAIL_ENCRYPTION: ${WEB_MAIL_ENCRYPTION}
MAIL_FROM_ADDRESS: ${WEB_MAIL_FROM_ADDRESS}
MAIL_FROM_NAME: ${WEB_MAIL_FROM_NAME}
ports:
- "${REDIS_EXTERNAL_PORT:-6379}:6379"
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
- "${WEB_EXTERNAL_PORT}:80"
volumes:
- ./storage/redis:/data
- ./storage/web/:/app/storage/
depends_on:
mariadb:
condition: service_healthy
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
test: curl --fail http://localhost:80/ || exit 1
interval: 10s
timeout: 5s
retries: 5
@ -83,15 +93,14 @@ services:
db:
image: ${GAME_IMAGE}
restart: always
env_file:
- .env
environment: *common-environment
expose:
- ${DB_PORT}
command: db
volumes:
- ./storage/log/db/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -101,13 +110,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
MARIADB_DB: ${MARIADB_DB}
GAME_HOSTNAME: auth
GAME_CHANNEL: 1
GAME_AUTH_SERVER: master
GAME_PORT: 11000
GAME_P2P_PORT: 12000
env_file:
- .env
expose:
- 11000
- 12000
@ -117,7 +126,7 @@ services:
volumes:
- ./storage/log/auth/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -127,14 +136,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_first
GAME_CHANNEL: 1
GAME_MARK_SERVER: true
GAME_MARK_SERVER: 1
GAME_PORT: 13000
GAME_P2P_PORT: 14000
GAME_MAP_ALLOW: 1 3 4 5 6 21 23 24 25 26 41 43 44 45 46 91 92 93 107 112 201
env_file:
- .env
expose:
- 13000
- 14000
@ -145,7 +153,7 @@ services:
- ./storage/log/ch1/first/:/app/log/
- ./storage/mark/:/app/mark/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -154,14 +162,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_game1
GAME_CHANNEL: 1
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13001
GAME_P2P_PORT: 14001
GAME_MAP_ALLOW: 61 62 67 68 72 73 208 351 352 356
env_file:
- .env
expose:
- 13001
- 14001
@ -171,7 +178,7 @@ services:
volumes:
- ./storage/log/ch1/game1/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -180,14 +187,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_game2
GAME_CHANNEL: 1
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13002
GAME_P2P_PORT: 14002
GAME_MAP_ALLOW: 63 64 65 66 69 70 71 104 108 109 216 217
env_file:
- .env
expose:
- 13002
- 14002
@ -197,7 +203,7 @@ services:
volumes:
- ./storage/log/ch1/game2/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -206,14 +212,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_game3
GAME_CHANNEL: 1
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13003
GAME_P2P_PORT: 14003
GAME_MAP_ALLOW: 301 302 303 304 353 354 358 359 378 379 380 381
env_file:
- .env
expose:
- 13003
- 14003
@ -223,7 +228,7 @@ services:
volumes:
- ./storage/log/ch1/game3/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -232,14 +237,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_game4
GAME_CHANNEL: 1
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13004
GAME_P2P_PORT: 14004
GAME_MAP_ALLOW: 373 374 376 377 382 383 384 385 386 387 388 389 390 391 393 394 395
env_file:
- .env
expose:
- 13004
- 14004
@ -249,7 +253,7 @@ services:
volumes:
- ./storage/log/ch1/game4/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -258,14 +262,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: ch1_game5
GAME_CHANNEL: 1
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13005
GAME_P2P_PORT: 14005
GAME_MAP_ALLOW: 355 357 360 361 362 372 375 396 399 404 405 406 407 408 409 410 411 412 413 414 415 419 420
env_file:
- .env
expose:
- 13005
- 14005
@ -275,7 +278,7 @@ services:
volumes:
- ./storage/log/ch1/game5/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2
@ -285,14 +288,13 @@ services:
image: ${GAME_IMAGE}
restart: always
environment:
<<: *common-environment
GAME_HOSTNAME: game99
GAME_CHANNEL: 99
GAME_MARK_SERVER: false
GAME_MARK_SERVER: 0
GAME_PORT: 13099
GAME_P2P_PORT: 14099
GAME_MAP_ALLOW: 81 103 105 110 111 113 114 118 119 120 121 122 123 124 125 126 127 128 130 131 132 133 181 182 183 200 392 363 364 365 366 367 368 369 370 371 400 401 402 403
env_file:
- .env
expose:
- 13099
- 14099
@ -302,7 +304,7 @@ services:
volumes:
- ./storage/log/game99/:/app/log/
depends_on:
database:
mariadb:
condition: service_healthy
networks:
- metin2