#!/bin/bash # ac-compose helper to rebuild AzerothCore from source with enabled modules. set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" ENV_FILE="$PROJECT_DIR/.env" BLUE='\033[0;34m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' show_rebuild_step(){ local step="$1" total="$2" message="$3" echo -e "${YELLOW}๐Ÿ”ง Step ${step}/${total}: ${message}...${NC}" } usage(){ cat <&2; usage; exit 1;; esac done if ! command -v docker >/dev/null 2>&1; then echo "โŒ Docker CLI not found in PATH." exit 1 fi STORAGE_PATH="$(read_env STORAGE_PATH "./storage")" if [[ "$STORAGE_PATH" != /* ]]; then STORAGE_PATH="$PROJECT_DIR/${STORAGE_PATH#./}" fi MODULES_DIR="$STORAGE_PATH/modules" SENTINEL_FILE="$MODULES_DIR/.requires_rebuild" STORAGE_PATH_ABS="$STORAGE_PATH" REBUILD_SOURCE_PATH="$SOURCE_OVERRIDE" default_path="$(default_source_path)" if [ -z "$REBUILD_SOURCE_PATH" ]; then REBUILD_SOURCE_PATH="$(read_env MODULES_REBUILD_SOURCE_PATH "$default_path")" fi if [ -z "$REBUILD_SOURCE_PATH" ]; then REBUILD_SOURCE_PATH="$default_path" fi if [[ "$REBUILD_SOURCE_PATH" != /* ]]; then REBUILD_SOURCE_PATH="$PROJECT_DIR/${REBUILD_SOURCE_PATH#./}" fi if [[ "$default_path" != /* ]]; then default_path_abs="$PROJECT_DIR/${default_path#./}" else default_path_abs="$default_path" fi if [[ "$REBUILD_SOURCE_PATH" == "$STORAGE_PATH_ABS"* ]]; then echo "โš ๏ธ Source path $REBUILD_SOURCE_PATH is inside shared storage ($STORAGE_PATH_ABS). Using local workspace $default_path_abs instead." REBUILD_SOURCE_PATH="$default_path_abs" fi REBUILD_SOURCE_PATH="$(realpath "$REBUILD_SOURCE_PATH" 2>/dev/null || echo "$REBUILD_SOURCE_PATH")" # Check for modules in source directory first, then fall back to shared storage LOCAL_MODULES_DIR="$REBUILD_SOURCE_PATH/modules" SHARED_MODULES_DIR="$STORAGE_PATH/modules" if [ -d "$LOCAL_MODULES_DIR" ]; then echo "๐Ÿ”ง Using modules from source directory: $LOCAL_MODULES_DIR" MODULES_DIR="$LOCAL_MODULES_DIR" SENTINEL_FILE="$LOCAL_MODULES_DIR/.requires_rebuild" else echo "๐Ÿ”ง Using modules from shared storage: $SHARED_MODULES_DIR" MODULES_DIR="$SHARED_MODULES_DIR" SENTINEL_FILE="$SHARED_MODULES_DIR/.requires_rebuild" fi SOURCE_COMPOSE="$REBUILD_SOURCE_PATH/docker-compose.yml" if [ ! -f "$SOURCE_COMPOSE" ]; then echo "โŒ Source docker-compose.yml not found at $SOURCE_COMPOSE" exit 1 fi declare -A MODULE_REPO_MAP=( [MODULE_AOE_LOOT]=mod-aoe-loot [MODULE_LEARN_SPELLS]=mod-learn-spells [MODULE_FIREWORKS]=mod-fireworks-on-level [MODULE_INDIVIDUAL_PROGRESSION]=mod-individual-progression [MODULE_AHBOT]=mod-ahbot [MODULE_AUTOBALANCE]=mod-autobalance [MODULE_TRANSMOG]=mod-transmog [MODULE_NPC_BUFFER]=mod-npc-buffer [MODULE_DYNAMIC_XP]=mod-dynamic-xp [MODULE_SOLO_LFG]=mod-solo-lfg [MODULE_1V1_ARENA]=mod-1v1-arena [MODULE_PHASED_DUELS]=mod-phased-duels [MODULE_BREAKING_NEWS]=mod-breaking-news-override [MODULE_BOSS_ANNOUNCER]=mod-boss-announcer [MODULE_ACCOUNT_ACHIEVEMENTS]=mod-account-achievements [MODULE_AUTO_REVIVE]=mod-auto-revive [MODULE_GAIN_HONOR_GUARD]=mod-gain-honor-guard [MODULE_TIME_IS_TIME]=mod-TimeIsTime [MODULE_POCKET_PORTAL]=mod-pocket-portal [MODULE_RANDOM_ENCHANTS]=mod-random-enchants [MODULE_SOLOCRAFT]=mod-solocraft [MODULE_PVP_TITLES]=mod-pvp-titles [MODULE_NPC_BEASTMASTER]=mod-npc-beastmaster [MODULE_NPC_ENCHANTER]=mod-npc-enchanter [MODULE_INSTANCE_RESET]=mod-instance-reset [MODULE_LEVEL_GRANT]=mod-quest-count-level [MODULE_ARAC]=mod-arac [MODULE_ASSISTANT]=mod-assistant [MODULE_REAGENT_BANK]=mod-reagent-bank [MODULE_CHALLENGE_MODES]=mod-challenge-modes [MODULE_OLLAMA_CHAT]=mod-ollama-chat [MODULE_PLAYER_BOT_LEVEL_BRACKETS]=mod-player-bot-level-brackets [MODULE_STATBOOSTER]=StatBooster [MODULE_DUNGEON_RESPAWN]=DungeonRespawn [MODULE_SKELETON_MODULE]=skeleton-module [MODULE_BG_SLAVERYVALLEY]=mod-bg-slaveryvalley [MODULE_AZEROTHSHARD]=mod-azerothshard [MODULE_WORGOBLIN]=mod-worgoblin ) compile_modules=() for key in "${!MODULE_REPO_MAP[@]}"; do if [ "$(read_env "$key" "0")" = "1" ]; then compile_modules+=("${MODULE_REPO_MAP[$key]}") fi done if [ ${#compile_modules[@]} -eq 0 ]; then echo "โœ… No C++ modules enabled that require a source rebuild." rm -f "$SENTINEL_FILE" 2>/dev/null || true exit 0 fi echo "๐Ÿ”ง Modules requiring compilation:" for mod in "${compile_modules[@]}"; do echo " โ€ข $mod" done if [ ! -d "$MODULES_DIR" ]; then echo "โš ๏ธ Modules directory not found at $MODULES_DIR" fi if ! confirm "Proceed with source rebuild in $REBUILD_SOURCE_PATH? (15-45 minutes)" n; then echo "โŒ Rebuild cancelled" exit 1 fi pushd "$REBUILD_SOURCE_PATH" >/dev/null if [ "$SKIP_STOP" != "1" ]; then echo "๐Ÿ›‘ Stopping existing source services (if any)..." docker compose down || true fi if [ -d "$MODULES_DIR" ]; then echo "๐Ÿ”„ Syncing enabled modules into source tree..." mkdir -p modules find modules -mindepth 1 -maxdepth 1 -type d -name 'mod-*' -exec rm -rf {} + 2>/dev/null || true if command -v rsync >/dev/null 2>&1; then rsync -a "$MODULES_DIR"/ modules/ else cp -R "$MODULES_DIR"/. modules/ fi else echo "โš ๏ธ No modules directory found at $MODULES_DIR; continuing without sync." fi echo "๐Ÿš€ Building AzerothCore with modules..." docker compose build --no-cache echo "๐Ÿ”– Tagging modules-latest images" # Get image names and tags from .env.template TEMPLATE_FILE="$PROJECT_DIR/.env.template" get_template_value() { local key="$1" local fallback="$2" if [ -f "$TEMPLATE_FILE" ]; then local value value=$(grep "^${key}=" "$TEMPLATE_FILE" | head -1 | cut -d'=' -f2- | sed 's/^"\(.*\)"$/\1/') if [[ "$value" =~ ^\$\{[^}]*:-([^}]*)\}$ ]]; then value="${BASH_REMATCH[1]}" fi [ -n "$value" ] && echo "$value" || echo "$fallback" else echo "$fallback" fi } TARGET_AUTHSERVER_IMAGE="$(read_env AC_AUTHSERVER_IMAGE_MODULES "$(get_template_value "AC_AUTHSERVER_IMAGE_MODULES")")" TARGET_WORLDSERVER_IMAGE="$(read_env AC_WORLDSERVER_IMAGE_MODULES "$(get_template_value "AC_WORLDSERVER_IMAGE_MODULES")")" PLAYERBOTS_AUTHSERVER_IMAGE="$(read_env AC_AUTHSERVER_IMAGE_PLAYERBOTS "$(get_template_value "AC_AUTHSERVER_IMAGE_PLAYERBOTS")")" PLAYERBOTS_WORLDSERVER_IMAGE="$(read_env AC_WORLDSERVER_IMAGE_PLAYERBOTS "$(get_template_value "AC_WORLDSERVER_IMAGE_PLAYERBOTS")")" echo "๐Ÿ” Tagging modules images from playerbot build artifacts" if docker image inspect "$PLAYERBOTS_AUTHSERVER_IMAGE" >/dev/null 2>&1; then docker tag "$PLAYERBOTS_AUTHSERVER_IMAGE" "$TARGET_AUTHSERVER_IMAGE" else echo "โš ๏ธ Warning: $PLAYERBOTS_AUTHSERVER_IMAGE not found, skipping authserver tag" fi if docker image inspect "$PLAYERBOTS_WORLDSERVER_IMAGE" >/dev/null 2>&1; then docker tag "$PLAYERBOTS_WORLDSERVER_IMAGE" "$TARGET_WORLDSERVER_IMAGE" else echo "โš ๏ธ Warning: $PLAYERBOTS_WORLDSERVER_IMAGE not found, skipping worldserver tag" fi show_rebuild_step 5 5 "Cleaning up build containers" echo "๐Ÿงน Cleaning up source build containers..." docker compose down --remove-orphans >/dev/null 2>&1 || true popd >/dev/null if [ -n "$SENTINEL_FILE" ]; then if ! rm -f "$SENTINEL_FILE" 2>/dev/null; then if [ -f "$SENTINEL_FILE" ] && command -v docker >/dev/null 2>&1; then DB_IMPORT_IMAGE="$(read_env AC_DB_IMPORT_IMAGE "acore/ac-wotlk-db-import:14.0.0-dev")" if docker image inspect "$DB_IMPORT_IMAGE" >/dev/null 2>&1; then docker run --rm \ --entrypoint /bin/sh \ --user 0:0 \ -v "$MODULES_DIR":/modules \ "$DB_IMPORT_IMAGE" \ -c 'rm -f /modules/.requires_rebuild' >/dev/null 2>&1 || true fi fi fi if [ -f "$SENTINEL_FILE" ]; then echo "โš ๏ธ Unable to remove rebuild sentinel at $SENTINEL_FILE. Remove manually if rebuild detection persists." fi fi echo "" echo -e "${GREEN}โš”๏ธ Module build forged successfully! โš”๏ธ${NC}" echo -e "${GREEN}๐Ÿฐ Your custom AzerothCore images are ready${NC}" echo -e "${GREEN}๐Ÿ—ก๏ธ Time to stage your enhanced realm!${NC}"