diff --git a/.env.template b/.env.template index 00b178d..438b088 100644 --- a/.env.template +++ b/.env.template @@ -101,6 +101,7 @@ MYSQL_ROOT_HOST=% MYSQL_USER=root MYSQL_PORT=3306 MYSQL_EXTERNAL_PORT=64306 +MYSQL_EXPOSE_PORT=0 MYSQL_CHARACTER_SET=utf8mb4 MYSQL_COLLATION=utf8mb4_unicode_ci MYSQL_MAX_CONNECTIONS=1000 @@ -108,6 +109,8 @@ MYSQL_INNODB_BUFFER_POOL_SIZE=256M MYSQL_INNODB_LOG_FILE_SIZE=64M MYSQL_INNODB_REDO_LOG_CAPACITY=512M MYSQL_RUNTIME_TMPFS_SIZE=8G +MYSQL_DISABLE_BINLOG=1 +MYSQL_CONFIG_DIR=${STORAGE_PATH}/config/mysql/conf.d DB_WAIT_RETRIES=60 DB_WAIT_SLEEP=10 @@ -137,13 +140,19 @@ BACKUP_HEALTHCHECK_START_PERIOD=120s PLAYERBOT_ENABLED=0 PLAYERBOT_MIN_BOTS=40 PLAYERBOT_MAX_BOTS=40 +STACK_IMAGE_MODE=standard +STACK_SOURCE_VARIANT=core +MODULES_ENABLED_LIST= +MODULES_CPP_LIST= +MODULES_REQUIRES_CUSTOM_BUILD=0 +MODULES_REQUIRES_PLAYERBOT_SOURCE=0 # ===================== # Client Data Settings # ===================== -CLIENT_DATA_VERSION=v17 +CLIENT_DATA_VERSION=v18 CLIENT_DATA_CACHE_PATH=${STORAGE_PATH_LOCAL}/client-data-cache -CLIENT_DATA_VOLUME=ac-client-data +CLIENT_DATA_PATH=${STORAGE_PATH}/client-data # ===================== # Module toggles (0/1) diff --git a/README.md b/README.md index 7becc5f..8a1f2a3 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ The setup wizard will guide you through: **Required when:** - Playerbots enabled (`MODULE_PLAYERBOTS=1`) -- Any C++ module enabled (check `needs_build: true` in `config/modules.json`) +- Any C++ module enabled (modules with `"type": "cpp"` in `config/modules.json`) **Build process:** 1. Clones AzerothCore source to `local-storage/source/` @@ -757,7 +757,7 @@ flowchart TB | Service / Container | Role | Ports (host → container) | Profile | |---------------------|------|--------------------------|---------| -| `ac-mysql` | MySQL 8.0 database | `64306 → 3306` | `db` | +| `ac-mysql` | MySQL 8.0 database | *(optional)* `64306 → 3306` (`MYSQL_EXPOSE_PORT=1`) | `db` | | `ac-db-init` | Database schema initialization | – | `db` | | `ac-db-import` | Database content import | – | `db` | | `ac-backup` | Automated backup system | – | `db` | @@ -773,6 +773,12 @@ flowchart TB | `ac-phpmyadmin` | Database admin UI | `8081 → 80` | `tools` | | `ac-keira3` | Game content editor | `4201 → 8080` | `tools` | +### Database Hardening + +- **MySQL port exposure** – By default `MYSQL_EXPOSE_PORT=0`, so `ac-mysql` is reachable only from the internal Docker network. Set `MYSQL_EXPOSE_PORT=1` to publish `${MYSQL_EXTERNAL_PORT}` on the host; RealmMaster scripts automatically include `docker-compose.mysql-expose.yml` so the override Just Works. If you invoke Compose manually, remember to add `-f docker-compose.mysql-expose.yml`. +- **Binary logging toggle** – `MYSQL_DISABLE_BINLOG=1` appends `--skip-log-bin` via the MySQL wrapper entrypoint to keep disk churn low (and match Playerbot guidance). Flip the flag to `0` to re-enable binlogs for debugging or replication. +- **Drop-in configs** – Any `.cnf` placed in `${STORAGE_PATH}/config/mysql/conf.d` (exposed via `MYSQL_CONFIG_DIR`) is mounted into `/etc/mysql/conf.d`. Use this to add custom tunables or temporarily override the binlog setting without touching the image. + ### Storage Structure The project uses a dual-storage approach for optimal performance: @@ -780,16 +786,23 @@ The project uses a dual-storage approach for optimal performance: **Primary Storage** (`STORAGE_PATH` - default: NFS mount or shared storage) ``` storage/ -├── config/ # Server configuration files (.conf) -├── logs/ # Server log files -├── modules/ # Downloaded module source code -├── lua_scripts/ # Eluna Lua scripts (auto-loaded) -├── install-markers/ # Module installation state tracking -└── backups/ # Automated database backups +├── config/ # Server configuration files (.conf) +│ └── mysql/ +│ └── conf.d/ # Drop-in MySQL overrides (mapped to /etc/mysql/conf.d) +├── client-data/ # Unpacked WoW client data & DBC overrides +├── logs/ # Server log files +├── modules/ # Downloaded module source code +├── lua_scripts/ # Eluna Lua scripts (auto-loaded) +├── install-markers/ # Module installation state tracking +└── backups/ # Automated database backups ├── daily/ # Daily backups (retained per BACKUP_RETENTION_DAYS) └── hourly/ # Hourly backups (retained per BACKUP_RETENTION_HOURS) ``` +`storage/client-data` is bind-mounted into every world/auth/client-data container. Drop patched `dbc`, `maps`, `vmaps`, or `mmaps` files directly into that folder (e.g., `storage/client-data/dbc/SkillLine.dbc`) and the containers will read them immediately—perfect for modules like Individual Progression or mod-worgoblin that need to overwrite Blizzard data. + +To tweak MySQL settings, place `.cnf` snippets in `storage/config/mysql/conf.d`. Files in this directory map straight to `/etc/mysql/conf.d` inside `ac-mysql`, so you can re-enable binary logs or tune buffers without rebuilding images. + **Local Storage** (`STORAGE_PATH_LOCAL` - default: `./local-storage`) ``` local-storage/ @@ -800,8 +813,7 @@ local-storage/ └── images/ # Exported Docker images for remote deployment ``` -**Docker Volumes** -- `ac-client-data` - Unpacked game client data (DBC, maps, vmaps, mmaps) +**Docker Volume** - `client-data-cache` - Temporary storage for client data downloads This separation ensures database and build artifacts stay on fast local storage while configuration, modules, and backups can be shared across hosts via NFS. @@ -1261,4 +1273,3 @@ This project builds upon: - `3784` (authserver) - `8215` (worldserver) - Configure NAT/port forwarding for public access - diff --git a/build.sh b/build.sh index d1dd135..3a4c90f 100755 --- a/build.sh +++ b/build.sh @@ -199,6 +199,37 @@ ensure_source_repo(){ echo "$src_path" } +show_client_data_requirement(){ + local repo_path="$1" + local detector="$ROOT_DIR/scripts/detect-client-data-version.sh" + if [ ! -x "$detector" ]; then + return + fi + + local detection + if ! detection="$("$detector" --no-header "$repo_path" 2>/dev/null | head -n1)"; then + warn "Could not detect client data version for $repo_path" + return + fi + + local detected_repo raw_version normalized_version + IFS=$'\t' read -r detected_repo raw_version normalized_version <<< "$detection" + if [ -z "$normalized_version" ] || [ "$normalized_version" = "" ]; then + warn "Could not detect client data version for $repo_path" + return + fi + + local env_value + env_value="$(read_env CLIENT_DATA_VERSION)" + if [ -n "$env_value" ] && [ "$env_value" != "$normalized_version" ]; then + warn "Source at $repo_path expects client data ${normalized_version} (raw ${raw_version}) but .env specifies ${env_value}. Update CLIENT_DATA_VERSION to avoid mismatched maps." + elif [ -n "$env_value" ]; then + info "Client data requirement satisfied: ${normalized_version} (raw ${raw_version})" + else + info "Detected client data requirement: ${normalized_version} (raw ${raw_version}). Set CLIENT_DATA_VERSION in .env to avoid mismatches." + fi +} + # Build state detection (extracted from setup.sh and deploy.sh) modules_need_rebuild(){ local storage_path @@ -576,6 +607,7 @@ main(){ info "Step 1/6: Setting up source repository" src_dir="$(ensure_source_repo)" + show_client_data_requirement "$src_dir" info "Step 2/6: Detecting build requirements" readarray -t rebuild_reasons < <(detect_rebuild_reasons) diff --git a/cleanup.sh b/cleanup.sh index c616a2a..fda2d05 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -11,7 +11,7 @@ set -e # Resolve project dir and compose SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="${SCRIPT_DIR}" -COMPOSE_FILE="${PROJECT_DIR}/docker-compose.yml" +DEFAULT_COMPOSE_FILE="${PROJECT_DIR}/docker-compose.yml" ENV_FILE="${PROJECT_DIR}/.env" # Colors @@ -87,7 +87,7 @@ confirm() { format_container_table() { local containers - containers="$(docker compose -f "$COMPOSE_FILE" ps -a --format 'table {{.Name}}\t{{.Status}}\t{{.Service}}\t{{.Image}}' 2>/dev/null || echo "")" + containers="$($COMPOSE_BASE ps -a --format 'table {{.Name}}\t{{.Status}}\t{{.Service}}\t{{.Image}}' 2>/dev/null || echo "")" if [ -n "$containers" ]; then echo "$containers" | head -1 echo "$containers" | tail -n +2 | while IFS=$'\t' read -r name status service image; do @@ -124,6 +124,21 @@ STORAGE_PATH_LOCAL_DEFAULT="${PROJECT_DIR}/local-storage" if [ -f "$ENV_FILE" ]; then set -a; source "$ENV_FILE"; set +a fi + +COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") +if [ "${MYSQL_EXPOSE_PORT:-0}" = "1" ]; then + EXTRA_COMPOSE_FILE="${PROJECT_DIR}/docker-compose.mysql-expose.yml" + if [ -f "$EXTRA_COMPOSE_FILE" ]; then + COMPOSE_FILE_ARGS+=(-f "$EXTRA_COMPOSE_FILE") + else + print_status WARNING "MYSQL_EXPOSE_PORT=1 but $EXTRA_COMPOSE_FILE missing; skipping port exposure override." + fi +fi +COMPOSE_FILE_ARGS_STR="" +for arg in "${COMPOSE_FILE_ARGS[@]}"; do + COMPOSE_FILE_ARGS_STR+=" ${arg}" +done +COMPOSE_BASE="docker compose${COMPOSE_FILE_ARGS_STR}" STORAGE_PATH="${STORAGE_PATH:-$STORAGE_PATH_DEFAULT}" STORAGE_PATH_LOCAL="${STORAGE_PATH_LOCAL:-$STORAGE_PATH_LOCAL_DEFAULT}" PROJECT_NAME="${COMPOSE_PROJECT_NAME:-ac-compose}" @@ -170,7 +185,7 @@ soft_cleanup() { --profile tools --profile db ) - execute_command "Stopping runtime profiles" docker compose -f "$COMPOSE_FILE" "${profiles[@]}" down + execute_command "Stopping runtime profiles" $COMPOSE_BASE "${profiles[@]}" down print_status SUCCESS "Soft cleanup complete" } @@ -187,7 +202,7 @@ hard_cleanup() { --profile tools --profile db ) - execute_command "Removing containers and networks" docker compose -f "$COMPOSE_FILE" "${profiles[@]}" down --remove-orphans + execute_command "Removing containers and networks" $COMPOSE_BASE "${profiles[@]}" down --remove-orphans execute_command "Remove project volumes" remove_project_volumes # Remove straggler containers matching project name (defensive) execute_command "Remove stray project containers" "docker ps -a --format '{{.Names}}' | grep -E '^ac-' | xargs -r docker rm -f" @@ -214,7 +229,7 @@ nuclear_cleanup() { --profile tools --profile db ) - execute_command "Removing containers, networks and volumes" docker compose -f "$COMPOSE_FILE" "${profiles[@]}" down --volumes --remove-orphans + execute_command "Removing containers, networks and volumes" $COMPOSE_BASE "${profiles[@]}" down --volumes --remove-orphans execute_command "Remove leftover volumes" remove_project_volumes # Remove project images (server/tool images typical to this project) @@ -266,8 +281,8 @@ main(){ print_status ERROR "Docker not found" exit 1 fi - if [ ! -f "$COMPOSE_FILE" ]; then - print_status ERROR "Compose file not found at $COMPOSE_FILE" + if [ ! -f "$DEFAULT_COMPOSE_FILE" ]; then + print_status ERROR "Compose file not found at $DEFAULT_COMPOSE_FILE" exit 1 fi if [ -z "$CLEANUP_LEVEL" ]; then diff --git a/config/modules.json b/config/modules.json index 2598b1a..abfda1a 100644 --- a/config/modules.json +++ b/config/modules.json @@ -5,7 +5,6 @@ "name": "mod-playerbots", "repo": "https://github.com/mod-playerbots/mod-playerbots.git", "description": "Adds scriptable playerbot characters that can form dungeon parties, raid, and PvP with humans", - "needs_build": false, "type": "data", "notes": "Installs SQL/config assets; core functionality is built into playerbot images", "post_install_hooks": [], @@ -14,14 +13,13 @@ ], "category": "automation", "order": 1, - "special_message": "🤖 Foundation module: Enables AI-powered companions for solo and group content" + "special_message": "\ud83e\udd16 Foundation module: Enables AI-powered companions for solo and group content" }, { "key": "MODULE_AOE_LOOT", "name": "mod-aoe-loot", "repo": "https://github.com/azerothcore/mod-aoe-loot.git", "description": "Lets characters loot multiple corpses with one click for faster farming", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -34,7 +32,6 @@ "name": "mod-learn-spells", "repo": "https://github.com/azerothcore/mod-learn-spells.git", "description": "Teaches class spells automatically at the correct level to streamline leveling", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -47,7 +44,6 @@ "name": "mod-fireworks-on-level", "repo": "https://github.com/azerothcore/mod-fireworks-on-level.git", "description": "Spawns celebratory fireworks whenever a player dings a new level", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -60,7 +56,6 @@ "name": "mod-individual-progression", "repo": "https://github.com/ZhengPeiRu21/mod-individual-progression.git", "description": "Tracks each character through Vanilla \u2192 TBC \u2192 WotLK progression, unlocking content sequentially", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -73,7 +68,6 @@ "name": "mod-ahbot", "repo": "https://github.com/azerothcore/mod-ahbot.git", "description": "Populates the auction house with configurable buying/selling behavior to keep markets active", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -85,7 +79,6 @@ "key": "MODULE_AUTOBALANCE", "name": "mod-autobalance", "repo": "https://github.com/azerothcore/mod-autobalance.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -98,7 +91,6 @@ "key": "MODULE_TRANSMOG", "name": "mod-transmog", "repo": "https://github.com/azerothcore/mod-transmog.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -111,7 +103,6 @@ "key": "MODULE_NPC_BUFFER", "name": "mod-npc-buffer", "repo": "https://github.com/azerothcore/mod-npc-buffer.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -124,7 +115,6 @@ "key": "MODULE_DYNAMIC_XP", "name": "mod-dynamic-xp", "repo": "https://github.com/azerothcore/mod-dynamic-xp.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -137,7 +127,6 @@ "key": "MODULE_SOLO_LFG", "name": "mod-solo-lfg", "repo": "https://github.com/azerothcore/mod-solo-lfg.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -150,7 +139,6 @@ "key": "MODULE_1V1_ARENA", "name": "mod-1v1-arena", "repo": "https://github.com/azerothcore/mod-1v1-arena.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -163,7 +151,6 @@ "key": "MODULE_PHASED_DUELS", "name": "mod-phased-duels", "repo": "https://github.com/azerothcore/mod-phased-duels.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -176,7 +163,6 @@ "key": "MODULE_BREAKING_NEWS", "name": "mod-breaking-news-override", "repo": "https://github.com/azerothcore/mod-breaking-news-override.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -189,7 +175,6 @@ "key": "MODULE_BOSS_ANNOUNCER", "name": "mod-boss-announcer", "repo": "https://github.com/azerothcore/mod-boss-announcer.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -202,7 +187,6 @@ "key": "MODULE_ACCOUNT_ACHIEVEMENTS", "name": "mod-account-achievements", "repo": "https://github.com/azerothcore/mod-account-achievements.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -215,7 +199,6 @@ "key": "MODULE_AUTO_REVIVE", "name": "mod-auto-revive", "repo": "https://github.com/azerothcore/mod-auto-revive.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -228,7 +211,6 @@ "key": "MODULE_GAIN_HONOR_GUARD", "name": "mod-gain-honor-guard", "repo": "https://github.com/azerothcore/mod-gain-honor-guard.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -241,7 +223,6 @@ "key": "MODULE_ELUNA", "name": "mod-ale", "repo": "https://github.com/azerothcore/mod-ale.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [ "mod-ale-patches" @@ -252,13 +233,12 @@ "description": "Adds Eluna Lua scripting engine for creating custom gameplay mechanics", "category": "scripting", "order": 9998, - "special_message": "⚡ Advanced scripting: Required by many Lua-based modules. Enable this first if you plan to use scripted features" + "special_message": "\u26a1 Advanced scripting: Required by many Lua-based modules. Enable this first if you plan to use scripted features" }, { "key": "MODULE_TIME_IS_TIME", "name": "mod-TimeIsTime", "repo": "https://github.com/dunjeon/mod-TimeIsTime.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -271,7 +251,6 @@ "key": "MODULE_POCKET_PORTAL", "name": "mod-pocket-portal", "repo": "https://github.com/azerothcore/mod-pocket-portal.git", - "needs_build": true, "type": "cpp", "status": "blocked", "block_reason": "Requires C++20 std::format support patch before enabling", @@ -286,7 +265,6 @@ "key": "MODULE_RANDOM_ENCHANTS", "name": "mod-random-enchants", "repo": "https://github.com/azerothcore/mod-random-enchants.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -299,7 +277,6 @@ "key": "MODULE_SOLOCRAFT", "name": "mod-solocraft", "repo": "https://github.com/azerothcore/mod-solocraft.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -312,7 +289,6 @@ "key": "MODULE_PVP_TITLES", "name": "mod-pvp-titles", "repo": "https://github.com/azerothcore/mod-pvp-titles.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -328,7 +304,6 @@ "key": "MODULE_NPC_BEASTMASTER", "name": "mod-npc-beastmaster", "repo": "https://github.com/azerothcore/mod-npc-beastmaster.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [ "fix-beastmaster-sql" @@ -343,7 +318,6 @@ "key": "MODULE_NPC_ENCHANTER", "name": "mod-npc-enchanter", "repo": "https://github.com/azerothcore/mod-npc-enchanter.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -356,7 +330,6 @@ "key": "MODULE_INSTANCE_RESET", "name": "mod-instance-reset", "repo": "https://github.com/azerothcore/mod-instance-reset.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -369,7 +342,6 @@ "key": "MODULE_LEVEL_GRANT", "name": "mod-quest-count-level", "repo": "https://github.com/michaeldelago/mod-quest-count-level.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -382,7 +354,6 @@ "key": "MODULE_ARAC", "name": "mod-arac", "repo": "https://github.com/heyitsbench/mod-arac.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -395,7 +366,6 @@ "key": "MODULE_ASSISTANT", "name": "mod-assistant", "repo": "https://github.com/noisiver/mod-assistant.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Spawns an all-purpose assistant NPC with heirlooms, professions, and convenience commands", @@ -405,7 +375,6 @@ "key": "MODULE_REAGENT_BANK", "name": "mod-reagent-bank", "repo": "https://github.com/ZhengPeiRu21/mod-reagent-bank.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Lets players stash crafting reagents with a dedicated banker NPC", @@ -415,7 +384,6 @@ "key": "MODULE_BLACK_MARKET_AUCTION_HOUSE", "name": "mod-black-market", "repo": "https://github.com/Youpeoples/Black-Market-Auction-House.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -430,7 +398,6 @@ "key": "MODULE_CHALLENGE_MODES", "name": "mod-challenge-modes", "repo": "https://github.com/ZhengPeiRu21/mod-challenge-modes.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Implements keystone-style timed runs with leaderboards and scaling modifiers", @@ -440,7 +407,6 @@ "key": "MODULE_OLLAMA_CHAT", "name": "mod-ollama-chat", "repo": "https://github.com/DustinHendrickson/mod-ollama-chat.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Connects playerbots to an Ollama LLM so they can chat with humans organically", @@ -450,7 +416,6 @@ "key": "MODULE_PLAYER_BOT_LEVEL_BRACKETS", "name": "mod-player-bot-level-brackets", "repo": "https://github.com/DustinHendrickson/mod-player-bot-level-brackets.git", - "needs_build": true, "type": "cpp", "requires": [ "MODULE_PLAYERBOTS" @@ -463,7 +428,6 @@ "key": "MODULE_STATBOOSTER", "name": "StatBooster", "repo": "https://github.com/AnchyDev/StatBooster.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [ "fix-statbooster-api" @@ -475,7 +439,6 @@ "key": "MODULE_DUNGEON_RESPAWN", "name": "DungeonRespawn", "repo": "https://github.com/AnchyDev/DungeonRespawn.git", - "needs_build": true, "type": "cpp", "status": "blocked", "block_reason": "Upstream override signature mismatch (OnBeforeTeleport); awaiting fix", @@ -487,7 +450,6 @@ "key": "MODULE_SKELETON_MODULE", "name": "skeleton-module", "repo": "https://github.com/azerothcore/skeleton-module.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Provides a minimal AzerothCore module scaffold for building new features", @@ -497,7 +459,6 @@ "key": "MODULE_BG_SLAVERYVALLEY", "name": "mod-bg-slaveryvalley", "repo": "https://github.com/Helias/mod-bg-slaveryvalley.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Adds the Slavery Valley battleground complete with objectives and queue hooks", @@ -510,7 +471,6 @@ "key": "MODULE_AZEROTHSHARD", "name": "mod-azerothshard", "repo": "https://github.com/azerothcore/mod-azerothshard.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Bundles AzerothShard tweaks: utility NPCs, scripted events, and gameplay improvements", @@ -520,7 +480,6 @@ "key": "MODULE_WORGOBLIN", "name": "mod-worgoblin", "repo": "https://github.com/heyitsbench/mod-worgoblin.git", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "description": "Enables Worgen and Goblin characters with DB/DBC adjustments", @@ -533,7 +492,6 @@ "key": "MODULE_ELUNA_TS", "name": "eluna-ts", "repo": "https://github.com/azerothcore/eluna-ts.git", - "needs_build": false, "type": "tool", "requires": [ "MODULE_ELUNA" @@ -546,20 +504,18 @@ "key": "MODULE_AIO", "name": "mod-aio", "repo": "https://github.com/Rochet2/AIO.git", - "needs_build": true, "type": "cpp", "notes": "Azeroth Interface Override - enables client-server interface communication", "post_install_hooks": [], "description": "Pure Lua server-client communication system for bidirectional data transmission", "category": "scripting", "order": 9999, - "special_message": "🌉 UI Bridge: Enables advanced client-server interfaces. Most users don't need this unless using AIO-based modules" + "special_message": "\ud83c\udf09 UI Bridge: Enables advanced client-server interfaces. Most users don't need this unless using AIO-based modules" }, { "key": "MODULE_ELUNA_SCRIPTS", "name": "eluna-scripts", "repo": "https://github.com/Isidorsson/Eluna-scripts.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -574,7 +530,6 @@ "key": "MODULE_TRANSMOG_AIO", "name": "azerothcore-transmog-3.3.5a", "repo": "https://github.com/DanieltheDeveloper/azerothcore-transmog-3.3.5a.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_AIO" @@ -589,7 +544,6 @@ "key": "MODULE_EVENT_SCRIPTS", "name": "acore-eventscripts", "repo": "https://github.com/55Honey/Acore_eventScripts.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -604,7 +558,6 @@ "key": "MODULE_LEVEL_UP_REWARD", "name": "acore-levelupreward", "repo": "https://github.com/55Honey/Acore_LevelUpReward.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -619,7 +572,6 @@ "key": "MODULE_ACCOUNTWIDE_SYSTEMS", "name": "azerothcore-eluna-accountwide", "repo": "https://github.com/Aldori15/azerothcore-eluna-accountwide.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -634,7 +586,6 @@ "key": "MODULE_EXCHANGE_NPC", "name": "acore-exchangenpc", "repo": "https://github.com/55Honey/Acore_ExchangeNpc.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -649,7 +600,6 @@ "key": "MODULE_RECRUIT_A_FRIEND", "name": "acore-recruitafriend", "repo": "https://github.com/55Honey/Acore_RecruitAFriend.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -664,7 +614,6 @@ "key": "MODULE_PRESTIGE_DRAFT_MODE", "name": "prestige-and-draft-mode", "repo": "https://github.com/Youpeoples/Prestige-and-Draft-Mode.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -679,7 +628,6 @@ "key": "MODULE_LUA_AH_BOT", "name": "azerothcore-lua-ah-bot", "repo": "https://github.com/mostlynick3/azerothcore-lua-ah-bot.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -694,7 +642,6 @@ "key": "MODULE_HARDCORE_MODE", "name": "lua-hardcoremode", "repo": "https://github.com/HellionOP/Lua-HardcoreMode.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -709,7 +656,6 @@ "key": "MODULE_NPCBOT_EXTENDED_COMMANDS", "name": "npcbot-extended-commands", "repo": "https://github.com/Day36512/Npcbot_Extended_Commands.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -724,7 +670,6 @@ "key": "MODULE_MULTIVENDOR", "name": "azerothcore-lua-multivendor", "repo": "https://github.com/Shadowveil-WotLK/AzerothCore-lua-MultiVendor.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -739,7 +684,6 @@ "key": "MODULE_TREASURE_CHEST_SYSTEM", "name": "treasure-chest-system", "repo": "https://github.com/zyggy123/Treasure-Chest-System.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -754,7 +698,6 @@ "key": "MODULE_ACTIVE_CHAT", "name": "activechat", "repo": "https://github.com/Day36512/ActiveChat.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -769,7 +712,6 @@ "key": "MODULE_ULTIMATE_FULL_LOOT_PVP", "name": "ultimate-full-loot-pvp", "repo": "https://github.com/Youpeoples/Ultimate-Full-Loot-Pvp.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -784,7 +726,6 @@ "key": "MODULE_HORADRIC_CUBE", "name": "horadric-cube-for-world-of-warcraft", "repo": "https://github.com/TITIaio/Horadric-Cube-for-World-of-Warcraft.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -799,7 +740,6 @@ "key": "MODULE_CARBON_COPY", "name": "acore-carboncopy", "repo": "https://github.com/55Honey/Acore_CarbonCopy.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -814,7 +754,6 @@ "key": "MODULE_TEMP_ANNOUNCEMENTS", "name": "acore-tempannouncements", "repo": "https://github.com/55Honey/Acore_TempAnnouncements.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -829,7 +768,6 @@ "key": "MODULE_ZONE_CHECK", "name": "acore-zonecheck", "repo": "https://github.com/55Honey/Acore_Zonecheck.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -844,7 +782,6 @@ "key": "MODULE_AIO_BLACKJACK", "name": "aio-blackjack", "repo": "https://github.com/Manmadedrummer/AIO-Blackjack.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_AIO" @@ -859,7 +796,6 @@ "key": "MODULE_SEND_AND_BIND", "name": "acore-sendandbind", "repo": "https://github.com/55Honey/Acore_SendAndBind.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -874,7 +810,6 @@ "key": "MODULE_DYNAMIC_TRADER", "name": "dynamic-trader", "repo": "https://github.com/Day36512/Dynamic-Trader.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -889,7 +824,6 @@ "key": "MODULE_LOTTERY_LUA", "name": "lottery-lua", "repo": "https://github.com/zyggy123/lottery-lua.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -904,7 +838,6 @@ "key": "MODULE_DISCORD_NOTIFIER", "name": "acore-discordnotifier", "repo": "https://github.com/0xCiBeR/Acore_DiscordNotifier.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -919,7 +852,6 @@ "key": "MODULE_GLOBAL_MAIL_BANKING_AUCTIONS", "name": "azerothcore-global-mail-banking-auctions", "repo": "https://github.com/Aldori15/azerothcore-global-mail_banking_auctions.git", - "needs_build": false, "type": "lua", "requires": [ "MODULE_ELUNA" @@ -935,7 +867,6 @@ "name": "mod-guildhouse", "repo": "https://github.com/azerothcore/mod-guildhouse.git", "description": "Phased guild house system allowing guild members to visit their private guild house", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -948,7 +879,6 @@ "name": "mod-progression-system", "repo": "https://github.com/azerothcore/mod-progression-system.git", "description": "Allows for the automatic loading of scripts and SQL files based on level brackets", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -962,7 +892,6 @@ "name": "mod-npc-free-professions", "repo": "https://github.com/azerothcore/mod-npc-free-professions.git", "description": "Makes a ProfessionsNPC who gives 2 free professions (full with recipes) to player", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -973,7 +902,6 @@ "name": "mod-duel-reset", "repo": "https://github.com/azerothcore/mod-duel-reset.git", "description": "Adds some duel reset features", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -986,7 +914,6 @@ "name": "mod-zone-difficulty", "repo": "https://github.com/azerothcore/mod-zone-difficulty.git", "description": "Support module for mod-progression-system, handles nerfs and debuffs per zone", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -998,7 +925,6 @@ "name": "mod-morphsummon", "repo": "https://github.com/azerothcore/mod-morphsummon.git", "description": "Change appearance of summoned permanent creatures", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1012,7 +938,6 @@ "name": "mod-spell-regulator", "repo": "https://github.com/azerothcore/mod-spell-regulator.git", "description": "Modify the percentage of the spells by regulating in the best way", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1024,7 +949,6 @@ "name": "mod-weekend-xp", "repo": "https://github.com/azerothcore/mod-weekend-xp.git", "description": "XP module that allows server owner to select how much XP players can receive on the weekend via config file", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1037,7 +961,6 @@ "name": "mod-reward-played-time", "repo": "https://github.com/azerothcore/mod-reward-played-time.git", "description": "Adds items for players that have stayed logged in for x amount of time", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1050,7 +973,6 @@ "name": "mod-resurrection-scroll", "repo": "https://github.com/azerothcore/mod-resurrection-scroll.git", "description": "Allows users to grant rested XP bonuses to players who have not logged in X days", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1062,7 +984,6 @@ "name": "mod-item-level-up", "repo": "https://github.com/azerothcore/mod-item-level-up.git", "description": "Creates an item that allows you to level up (id = 701001)", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1073,7 +994,6 @@ "name": "mod-npc-talent-template", "repo": "https://github.com/azerothcore/mod-npc-talent-template.git", "description": "An NPC that allows players to instantly apply pre-configured character templates that gear up, gem, set talents, and apply glyphs for any class", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1085,7 +1005,6 @@ "name": "mod-global-chat", "repo": "https://github.com/azerothcore/mod-global-chat.git", "description": "Simple global chat for AzerothCore enabling worldserver-wide messaging functionality", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1098,7 +1017,6 @@ "name": "mod-premium", "repo": "https://github.com/azerothcore/mod-premium.git", "description": "Adds Premium account features to players", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1112,7 +1030,6 @@ "name": "mod-system-vip", "repo": "https://github.com/azerothcore/mod-system-vip.git", "description": "System offering VIP features and benefits to players", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1125,7 +1042,6 @@ "name": "mod-acore-subscriptions", "repo": "https://github.com/azerothcore/mod-acore-subscriptions.git", "description": "Handles the subscription logic, no longer requires modules or services to have subscription logic in their code", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1136,7 +1052,6 @@ "name": "mod-keep-out", "repo": "https://github.com/azerothcore/mod-keep-out.git", "description": "Keeps players who are non-GM from entering a zone/map", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1148,7 +1063,6 @@ "name": "mod-server-auto-shutdown", "repo": "https://github.com/azerothcore/mod-server-auto-shutdown.git", "description": "Establishes a daily restart with configurable time, notification period, and custom messages", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1159,7 +1073,6 @@ "name": "mod-who-logged", "repo": "https://github.com/azerothcore/mod-who-logged.git", "description": "Outputs to the console when a player logs into the world", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1172,7 +1085,6 @@ "name": "mod-account-mounts", "repo": "https://github.com/azerothcore/mod-account-mounts.git", "description": "Goes through the list of characters on an account to obtain playerGuids and store mount spells that all characters know", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1183,7 +1095,6 @@ "name": "mod-antifarming", "repo": "https://github.com/azerothcore/mod-antifarming.git", "description": "Port of the AntiFarming Script from SymbolixDEV's repo to AzerothCore", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1196,7 +1107,6 @@ "name": "mod-arena-replay", "repo": "https://github.com/azerothcore/mod-arena-replay.git", "description": "Allows you to watch a replay of rated arena games", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1208,7 +1118,6 @@ "name": "mod-tic-tac-toe", "repo": "https://github.com/azerothcore/mod-tic-tac-toe.git", "description": "Allows players to play Tic Tac Toe between players and against different AI", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [], @@ -1220,7 +1129,6 @@ "name": "mod-war-effort", "repo": "https://github.com/azerothcore/mod-war-effort.git", "description": "Brings back the war effort of the two factions for the opening of the gates of Ahn'Qiraj", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1233,7 +1141,6 @@ "name": "mod-promotion-azerothcore", "repo": "https://github.com/azerothcore/mod-promotion-azerothcore.git", "description": "Allows player to receive a promotion consisting of a level 90 character, backpacks, gold, armor, and a mount", - "needs_build": true, "type": "cpp", "post_install_hooks": [], "config_cleanup": [ @@ -1242,4 +1149,4 @@ "category": "progression" } ] -} \ No newline at end of file +} diff --git a/deploy.sh b/deploy.sh index 9fb67da..02459a7 100755 --- a/deploy.sh +++ b/deploy.sh @@ -9,7 +9,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" +DEFAULT_COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" ENV_PATH="$ROOT_DIR/.env" TARGET_PROFILE="" WATCH_LOGS=1 @@ -29,6 +29,7 @@ REMOTE_ARGS_PROVIDED=0 MODULE_HELPER="$ROOT_DIR/scripts/modules.py" MODULE_STATE_INITIALIZED=0 declare -a MODULES_COMPILE_LIST=() +declare -a COMPOSE_FILE_ARGS=() BLUE='\033[0;34m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m' info(){ printf '%b\n' "${BLUE}ℹ️ $*${NC}"; } @@ -278,6 +279,22 @@ read_env(){ echo "$value" } +init_compose_files(){ + local expose_port + expose_port="$(read_env MYSQL_EXPOSE_PORT "0")" + COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") + if [ "$expose_port" = "1" ]; then + local extra_file="$ROOT_DIR/docker-compose.mysql-expose.yml" + if [ -f "$extra_file" ]; then + COMPOSE_FILE_ARGS+=(-f "$extra_file") + else + warn "MYSQL_EXPOSE_PORT=1 but $extra_file not found; skipping port override" + fi + fi +} + +init_compose_files + resolve_local_storage_path(){ local path path="$(read_env STORAGE_PATH_LOCAL "./local-storage")" @@ -376,7 +393,7 @@ compose(){ local project_name project_name="$(resolve_project_name)" # Add --quiet for less verbose output, filter excessive empty lines - docker compose --project-name "$project_name" -f "$COMPOSE_FILE" "$@" | filter_empty_lines + docker compose --project-name "$project_name" "${COMPOSE_FILE_ARGS[@]}" "$@" | filter_empty_lines } # Build detection logic diff --git a/docker-compose.mysql-expose.yml b/docker-compose.mysql-expose.yml new file mode 100644 index 0000000..467832b --- /dev/null +++ b/docker-compose.mysql-expose.yml @@ -0,0 +1,5 @@ +services: + ac-mysql: + # Optional override that publishes the MySQL port when MYSQL_EXPOSE_PORT=1 + ports: + - "${MYSQL_EXTERNAL_PORT}:${MYSQL_PORT}" diff --git a/docker-compose.yml b/docker-compose.yml index 22c720c..5cabb68 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,8 +18,6 @@ services: MYSQL_MAX_CONNECTIONS: ${MYSQL_MAX_CONNECTIONS} MYSQL_INNODB_BUFFER_POOL_SIZE: ${MYSQL_INNODB_BUFFER_POOL_SIZE} MYSQL_INNODB_LOG_FILE_SIZE: ${MYSQL_INNODB_LOG_FILE_SIZE} - ports: - - "${MYSQL_EXTERNAL_PORT}:${MYSQL_PORT}" entrypoint: - /usr/local/bin/mysql-entrypoint.sh volumes: @@ -27,6 +25,7 @@ services: - ${STORAGE_PATH_LOCAL}/mysql-data:/var/lib/mysql-persistent - ${BACKUP_PATH}:/backups - ${HOST_ZONEINFO_PATH}:/usr/share/zoneinfo:ro + - ${MYSQL_CONFIG_DIR:-${STORAGE_PATH}/config/mysql/conf.d}:/etc/mysql/conf.d tmpfs: - /var/lib/mysql-runtime:size=${MYSQL_RUNTIME_TMPFS_SIZE} command: @@ -210,12 +209,13 @@ services: container_name: ac-volume-init user: "0:0" volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - client-data-cache:/cache command: - sh - -c - | + mkdir -p /azerothcore/data echo "🔧 Fixing Docker volume permissions..." chown -R ${CONTAINER_USER} /azerothcore/data /cache chmod -R 755 /azerothcore/data /cache @@ -236,6 +236,8 @@ services: - | echo "🔧 Initializing storage directories with proper permissions..." mkdir -p /storage-root/config /storage-root/logs /storage-root/modules /storage-root/lua_scripts /storage-root/install-markers + mkdir -p /storage-root/config/mysql/conf.d + mkdir -p /storage-root/client-data mkdir -p /storage-root/backups /local-storage-root/mysql-data # Fix ownership of root directories and all contents chown -R ${CONTAINER_USER} /storage-root /local-storage-root @@ -255,7 +257,7 @@ services: ac-volume-init: condition: service_completed_successfully volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - client-data-cache:/cache - ./scripts:/tmp/scripts:ro working_dir: /tmp @@ -286,7 +288,7 @@ services: ac-volume-init: condition: service_completed_successfully volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - client-data-cache:/cache - ./scripts:/tmp/scripts:ro working_dir: /tmp @@ -380,7 +382,7 @@ services: - "${WORLD_EXTERNAL_PORT}:${WORLD_PORT}" - "${SOAP_EXTERNAL_PORT}:${SOAP_PORT}" volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - ${STORAGE_PATH}/config:/azerothcore/env/dist/etc - ${STORAGE_PATH}/logs:/azerothcore/logs - ${STORAGE_PATH}/modules:/azerothcore/modules @@ -503,7 +505,7 @@ services: - "${WORLD_EXTERNAL_PORT}:${WORLD_PORT}" - "${SOAP_EXTERNAL_PORT}:${SOAP_PORT}" volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - ${STORAGE_PATH}/config:/azerothcore/env/dist/etc - ${STORAGE_PATH}/logs:/azerothcore/logs - ${STORAGE_PATH}/modules:/azerothcore/modules @@ -550,7 +552,7 @@ services: PLAYERBOT_MAX_BOTS: "${PLAYERBOT_MAX_BOTS}" AC_LOG_LEVEL: "2" volumes: - - ac-client-data:/azerothcore/data + - ${CLIENT_DATA_PATH:-${STORAGE_PATH}/client-data}:/azerothcore/data - ${STORAGE_PATH}/config:/azerothcore/env/dist/etc - ${STORAGE_PATH}/logs:/azerothcore/logs - ${STORAGE_PATH}/modules:/azerothcore/modules @@ -723,9 +725,6 @@ services: - azerothcore volumes: - ac-client-data: - name: ${CLIENT_DATA_VOLUME} - driver: local client-data-cache: driver: local diff --git a/scripts/deploy-tools.sh b/scripts/deploy-tools.sh index a063b8c..dd73031 100755 --- a/scripts/deploy-tools.sh +++ b/scripts/deploy-tools.sh @@ -5,8 +5,9 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.." -COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" +DEFAULT_COMPOSE_FILE="$ROOT_DIR/docker-compose.yml" ENV_FILE="$ROOT_DIR/.env" +declare -a COMPOSE_FILE_ARGS=() BLUE='\033[0;34m' GREEN='\033[0;32m' @@ -44,8 +45,22 @@ resolve_project_name(){ echo "$sanitized" } +init_compose_files(){ + COMPOSE_FILE_ARGS=(-f "$DEFAULT_COMPOSE_FILE") + if [ "$(read_env MYSQL_EXPOSE_PORT "0")" = "1" ]; then + local extra_file="$ROOT_DIR/docker-compose.mysql-expose.yml" + if [ -f "$extra_file" ]; then + COMPOSE_FILE_ARGS+=(-f "$extra_file") + else + warn "MYSQL_EXPOSE_PORT=1 but $extra_file missing; skipping port override." + fi + fi +} + +init_compose_files + compose(){ - docker compose --project-name "$PROJECT_NAME" -f "$COMPOSE_FILE" "$@" + docker compose --project-name "$PROJECT_NAME" "${COMPOSE_FILE_ARGS[@]}" "$@" } show_header(){ diff --git a/scripts/detect-client-data-version.sh b/scripts/detect-client-data-version.sh new file mode 100755 index 0000000..205b977 --- /dev/null +++ b/scripts/detect-client-data-version.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# +# Detect which wowgaming/client-data release an AzerothCore checkout expects. +# Currently inspects apps/installer/includes/functions.sh for the +# inst_download_client_data version marker, but can be extended with new +# heuristics if needed. + +set -euo pipefail + +print_usage() { + cat <<'EOF' +Usage: scripts/detect-client-data-version.sh [--no-header] [...] + +Outputs a tab-separated list of repository path, raw version token found in the +source tree, and a normalized CLIENT_DATA_VERSION (e.g., v18). +EOF +} + +if [[ "${1:-}" == "--help" ]]; then + print_usage + exit 0 +fi + +show_header=1 +if [[ "${1:-}" == "--no-header" ]]; then + show_header=0 + shift +fi + +if [[ $# -lt 1 ]]; then + print_usage >&2 + exit 1 +fi + +normalize_version() { + local token="$1" + token="${token//$'\r'/}" + token="${token//\"/}" + token="${token//\'/}" + token="${token// /}" + token="${token%%#*}" + token="${token%%;*}" + token="${token%%\)*}" + token="${token%%\}*}" + echo "$token" +} + +detect_from_installer() { + local repo_path="$1" + local installer_file="$repo_path/apps/installer/includes/functions.sh" + [[ -f "$installer_file" ]] || return 1 + local raw + raw="$(grep -E 'local[[:space:]]+VERSION=' "$installer_file" | head -n1 | cut -d'=' -f2-)" + [[ -n "$raw" ]] || return 1 + echo "$raw" +} + +detect_version() { + local repo_path="$1" + if [[ ! -d "$repo_path" ]]; then + printf '%s\t%s\t%s\n' "$repo_path" "" "" + return + fi + + local raw="" + if raw="$(detect_from_installer "$repo_path")"; then + : + elif [[ -f "$repo_path/.env" ]]; then + raw="$(grep -E '^CLIENT_DATA_VERSION=' "$repo_path/.env" | head -n1 | cut -d'=' -f2-)" + fi + + if [[ -z "$raw" ]]; then + printf '%s\t%s\t%s\n' "$repo_path" "" "" + return + fi + + local normalized + normalized="$(normalize_version "$raw")" + printf '%s\t%s\t%s\n' "$repo_path" "$raw" "$normalized" +} + +[[ "$show_header" -eq 0 ]] || printf 'repo\traw\tclient_data_version\n' +for repo in "$@"; do + detect_version "$repo" +done diff --git a/scripts/modules.py b/scripts/modules.py index 5a23b0a..9fec09c 100755 --- a/scripts/modules.py +++ b/scripts/modules.py @@ -131,9 +131,10 @@ class ModuleCollectionState: def requires_playerbot_source(self) -> bool: module_map = {m.key: m for m in self.modules} playerbots_enabled = module_map.get("MODULE_PLAYERBOTS") - playerbots = bool(playerbots_enabled and playerbots_enabled.enabled_effective) - needs_cpp = any(module.needs_build and module.enabled_effective for module in self.modules) - return playerbots or needs_cpp + return bool(playerbots_enabled and playerbots_enabled.enabled_effective) + + def requires_custom_build(self) -> bool: + return any(module.needs_build and module.enabled_effective for module in self.modules) def build_state(env_path: Path, manifest_path: Path) -> ModuleCollectionState: @@ -150,8 +151,12 @@ def build_state(env_path: Path, manifest_path: Path) -> ModuleCollectionState: key = entry["key"] name = entry["name"] repo = entry["repo"] - needs_build = bool(entry.get("needs_build", False)) module_type = str(entry.get("type", "cpp")) + needs_build_flag = entry.get("needs_build") + if needs_build_flag is None: + needs_build = module_type.lower() == "cpp" + else: + needs_build = bool(needs_build_flag) requires = entry.get("requires") or [] if not isinstance(requires, list): raise ValueError(f"Manifest entry {key} has non-list 'requires'") @@ -263,20 +268,30 @@ def write_outputs(state: ModuleCollectionState, output_dir: Path) -> None: enabled_names: List[str] = [] compile_names: List[str] = [] + enabled_keys: List[str] = [] + compile_keys: List[str] = [] for module in state.modules: env_lines.append(f"export {module.key}={module.value}") if module.enabled_effective: enabled_names.append(module.name) + enabled_keys.append(module.key) if module.enabled_effective and module.needs_build: compile_names.append(module.name) + compile_keys.append(module.key) env_lines.append(f'export MODULES_ENABLED="{ " ".join(enabled_names) }"'.rstrip()) env_lines.append(f'export MODULES_COMPILE="{ " ".join(compile_names) }"'.rstrip()) + env_lines.append(f'export MODULES_ENABLED_LIST="{",".join(enabled_keys)}"') + env_lines.append(f'export MODULES_CPP_LIST="{",".join(compile_keys)}"') env_lines.append( f"export MODULES_REQUIRES_PLAYERBOT_SOURCE=" f'{"1" if state.requires_playerbot_source() else "0"}' ) + env_lines.append( + f"export MODULES_REQUIRES_CUSTOM_BUILD=" + f'{"1" if state.requires_custom_build() else "0"}' + ) env_lines.append(f"export MODULES_WARNING_COUNT={len(state.warnings)}") env_lines.append(f"export MODULES_ERROR_COUNT={len(state.errors)}") @@ -301,6 +316,7 @@ def write_outputs(state: ModuleCollectionState, output_dir: Path) -> None: "enabled_modules": [module.name for module in state.enabled_modules()], "compile_modules": [module.name for module in state.compile_modules()], "requires_playerbot_source": state.requires_playerbot_source(), + "requires_custom_build": state.requires_custom_build(), } modules_state_path = output_dir / "modules-state.json" @@ -342,6 +358,11 @@ def print_requires_playerbot(state: ModuleCollectionState) -> None: print("1" if state.requires_playerbot_source() else "0") + +def print_requires_custom_build(state: ModuleCollectionState) -> None: + print("1" if state.requires_custom_build() else "0") + + def print_state(state: ModuleCollectionState, fmt: str) -> None: payload = { "generated_at": state.generated_at.isoformat(), @@ -485,6 +506,18 @@ def configure_parser() -> argparse.ArgumentParser: rps_parser.set_defaults(func=handle_requires_playerbot) + rcb_parser = subparsers.add_parser( + "requires-custom-build", + help="Print 1 if a custom source build is required else 0", + ) + + def handle_requires_custom_build(args: argparse.Namespace) -> int: + state = build_state(Path(args.env_path).resolve(), Path(args.manifest).resolve()) + print_requires_custom_build(state) + return 1 if state.errors else 0 + + rcb_parser.set_defaults(func=handle_requires_custom_build) + dump_parser = subparsers.add_parser("dump", help="Dump module state (JSON format)") dump_parser.add_argument( "--format", diff --git a/scripts/mysql-entrypoint.sh b/scripts/mysql-entrypoint.sh index 941d194..5e9bb58 100755 --- a/scripts/mysql-entrypoint.sh +++ b/scripts/mysql-entrypoint.sh @@ -79,4 +79,18 @@ for path in /var/lib/mysql-runtime /var/lib/mysql /var/lib/mysql-persistent /bac fi done +disable_binlog="${MYSQL_DISABLE_BINLOG:-}" +if [ "${disable_binlog}" = "1" ]; then + add_skip_flag=1 + for arg in "$@"; do + if [ "$arg" = "--skip-log-bin" ] || [[ "$arg" == --log-bin* ]]; then + add_skip_flag=0 + break + fi + done + if [ "$add_skip_flag" -eq 1 ]; then + set -- "$@" --skip-log-bin + fi +fi + exec "$ORIGINAL_ENTRYPOINT" "$@" diff --git a/scripts/setup-source.sh b/scripts/setup-source.sh index 1442daa..673724f 100755 --- a/scripts/setup-source.sh +++ b/scripts/setup-source.sh @@ -14,36 +14,55 @@ PROJECT_ROOT="$(pwd)" # Default values MODULE_PLAYERBOTS="${MODULE_PLAYERBOTS:-0}" -NEEDS_CXX_REBUILD="${NEEDS_CXX_REBUILD:-0}" - -COMPILE_MODULE_KEYS=( - MODULE_AOE_LOOT MODULE_LEARN_SPELLS MODULE_FIREWORKS MODULE_INDIVIDUAL_PROGRESSION MODULE_AHBOT MODULE_AUTOBALANCE - MODULE_TRANSMOG MODULE_NPC_BUFFER MODULE_DYNAMIC_XP MODULE_SOLO_LFG MODULE_1V1_ARENA MODULE_PHASED_DUELS - MODULE_BREAKING_NEWS MODULE_BOSS_ANNOUNCER MODULE_ACCOUNT_ACHIEVEMENTS MODULE_AUTO_REVIVE MODULE_GAIN_HONOR_GUARD - MODULE_TIME_IS_TIME MODULE_POCKET_PORTAL MODULE_RANDOM_ENCHANTS MODULE_SOLOCRAFT MODULE_PVP_TITLES MODULE_NPC_BEASTMASTER - MODULE_NPC_ENCHANTER MODULE_INSTANCE_RESET MODULE_LEVEL_GRANT MODULE_ARAC MODULE_ASSISTANT MODULE_REAGENT_BANK - MODULE_CHALLENGE_MODES MODULE_OLLAMA_CHAT MODULE_PLAYER_BOT_LEVEL_BRACKETS MODULE_STATBOOSTER MODULE_DUNGEON_RESPAWN - MODULE_SKELETON_MODULE MODULE_BG_SLAVERYVALLEY MODULE_AZEROTHSHARD MODULE_WORGOBLIN -) - -if [ "$NEEDS_CXX_REBUILD" != "1" ]; then - for key in "${COMPILE_MODULE_KEYS[@]}"; do - if [ "${!key:-0}" = "1" ]; then - NEEDS_CXX_REBUILD=1 - break +PLAYERBOT_ENABLED="${PLAYERBOT_ENABLED:-0}" +STACK_SOURCE_VARIANT="${STACK_SOURCE_VARIANT:-}" +if [ -z "$STACK_SOURCE_VARIANT" ]; then + if [ "$MODULE_PLAYERBOTS" = "1" ] || [ "$PLAYERBOT_ENABLED" = "1" ]; then + STACK_SOURCE_VARIANT="playerbots" + else + STACK_SOURCE_VARIANT="core" fi - done fi LOCAL_STORAGE_ROOT="${STORAGE_PATH_LOCAL:-./local-storage}" DEFAULT_STANDARD_PATH="${LOCAL_STORAGE_ROOT%/}/source/azerothcore" DEFAULT_PLAYERBOTS_PATH="${LOCAL_STORAGE_ROOT%/}/source/azerothcore-playerbots" SOURCE_PATH_DEFAULT="$DEFAULT_STANDARD_PATH" -if [ "$MODULE_PLAYERBOTS" = "1" ] || [ "$NEEDS_CXX_REBUILD" = "1" ]; then +if [ "$STACK_SOURCE_VARIANT" = "playerbots" ]; then SOURCE_PATH_DEFAULT="$DEFAULT_PLAYERBOTS_PATH" fi SOURCE_PATH="${MODULES_REBUILD_SOURCE_PATH:-$SOURCE_PATH_DEFAULT}" +show_client_data_requirement(){ + local repo_path="$1" + local detector="$PROJECT_ROOT/scripts/detect-client-data-version.sh" + if [ ! -x "$detector" ]; then + return + fi + + local detection + if ! detection="$("$detector" --no-header "$repo_path" 2>/dev/null | head -n1)"; then + echo "⚠️ Could not detect client data version for $repo_path" + return + fi + + local detected_repo raw_version normalized_version + IFS=$'\t' read -r detected_repo raw_version normalized_version <<< "$detection" + if [ -z "$normalized_version" ] || [ "$normalized_version" = "" ]; then + echo "⚠️ Could not detect client data version for $repo_path" + return + fi + + local env_value="${CLIENT_DATA_VERSION:-}" + if [ -n "$env_value" ] && [ "$env_value" != "$normalized_version" ]; then + echo "⚠️ Source requires client data ${normalized_version} (raw ${raw_version}) but .env specifies ${env_value}. Update CLIENT_DATA_VERSION to avoid mismatched maps." + elif [ -n "$env_value" ]; then + echo "📦 Client data requirement satisfied: ${normalized_version} (raw ${raw_version})" + else + echo "ℹ️ Detected client data requirement: ${normalized_version} (raw ${raw_version}). Set CLIENT_DATA_VERSION in .env to avoid mismatches." + fi +} + STORAGE_PATH_VALUE="${STORAGE_PATH:-./storage}" if [[ "$STORAGE_PATH_VALUE" != /* ]]; then STORAGE_PATH_ABS="$PROJECT_ROOT/${STORAGE_PATH_VALUE#./}" @@ -72,8 +91,8 @@ ACORE_BRANCH_STANDARD="${ACORE_BRANCH_STANDARD:-master}" ACORE_REPO_PLAYERBOTS="${ACORE_REPO_PLAYERBOTS:-https://github.com/mod-playerbots/azerothcore-wotlk.git}" ACORE_BRANCH_PLAYERBOTS="${ACORE_BRANCH_PLAYERBOTS:-Playerbot}" -# Repository and branch selection based on playerbots mode -if [ "$MODULE_PLAYERBOTS" = "1" ] || [ "$NEEDS_CXX_REBUILD" = "1" ]; then +# Repository and branch selection based on source variant +if [ "$STACK_SOURCE_VARIANT" = "playerbots" ]; then REPO_URL="$ACORE_REPO_PLAYERBOTS" BRANCH="$ACORE_BRANCH_PLAYERBOTS" echo "📌 Playerbots mode: Using $REPO_URL, branch $BRANCH" @@ -130,5 +149,6 @@ echo "📊 Current status:" echo " Branch: $CURRENT_BRANCH" echo " Commit: $CURRENT_COMMIT" echo " Last commit: $(git log -1 --pretty=format:'%s (%an, %ar)')" +show_client_data_requirement "$SOURCE_PATH" echo '🎉 Source repository setup complete!' diff --git a/scripts/setup_manifest.py b/scripts/setup_manifest.py index 6dfb14e..6d27b95 100755 --- a/scripts/setup_manifest.py +++ b/scripts/setup_manifest.py @@ -58,8 +58,13 @@ def cmd_metadata(manifest_path: str) -> None: for entry in iter_modules(manifest): key = entry["key"] name = clean(entry.get("name", key)) - needs_build = "1" if entry.get("needs_build") else "0" - module_type = clean(entry.get("type", "")) + module_type_raw = entry.get("type", "") + module_type = clean(module_type_raw) + needs_build_flag = entry.get("needs_build") + if needs_build_flag is None: + needs_build = "1" if str(module_type_raw).lower() == "cpp" else "0" + else: + needs_build = "1" if needs_build_flag else "0" status = clean(entry.get("status", "active")) block_reason = clean(entry.get("block_reason", "")) requires = unique_preserve_order(entry.get("requires") or []) diff --git a/scripts/stage-modules.sh b/scripts/stage-modules.sh index baa2d9f..e2870e1 100755 --- a/scripts/stage-modules.sh +++ b/scripts/stage-modules.sh @@ -64,6 +64,8 @@ sync_local_staging(){ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" ENV_FILE="$PROJECT_DIR/.env" +DEFAULT_COMPOSE_FILE="$PROJECT_DIR/docker-compose.yml" +EXTRA_COMPOSE_FILE="$PROJECT_DIR/docker-compose.mysql-expose.yml" usage(){ cat </dev/null 2>&1; then - printf " %-10s %-6s %b●%b reachable\n" "$svc" "$port" "$GREEN" "$NC" + if [ "$svc" = "MySQL" ]; then + printf " %-10s %-6s %b●%b reachable %b!note%b exposed\n" "$svc" "$port" "$GREEN" "$NC" "$YELLOW" "$NC" + else + printf " %-10s %-6s %b●%b reachable\n" "$svc" "$port" "$GREEN" "$NC" + fi else printf " %-10s %-6s %b○%b unreachable\n" "$svc" "$port" "$RED" "$NC" fi