18 Commits

Author SHA1 Message Date
uprightbass360
b046f7f8ba fix: workaround for ale+playerbots 2026-01-02 02:53:47 -05:00
uprightbass360
f5b3b07bcb update module profiles 2026-01-01 15:50:04 -05:00
uprightbass360
07110902a6 cleanup: remove stale changelog and notes 2025-12-27 18:48:40 -05:00
uprightbass360
a67bfcd87b sets module rebuild path automatically 2025-12-27 18:41:33 -05:00
uprightbass360
b0444019ae cleans up variable expansion 2025-12-27 18:36:50 -05:00
Deckard
29d299e402 Change realmmaster status from 'active' to 'blocked' 2025-12-27 18:14:26 -05:00
Deckard
10c45716cf Remove MODULE_AZEROTHCORE_REALMMASTER from template 2025-12-27 18:14:26 -05:00
uprightbass360
3a8f076894 chore: sync module manifest 2025-12-27 18:14:26 -05:00
uprightbass360
3ec83b7714 adds fallback for workflow 2025-12-27 18:07:40 -05:00
uprightbass360
b7d55976cd updates setup language 2025-12-27 17:05:10 -05:00
uprightbass360
63b0a4ba5d adds thanks to readme 2025-12-27 17:00:05 -05:00
uprightbass360
9b9d99904a cleans up env generation and dropps disabled flags 2025-12-27 16:46:27 -05:00
uprightbass360
690ee4317c updates modules and module setup 2025-12-27 16:46:27 -05:00
uprightbass360
b8245e7b3f chore: updates modules and module updater 2025-12-27 15:30:59 -05:00
uprightbass360
6ed10dead7 add helps 2025-12-12 18:56:42 -05:00
uprightbass360
9f3038a516 flips qr generation to params 2025-12-12 18:49:17 -05:00
uprightbass360
ea3c2e750c adds pdump and 2fa generation 2025-12-12 18:33:53 -05:00
uprightbass360
63b2ca8151 backup fixes 2025-12-03 22:13:22 -05:00
27 changed files with 2646 additions and 474 deletions

View File

@@ -76,10 +76,17 @@ DB_GUARD_VERIFY_INTERVAL_SECONDS=86400
# =====================
STAGE_PATH_MODULE_SQL=${STORAGE_MODULE_SQL_PATH}
# =====================
# Modules rebuild source path
# =====================
# Default AzerothCore source checkout used for module rebuilds
MODULES_REBUILD_SOURCE_PATH=${STORAGE_PATH_LOCAL}/source/azerothcore
# =====================
# SQL Source Overlay
# =====================
AC_SQL_SOURCE_PATH=${STORAGE_LOCAL_SOURCE_PATH}/azerothcore-playerbots/data/sql
SOURCE_DIR=${MODULES_REBUILD_SOURCE_PATH}
AC_SQL_SOURCE_PATH=${MODULES_REBUILD_SOURCE_PATH}/data/sql
# =====================
# Images
@@ -219,6 +226,8 @@ MODULES_REQUIRES_PLAYERBOT_SOURCE=0
# Only set this if you need to override the auto-detected version
# Example: v18.0, v17.0, etc.
CLIENT_DATA_VERSION=
# Client data path for deployment (auto-calculated when left blank)
CLIENT_DATA_PATH=
# =====================
# Server Configuration
@@ -228,174 +237,10 @@ CLIENT_DATA_VERSION=
SERVER_CONFIG_PRESET=none
CLIENT_DATA_CACHE_PATH=${STORAGE_PATH_LOCAL}/client-data-cache
# =====================
# Module toggles (0/1)
# =====================
# Enable/disable modules by setting to 1 (enabled) or 0 (disabled)
# Modules are organized by category for easier navigation
# 🤖 Automation
# Playerbot and AI systems
MODULE_NPCBOT_EXTENDED_COMMANDS=0
MODULE_OLLAMA_CHAT=0
# mod-playerbots: Installs SQL/config assets; core functionality is built into playerbot images
MODULE_PLAYERBOTS=0
MODULE_PLAYER_BOT_LEVEL_BRACKETS=0
# ✨ Quality of Life
# Convenience features that improve gameplay experience
MODULE_AOE_LOOT=0
MODULE_AUTO_REVIVE=0
MODULE_FIREWORKS=0
MODULE_INSTANCE_RESET=0
MODULE_LEARN_SPELLS=0
MODULE_SOLO_LFG=0
# ⚔️ Gameplay Enhancement
# Core gameplay improvements and mechanics
MODULE_AUTOBALANCE=0
MODULE_CHALLENGE_MODES=0
MODULE_DUEL_RESET=0
MODULE_DUNGEON_RESPAWN=0
MODULE_HARDCORE_MODE=0
MODULE_HORADRIC_CUBE=0
MODULE_SOLOCRAFT=0
MODULE_STATBOOSTER=0
MODULE_TIME_IS_TIME=0
# 🏪 NPC Services
# Service NPCs that provide player utilities
MODULE_ASSISTANT=0
MODULE_MULTIVENDOR=0
MODULE_NPC_BEASTMASTER=0
MODULE_NPC_BUFFER=0
MODULE_NPC_ENCHANTER=0
MODULE_NPC_FREE_PROFESSIONS=0
# mod-npc-talent-template: Admin commands: .templatenpc create [TemplateName] and .templatenpc reload
MODULE_NPC_TALENT_TEMPLATE=0
MODULE_REAGENT_BANK=0
MODULE_TRANSMOG=0
# ⚡ PvP
# Player vs Player focused modules
MODULE_1V1_ARENA=0
# mod-arena-replay: NPC ID: 98500; known issue: players who were participants experience unusual behavior when watching their own replay
MODULE_ARENA_REPLAY=0
MODULE_GAIN_HONOR_GUARD=0
MODULE_PHASED_DUELS=0
MODULE_PVP_TITLES=0
MODULE_ULTIMATE_FULL_LOOT_PVP=0
# 📈 Progression
# Character and server progression systems
MODULE_DYNAMIC_XP=0
MODULE_INDIVIDUAL_PROGRESSION=0
MODULE_ITEM_LEVEL_UP=0
MODULE_LEVEL_GRANT=0
# mod-progression-system: SQL files cannot be unloaded once executed; requires auto DB updater enabled in worldserver config
MODULE_PROGRESSION_SYSTEM=0
MODULE_PROMOTION_AZEROTHCORE=0
MODULE_WEEKEND_XP=0
# mod-zone-difficulty: Mythicmode NPC 1128001 spawned in raids/heroic dungeons; NPC 1128002 for Mythicmode rewards
MODULE_ZONE_DIFFICULTY=0
# 💰 Economy
# Auction house, trading, and economic systems
MODULE_AHBOT=0
MODULE_BLACK_MARKET_AUCTION_HOUSE=0
MODULE_DYNAMIC_TRADER=0
MODULE_EXCHANGE_NPC=0
MODULE_GLOBAL_MAIL_BANKING_AUCTIONS=0
MODULE_LOTTERY_LUA=0
MODULE_LUA_AH_BOT=0
MODULE_RANDOM_ENCHANTS=0
# 👥 Social
# Social and community features
MODULE_ACTIVE_CHAT=0
MODULE_BOSS_ANNOUNCER=0
MODULE_BREAKING_NEWS=0
MODULE_DISCORD_NOTIFIER=0
MODULE_GLOBAL_CHAT=0
MODULE_TEMP_ANNOUNCEMENTS=0
# 👤 Account-Wide
# Features that apply across all characters on an account
MODULE_ACCOUNTWIDE_SYSTEMS=0
MODULE_ACCOUNT_ACHIEVEMENTS=0
MODULE_ACCOUNT_MOUNTS=0
# 🎨 Customization
# Character and appearance customization
MODULE_ARAC=0
# mod-morphsummon: Allows customization of summoned creature appearances (Warlock demons, Death Knight ghouls, Mage water elementals); NPC ID: 601072
MODULE_MORPHSUMMON=0
MODULE_TRANSMOG_AIO=0
MODULE_WORGOBLIN=0
# 📜 Scripting
# Lua/Eluna scripting frameworks and tools
# mod-aio: Azeroth Interface Override - enables client-server interface communication
MODULE_AIO=0
MODULE_ELUNA=1
MODULE_ELUNA_SCRIPTS=0
MODULE_ELUNA_TS=0
MODULE_EVENT_SCRIPTS=0
# 🔧 Admin Tools
# Server administration and management utilities
MODULE_ANTIFARMING=0
MODULE_CARBON_COPY=0
# mod-keep-out: Requires editing database table mod_mko_map_lock; use .gps command to obtain map and zone IDs
MODULE_KEEP_OUT=0
MODULE_SEND_AND_BIND=0
MODULE_SERVER_AUTO_SHUTDOWN=0
# mod-spell-regulator: WARNING: Custom code changes mandatory before module functions; requires custom hooks from external gist
MODULE_SPELL_REGULATOR=0
MODULE_WHO_LOGGED=0
MODULE_ZONE_CHECK=0
# 💎 Premium/VIP
# Premium account and VIP systems
MODULE_ACORE_SUBSCRIPTIONS=0
# mod-premium: Script must be assigned to an item (like hearthstone) using script name 'premium_account'
MODULE_PREMIUM=0
MODULE_SYSTEM_VIP=0
# 🎮 Mini-Games
# Fun and entertainment features
MODULE_AIO_BLACKJACK=0
MODULE_POCKET_PORTAL=0
# mod-tic-tac-toe: NPC ID: 100155
MODULE_TIC_TAC_TOE=0
# 🏰 Content
# Additional game content and features
MODULE_AZEROTHSHARD=0
MODULE_BG_SLAVERYVALLEY=0
MODULE_GUILDHOUSE=0
MODULE_TREASURE_CHEST_SYSTEM=0
MODULE_WAR_EFFORT=0
# 🎁 Rewards
# Player reward and incentive systems
MODULE_LEVEL_UP_REWARD=0
MODULE_PRESTIGE_DRAFT_MODE=0
MODULE_RECRUIT_A_FRIEND=0
# mod-resurrection-scroll: Requires EnablePlayerSettings to be enabled in worldserver config file
MODULE_RESURRECTION_SCROLL=0
MODULE_REWARD_PLAYED_TIME=0
# 🛠️ Developer Tools
# Development and testing utilities
MODULE_SKELETON_MODULE=0
# =====================
# Rebuild automation
# =====================
AUTO_REBUILD_ON_DEPLOY=0
# Default AzerothCore source checkout used for module rebuilds
MODULES_REBUILD_SOURCE_PATH=${STORAGE_PATH_LOCAL}/source/azerothcore
# =====================
# Source repositories
@@ -452,39 +297,111 @@ KEIRA_DATABASE_HOST=ac-mysql
KEIRA_DATABASE_PORT=3306
# Auto-generated defaults for new modules
MODULE_NPCBOT_EXTENDED_COMMANDS=0
MODULE_OLLAMA_CHAT=0
MODULE_PLAYERBOTS=0
MODULE_PLAYER_BOT_LEVEL_BRACKETS=0
MODULE_AOE_LOOT=0
MODULE_AUTO_REVIVE=0
MODULE_FIREWORKS=0
MODULE_INSTANCE_RESET=0
MODULE_LEARN_SPELLS=0
MODULE_SOLO_LFG=0
MODULE_AUTOBALANCE=0
MODULE_DUEL_RESET=0
MODULE_HARDCORE_MODE=0
MODULE_HORADRIC_CUBE=0
MODULE_SOLOCRAFT=0
MODULE_TIME_IS_TIME=0
MODULE_ASSISTANT=0
MODULE_NPC_BEASTMASTER=0
MODULE_NPC_BUFFER=0
MODULE_NPC_ENCHANTER=0
MODULE_NPC_FREE_PROFESSIONS=0
MODULE_NPC_TALENT_TEMPLATE=0
MODULE_REAGENT_BANK=0
MODULE_TRANSMOG=0
MODULE_1V1_ARENA=0
MODULE_ARENA_REPLAY=0
MODULE_GAIN_HONOR_GUARD=0
MODULE_PHASED_DUELS=0
MODULE_PVP_TITLES=0
MODULE_ULTIMATE_FULL_LOOT_PVP=0
MODULE_DYNAMIC_XP=0
MODULE_INDIVIDUAL_PROGRESSION=0
MODULE_ITEM_LEVEL_UP=0
MODULE_PROGRESSION_SYSTEM=0
MODULE_PROMOTION_AZEROTHCORE=0
MODULE_WEEKEND_XP=0
MODULE_ZONE_DIFFICULTY=0
MODULE_DYNAMIC_TRADER=0
MODULE_EXCHANGE_NPC=0
MODULE_GLOBAL_MAIL_BANKING_AUCTIONS=0
MODULE_LOTTERY_LUA=0
MODULE_LUA_AH_BOT=0
MODULE_RANDOM_ENCHANTS=0
MODULE_ACTIVE_CHAT=0
MODULE_BOSS_ANNOUNCER=0
MODULE_BREAKING_NEWS=0
MODULE_DISCORD_NOTIFIER=0
MODULE_GLOBAL_CHAT=0
MODULE_TEMP_ANNOUNCEMENTS=0
MODULE_ACCOUNTWIDE_SYSTEMS=0
MODULE_ACCOUNT_ACHIEVEMENTS=0
MODULE_ACCOUNT_MOUNTS=0
MODULE_ARAC=0
MODULE_MORPHSUMMON=0
MODULE_TRANSMOG_AIO=0
MODULE_WORGOBLIN=0
MODULE_AIO=0
MODULE_ELUNA=1
MODULE_ELUNA_SCRIPTS=0
MODULE_ELUNA_TS=0
MODULE_EVENT_SCRIPTS=0
MODULE_ANTIFARMING=0
MODULE_CARBON_COPY=0
MODULE_KEEP_OUT=0
MODULE_SEND_AND_BIND=0
MODULE_SERVER_AUTO_SHUTDOWN=0
MODULE_SPELL_REGULATOR=0
MODULE_WHO_LOGGED=0
MODULE_ZONE_CHECK=0
MODULE_PREMIUM=0
MODULE_SYSTEM_VIP=0
MODULE_AIO_BLACKJACK=0
MODULE_TIC_TAC_TOE=0
MODULE_BG_SLAVERYVALLEY=0
MODULE_GUILDHOUSE=0
MODULE_TREASURE_CHEST_SYSTEM=0
MODULE_WAR_EFFORT=0
MODULE_LEVEL_UP_REWARD=0
MODULE_PRESTIGE_DRAFT_MODE=0
MODULE_RECRUIT_A_FRIEND=0
MODULE_RESURRECTION_SCROLL=0
MODULE_REWARD_PLAYED_TIME=0
MODULE_SKELETON_MODULE=0
MODULE_1V1_PVP_SYSTEM=0
MODULE_ACI=0
MODULE_ACORE_API=0
MODULE_ACORE_BG_END_ANNOUNCER=0
MODULE_ACORE_BOX=0
MODULE_ACORE_CLIENT=0
MODULE_ACORE_CMS=0
MODULE_ACORE_ELUNATEST=0
MODULE_ACORE_LINUX_RESTARTER=0
MODULE_ACORE_LUA_UNLIMITED_AMMO=0
MODULE_ACORE_LXD_IMAGE=0
MODULE_ACORE_MALL=0
MODULE_ACORE_MINI_REG_PAGE=0
MODULE_ACORE_NODE_SERVER=0
MODULE_ACORE_PWA=0
MODULE_ACORE_SOD=0
MODULE_ACORE_SUMMONALL=0
MODULE_ACORE_TILEMAP=0
MODULE_ACORE_ZONEDEBUFF=0
MODULE_ACREBUILD=0
MODULE_ADDON_FACTION_FREE_UNIT_POPUP=0
MODULE_AOE_LOOT_MERGE=0
MODULE_APAW=0
MODULE_ARENA_SPECTATOR=0
MODULE_ARENA_STATS=0
MODULE_ATTRIBOOST=0
MODULE_AUTO_CHECK_RESTART=0
MODULE_AZEROTHCOREADMIN=0
MODULE_AZEROTHCOREDISCORDBOT=0
MODULE_AZEROTHCORE_ADDITIONS=0
MODULE_AZEROTHCORE_ALL_STACKABLES_200=0
MODULE_AZEROTHCORE_ANSIBLE=0
MODULE_AZEROTHCORE_ARMORY=0
MODULE_AZEROTHCORE_LUA_ARENA_MASTER_COMMAND=0
MODULE_AZEROTHCORE_LUA_DEMON_MORPHER=0
MODULE_AZEROTHCORE_PASSRESET=0
@@ -494,41 +411,25 @@ MODULE_AZEROTHCORE_TRIVIA_SYSTEM=0
MODULE_AZEROTHCORE_WEBSITE=0
MODULE_AZEROTHCORE_WOWHEAD_MOD_LUA=0
MODULE_AZTRAL_AIRLINES=0
MODULE_BGQUEUECHECKER=0
MODULE_BG_QUEUE_ABUSER_VIEWER=0
MODULE_BLIZZLIKE_TELES=0
MODULE_BREAKINGNEWSOVERRIDE=0
MODULE_CLASSIC_MODE=0
MODULE_CODEBASE=0
MODULE_CONFIG_RATES=0
MODULE_DEVJOESTAR=0
MODULE_ELUNA_WOW_SCRIPTS=0
MODULE_EXTENDEDXP=0
MODULE_EXTENDED_HOLIDAYS_LUA=0
MODULE_FFAFIX=0
MODULE_FLAG_CHECKER=0
MODULE_GUILDBANKTABFEEFIXER=0
MODULE_HARDMODE=0
MODULE_HEARTHSTONE_COOLDOWNS=0
MODULE_ITEMBROADCASTGUILDCHAT=0
MODULE_KARGATUM_SYSTEM=0
MODULE_KEIRA3=0
MODULE_LOTTERY_CHANCE_INSTANT=0
MODULE_LUA_AIO_MODRATE_EXP=0
MODULE_LUA_COMMAND_PLUS=0
MODULE_LUA_ITEMUPGRADER_TEMPLATE=0
MODULE_LUA_NOTONLY_RANDOMMORPHER=0
MODULE_LUA_PARAGON_ANNIVERSARY=0
MODULE_LUA_PVP_TITLES_RANKING_SYSTEM=0
MODULE_LUA_SCRIPTS=0
MODULE_LUA_SUPER_BUFFERNPC=0
MODULE_LUA_VIP=0
MODULE_MOD_ACCOUNTBOUND=0
MODULE_MOD_ACCOUNT_VANITY_PETS=0
MODULE_MOD_ACTIVATEZONES=0
MODULE_MOD_AH_BOT_PLUS=0
MODULE_MOD_ALPHA_REWARDS=0
MODULE_MOD_AOE_LOOT=0
MODULE_MOD_APPRECIATION=0
MODULE_MOD_ARENA_TIGERSPEAK=0
MODULE_MOD_ARENA_TOLVIRON=0
@@ -539,44 +440,29 @@ MODULE_MOD_BG_ITEM_REWARD=0
MODULE_MOD_BG_REWARD=0
MODULE_MOD_BG_TWINPEAKS=0
MODULE_MOD_BIENVENIDA=0
MODULE_MOD_BLACK_MARKET=0
MODULE_MOD_BRAWLERS_GUILD=0
MODULE_MOD_BUFF_COMMAND=0
MODULE_MOD_CFPVE=0
MODULE_MOD_CHANGEABLESPAWNRATES=0
MODULE_MOD_CHARACTER_SERVICES=0
MODULE_MOD_CHARACTER_TOOLS=0
MODULE_MOD_CHAT_TRANSMITTER=0
MODULE_MOD_CHROMIE_XP=0
MODULE_MOD_CONGRATS_ON_LEVEL=0
MODULE_MOD_COSTUMES=0
MODULE_MOD_CRAFTSPEED=0
MODULE_MOD_CTA_SWITCH=0
MODULE_MOD_DEAD_MEANS_DEAD=0
MODULE_MOD_DEATHROLL_AIO=0
MODULE_MOD_DEMONIC_PACT_CLASSIC=0
MODULE_MOD_DESERTION_WARNINGS=0
MODULE_MOD_DISCORD_ANNOUNCE=0
MODULE_MOD_DISCORD_WEBHOOK=0
MODULE_MOD_DMF_SWITCH=0
MODULE_MOD_DUNGEONMASTER=0
MODULE_MOD_DUNGEON_SCALE=0
MODULE_MOD_DYNAMIC_LOOT_RATES=0
MODULE_MOD_DYNAMIC_RESURRECTIONS=0
MODULE_MOD_ENCOUNTER_LOGS=0
MODULE_MOD_FACTION_FREE=0
MODULE_MOD_FIRSTLOGIN_AIO=0
MODULE_MOD_FLIGHTMASTER_WHISTLE=0
MODULE_MOD_FORTIS_AUTOBALANCE=0
MODULE_MOD_GAME_STATE_API=0
MODULE_MOD_GEDDON_BINDING_SHARD=0
MODULE_MOD_GHOST_SPEED=0
MODULE_MOD_GLOBALCHAT=0
MODULE_MOD_GM_COMMANDS=0
MODULE_MOD_GOMOVE=0
MODULE_MOD_GROWNUP=0
MODULE_MOD_GUILDFUNDS=0
MODULE_MOD_GUILD_VILLAGE=0
MODULE_MOD_GUILD_ZONE_SYSTEM=0
MODULE_MOD_HARDCORE=0
MODULE_MOD_HARDCORE_MAKGORA=0
@@ -585,32 +471,21 @@ MODULE_MOD_HIGH_RISK_SYSTEM=0
MODULE_MOD_HUNTER_PET_STORAGE=0
MODULE_MOD_IMPROVED_BANK=0
MODULE_MOD_INCREMENT_CACHE_VERSION=0
MODULE_MOD_INDIVIDUAL_XP=0
MODULE_MOD_INFLUXDB=0
MODULE_MOD_INSTANCE_TOOLS=0
MODULE_MOD_IP2NATION=0
MODULE_MOD_IP_TRACKER=0
MODULE_MOD_ITEMLEVEL=0
MODULE_MOD_ITEM_UPGRADE=0
MODULE_MOD_JUNK_TO_GOLD=0
MODULE_MOD_LEARNSPELLS=0
MODULE_MOD_LEECH=0
MODULE_MOD_LEVEL_15_BOOST=0
MODULE_MOD_LEVEL_ONE_MOUNTS=0
MODULE_MOD_LEVEL_REWARDS=0
MODULE_MOD_LOGIN_REWARDS=0
MODULE_MOD_LOW_LEVEL_ARENA=0
MODULE_MOD_LOW_LEVEL_RBG=0
MODULE_MOD_MISSING_OBJECTIVES=0
MODULE_MOD_MONEY_FOR_KILLS=0
MODULE_MOD_MOUNTS_ON_ACCOUNT=0
MODULE_MOD_MOUNT_REQUIREMENTS=0
MODULE_MOD_MULTI_VENDOR=0
MODULE_MOD_MYTHIC_PLUS=0
MODULE_MOD_NOCLIP=0
MODULE_MOD_NORDF=0
MODULE_MOD_NOTIFY_MUTED=0
MODULE_MOD_NO_FARMING=0
MODULE_MOD_NO_HEARTHSTONE_COOLDOWN=0
MODULE_MOD_NPC_ALL_MOUNTS=0
MODULE_MOD_NPC_CODEBOX=0
@@ -620,90 +495,64 @@ MODULE_MOD_NPC_PROMOTION=0
MODULE_MOD_NPC_SERVICES=0
MODULE_MOD_NPC_SPECTATOR=0
MODULE_MOD_NPC_SUBCLASS=0
MODULE_MOD_OBJSCALE=0
MODULE_MOD_OLLAMA_BOT_BUDDY=0
MODULE_MOD_ONY_NAXX_LOGOUT_TELEPORT=0
MODULE_MOD_PEACEKEEPER=0
MODULE_MOD_PETEQUIP=0
MODULE_MOD_PREMIUM=0
MODULE_MOD_PREMIUM_LIB=0
MODULE_MOD_PROFESSION_EXPERIENCE=0
MODULE_MOD_PROFSPECS=0
MODULE_MOD_PTR_TEMPLATE=0
MODULE_MOD_PVPSCRIPT=0
MODULE_MOD_PVPSTATS_ANNOUNCER=0
MODULE_MOD_PVP_ZONES=0
MODULE_MOD_QUEST_LOOT_PARTY=0
MODULE_MOD_QUEST_STATUS=0
MODULE_MOD_QUEUE_LIST_CACHE=0
MODULE_MOD_QUICKBALANCE=0
MODULE_MOD_QUICK_RESPAWN=0
MODULE_MOD_RACIAL_TRAIT_SWAP=0
MODULE_MOD_RARE_DROPS=0
MODULE_MOD_RDF_EXPANSION=0
MODULE_MOD_REAL_ONLINE=0
MODULE_MOD_RECRUIT_FRIEND=0
MODULE_MOD_REFORGING=0
MODULE_MOD_RESET_RAID_COOLDOWNS=0
MODULE_MOD_REWARD_PLAYED_TIME_IMPROVED=0
MODULE_MOD_REWARD_SHOP=0
MODULE_MOD_SELL_ITEMS=0
MODULE_MOD_SETXPBAR=0
MODULE_MOD_SHARE_MOUNTS=0
MODULE_MOD_SPAWNPOINTS=0
MODULE_MOD_SPEC_REWARD=0
MODULE_MOD_SPELLREGULATOR=0
MODULE_MOD_SPONSORSHIP=0
MODULE_MOD_STARTER_GUILD=0
MODULE_MOD_STARTER_WANDS=0
MODULE_MOD_STARTING_PET=0
MODULE_MOD_STREAMS=0
MODULE_MOD_SWIFT_TRAVEL_FORM=0
MODULE_MOD_TALENTBUTTON=0
MODULE_MOD_TRADE_ITEMS_FILTER=0
MODULE_MOD_TREASURE=0
MODULE_MOD_TRIAL_OF_FINALITY=0
MODULE_MOD_VANILLA_NAXXRAMAS=0
MODULE_MOD_WARLOCK_PET_RENAME=0
MODULE_MOD_WEAPON_VISUAL=0
MODULE_MOD_WEEKENDBONUS=0
MODULE_MOD_WEEKEND_XP=0
MODULE_MOD_WHOLOGGED=0
MODULE_MORZA_ISLAND_ARAXIA_SERVER=0
MODULE_MPQ_TOOLS_OSX=0
MODULE_MYSQL_TOOLS=0
MODULE_NODEROUTER=0
MODULE_OPENPROJECTS=0
MODULE_PLAYERTELEPORT=0
MODULE_PORTALS_IN_ALL_CAPITALS=0
MODULE_PRESTIGE=0
MODULE_PRESTIGIOUS=0
MODULE_PVPSTATS=0
MODULE_RAIDTELEPORTER=0
MODULE_RECACHE=0
MODULE_RECYCLEDITEMS=0
MODULE_REWARD_SYSTEM=0
MODULE_SAHTOUTCMS=0
MODULE_SERVER_STATUS=0
MODULE_SETXPBAR=0
MODULE_SPELLSCRIPT_REFACTOR_TOOL=0
MODULE_SQL_NPC_TELEPORTER=0
MODULE_STATBOOSTERREROLLER=0
MODULE_STRAPI_AZEROTHCORE=0
MODULE_TBC_RAID_HP_RESTORATION=0
MODULE_TELEGRAM_AUTOMATED_DB_BACKUP=0
MODULE_TOOL_TC_MIGRATION=0
MODULE_TRANSMOG_ADDONS=0
MODULE_UPDATE_MOB_LEVEL_TO_PLAYER_AND_RANDOM_ITEM_STATS=0
MODULE_UPDATE_MODULE_CONFS=0
MODULE_WEB_CHARACTER_MIGRATION_TOOL=0
MODULE_WEEKLY_ARMOR_VENDOR_BLACK_MARKET=0
MODULE_WORLD_BOSS_RANK=0
MODULE_WOWDATABASEEDITOR=0
MODULE_WOWLAUNCHER_DELPHI=0
MODULE_WOWSIMS_TO_COMMANDS=0
MODULE_WOW_CLIENT_PATCHER=0
MODULE_WOW_ELUNA_TS_MODULE=0
MODULE_WOW_SERVER_RELAY=0
MODULE_WOW_STATISTICS=0
MODULE_WRATH_OF_THE_VANILLA=0
MODULE_MOD_BOTS_LOGIN_FIX=0
MODULE_MOD_MATERIAL_BANK=0
MODULE_MOD_PROGRESSION_BLIZZLIKE=0
MODULE_MOD_PYTHON_ENGINE=0
MODULE_WRATH_OF_THE_VANILLA_V2=0

View File

@@ -17,13 +17,31 @@ jobs:
with:
python-version: '3.11'
- name: Configure git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Update manifest from GitHub topics
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 scripts/python/update_module_manifest.py --log
- name: Check for changes
id: changes
run: |
if git diff --quiet; then
echo "changed=false" >> $GITHUB_OUTPUT
echo "No changes detected in manifest or template files"
else
echo "changed=true" >> $GITHUB_OUTPUT
echo "Changes detected:"
git diff --name-only
fi
- name: Create Pull Request with changes
if: steps.changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: sync module manifest'

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ database-import/*.sql
database-import/*.sql.gz
database-import/*/
database-import/ImportBackup*/
db_*/
source/*
local-data-tools/
changelogs/

View File

@@ -1,87 +0,0 @@
# Changelog
## [2025-11-09] - Recent Changes
### ✨ Features
#### Backup System Enhancements
- **Manual Backup Support**: Added `manual-backup.sh` script (92 lines) enabling on-demand database backups through the ac-backup container
- **Backup Permission Fixes**: Resolved Docker volume permission issues with backup operations
- **Container User Configuration**: Backup operations now run as proper container user to avoid permission conflicts
#### Remote Deployment
- **Auto Deploy Option**: Added remote auto-deployment functionality to `deploy.sh` (36 additional lines) for automated server provisioning
#### Configuration Management System
- **Database/Config Import**: Major new feature with 1,405+ lines of code across 15 files
- Added `apply-config.py` (323 lines) for dynamic server configuration
- Created `configure-server.sh` (162 lines) for server setup automation
- Implemented `import-database-files.sh` (68 lines) for database initialization
- Added `parse-config-presets.py` (92 lines) for configuration templating
- **Configuration Presets**: 5 new server preset configurations
- `blizzlike.conf` - Authentic Blizzard-like experience
- `casual-pve.conf` - Relaxed PvE gameplay
- `fast-leveling.conf` - Accelerated character progression
- `hardcore-pvp.conf` - Competitive PvP settings
- `none.conf` - Minimal configuration baseline
- **Dynamic Server Overrides**: `server-overrides.conf` (134 lines) for customizable server parameters
- **Comprehensive Config Documentation**: `CONFIG_MANAGEMENT.md` (279 lines) detailing the entire configuration system
#### Infrastructure Improvements
- **MySQL Exposure Toggle**: Optional MySQL port exposure for external database access
- **Client Data Management**: Automatic client data detection, download, and binding with version detection
- **Dynamic Docker Overrides**: Flexible compose override system for modular container configurations
- **Module Profile System**: Structured module management with preset profiles
### 🏗️ Refactoring
#### Script Organization
- **Directory Restructure**: Reorganized all scripts into `scripts/bash/` and `scripts/python/` directories (40 files moved/modified)
- **Project Naming**: Added centralized project name management with `project_name.sh`
- **Module Manifest Rename**: Moved `modules.json``module-manifest.json` for clarity
### 🐛 Bug Fixes
#### Container Improvements
- **Client Data Container**: Enhanced with 7zip support, root access during extraction, and ownership fixes
- **Permission Resolution**: Fixed file ownership issues in client data extraction process
- **Path Updates**: Corrected deployment paths and script references after reorganization
### 📚 Documentation
#### Major Documentation Overhaul
- **Modular Documentation**: Split massive README into focused documents (1,500+ lines reorganized)
- `docs/GETTING_STARTED.md` (467 lines) - Setup and initial configuration
- `docs/MODULES.md` (264 lines) - Module management and customization
- `docs/SCRIPTS.md` (404 lines) - Script reference and automation
- `docs/ADVANCED.md` (207 lines) - Advanced configuration topics
- `docs/TROUBLESHOOTING.md` (127 lines) - Common issues and solutions
- **README Streamlining**: Reduced main README from 1,200+ to focused overview
- **Script Documentation**: Updated script references and usage examples throughout
### 🔧 Technical Changes
#### Development Experience
- **Setup Enhancements**: Improved `setup.sh` with better error handling and configuration options (66 lines added)
- **Status Monitoring**: Enhanced `status.sh` with better container and service monitoring
- **Build Process**: Updated build scripts with new directory structure and module handling
- **Cleanup Operations**: Improved cleanup scripts with proper path handling
#### DevOps & Deployment
- **Remote Cleanup**: Enhanced remote server cleanup and temporary file management
- **Network Binding**: Improved container networking and port management
- **Import Folder**: Added dedicated import directory structure
- **Development Onboarding**: Streamlined developer setup process
---
### Migration Notes
- Scripts have moved from `scripts/` to `scripts/bash/` and `scripts/python/`
- Module configuration is now in `config/module-manifest.json`
- New environment variables added for MySQL exposure and client data management
- Configuration presets are available in `config/presets/`
### Breaking Changes
- Script paths have changed due to reorganization
- Module manifest file has been renamed
- Some environment variables have been added/modified

View File

@@ -130,6 +130,13 @@ For diagnostic procedures, common issues, and backup system documentation, see *
This project builds upon:
- **[AzerothCore](https://github.com/azerothcore/azerothcore-wotlk)** - Core server application
- **[AzerothCore Module Community](https://github.com/azerothcore)** - Enhanced gameplay modules
- **[acore-docker](https://github.com/azerothcore/acore-docker)** - Inspiration for containerized deployment
- **[mod-playerbots](https://github.com/mod-playerbots/azerothcore-wotlk)** - Advanced playerbot functionality
- **All module creators** - Making amazing things every day
### Community & Support
- **[AzerothCore Discord](https://discord.gg/gkt4y2x)** - Join the community for support and discussions
- **[GitHub Issues](https://github.com/uprightbass360/AzerothCore-RealmMaster/issues)** - Report build or deployment issues here
#### Key Features
-**Fully Automated Setup** - Interactive configuration and deployment

View File

@@ -491,7 +491,7 @@
"block_reason": "Runtime error: SQL error: MODULE_mod-black-market_creature.sql references removed 'StatsCount' column",
"order": 5000,
"config_cleanup": [],
"notes": "Disabled due to runtime error: SQL error: MODULE_mod-black-market_creature.sql references removed 'StatsCount' column",
"notes": "Disabled due to runtime error: SQL error: MODULE_mod-black-market_creature.sql references removed 'StatsCount' column \nDiscovered via GitHub topic 'azerothcore-module'",
"last_modified": "2025-06-26T14:23:47Z"
},
{
@@ -1490,7 +1490,7 @@
"description": "Module for WoW 3.3.5a (AzerothCore \u2013 Playerbots). Tested on Ubuntu.",
"type": "cpp",
"category": "database",
"notes": "Disabled due to runtime error: MODULE_mod-guild-village_001_creature_template.sql tries to insert duplicate creature ID 987400 (ERROR 1062)",
"notes": "Disabled due to runtime error: MODULE_mod-guild-village_001_creature_template.sql tries to insert duplicate creature ID 987400 (ERROR 1062) \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -1581,7 +1581,7 @@
"description": "Module for WoW 3.3.5a (AzerothCore \u2013 Playerbots). Tested on Ubuntu.",
"type": "cpp",
"category": "database",
"notes": "Disabled due to runtime error: MODULE_mod-instance-tools_Creature.sql tries to insert duplicate creature ID 987456-0 (ERROR 1062)",
"notes": "Disabled due to runtime error: MODULE_mod-instance-tools_Creature.sql tries to insert duplicate creature ID 987456-0 (ERROR 1062) \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -1809,7 +1809,7 @@
"description": "Hardcore trial mod for groups of 1-5",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: no member named 'isEmpty' in 'MapRefMgr'",
"notes": "DISABLED: no member named 'isEmpty' in 'MapRefMgr' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2005,7 +2005,7 @@
"description": "AoE loot module for AzerothCore built from the ground up without loot loss. (No Loot Merging).",
"type": "cpp",
"category": "quality-of-life",
"notes": "DISABLED: Naming conflict with Item class",
"notes": "DISABLED: Naming conflict with Item class \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2142,7 +2142,7 @@
"description": "Adds Mist of Pandaria's Brawler's Guild to AzerothCore.",
"type": "cpp",
"category": "minigame",
"notes": "DISABLED: no matching member function for call to 'DelayEvents'",
"notes": "DISABLED: no matching member function for call to 'DelayEvents' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2188,7 +2188,7 @@
"description": "Mercado negro",
"type": "cpp",
"category": "database",
"notes": "DISABLED: SQL schema mismatch - StatsCount column doesn't exist",
"notes": "DISABLED: SQL schema mismatch - StatsCount column doesn't exist \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2835,7 +2835,7 @@
"description": "Prestige level system mod for Azerothcore",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: 'OnLogin' marked 'override' but does not override",
"notes": "DISABLED: 'OnLogin' marked 'override' but does not override \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2851,7 +2851,7 @@
"description": "Adds extra difficulty modes, heavily inspired by mod-challenge-modes.",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -2897,7 +2897,7 @@
"description": "This module aims to make mounts, companions and heirlooms shared across all characters of an account",
"type": "cpp",
"category": "account-wide",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3141,7 +3141,7 @@
"description": "An Azeroth Core module to allow the display of the Breaking News section on the character select screen.",
"type": "cpp",
"category": "social",
"notes": "DISABLED: no member named 'StringFormatFmt' in namespace 'Acore'",
"notes": "DISABLED: no member named 'StringFormatFmt' in namespace 'Acore' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3172,7 +3172,7 @@
"description": "This WoW-Azerothcore-Mod allows to change spawntimes based on a userdefined or dynamically calculated playerbased factor",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3188,7 +3188,7 @@
"description": "This mod allows noclip with a command noclip. on / off",
"type": "cpp",
"category": "admin",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3249,7 +3249,7 @@
"description": "AzerothCore port (as a Module) of Rochet2's Objscale",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3311,7 +3311,7 @@
"description": "An AzerothCore module that recycles unwanted items to the auction house.",
"type": "cpp",
"category": "economy",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3342,7 +3342,7 @@
"description": "AzerothCore module that adds a random attribute book.",
"type": "cpp",
"category": "admin",
"notes": "DISABLED: 'OnLogin' marked 'override' but does not override",
"notes": "DISABLED: 'OnLogin' marked 'override' but does not override \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3358,7 +3358,7 @@
"description": "AzerothCore module that allows prestige at max level.",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: use of undeclared identifier 'sSpellMgr'",
"notes": "DISABLED: use of undeclared identifier 'sSpellMgr' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3405,7 +3405,7 @@
"description": "An Azeroth Core module that adds alternative XP gains.",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3437,7 +3437,7 @@
"description": "Ensures all party members have matching faction before queueing into battleground.",
"type": "cpp",
"category": "pvp",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3484,7 +3484,7 @@
"description": "AzerothCore custom module which allows filtering traded items",
"type": "cpp",
"category": "economy",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3500,7 +3500,7 @@
"description": "This module allows you to search for quests by ID, which gives you greater precision in your search.",
"type": "cpp",
"category": "scripting",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3516,7 +3516,7 @@
"description": "PvPScript for Azerothcore",
"type": "cpp",
"category": "pvp",
"notes": "DISABLED: no member named 'SendNotification' in 'WorldSession'",
"notes": "DISABLED: no member named 'SendNotification' in 'WorldSession' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3532,7 +3532,7 @@
"description": "Broadcasts items with the ITEM_FLAG_REPORT_TO_GUILD_CHAT flag to guild chat.",
"type": "cpp",
"category": "scripting",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3548,7 +3548,7 @@
"description": "Fixes FFA for safe zones.",
"type": "cpp",
"category": "content",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3564,7 +3564,7 @@
"description": "AzerothCore Interconnect",
"type": "cpp",
"category": "tooling",
"notes": "DISABLED: no member named 'StringFormatFmt' in namespace 'Acore'",
"notes": "DISABLED: no member named 'StringFormatFmt' in namespace 'Acore' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3708,7 +3708,7 @@
"description": "Module for Azerothcore to teleport players to with a command",
"type": "cpp",
"category": "quality-of-life",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3739,7 +3739,7 @@
"description": "Adds a re-roll item for StatBooster bonus stat.",
"type": "cpp",
"category": "rewards",
"notes": "DISABLED: 'StatBoostMgr.h' file not found",
"notes": "DISABLED: 'StatBoostMgr.h' file not found \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3835,7 +3835,7 @@
"description": "All-In-One Solution module to easily enable features for new players",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: no member named 'getLevel'; did you mean 'GetLevel'?",
"notes": "DISABLED: no member named 'getLevel'; did you mean 'GetLevel'? \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3851,7 +3851,7 @@
"description": "Custom scipts and modules for Azerothcore",
"type": "cpp",
"category": "scripting",
"notes": "DISABLED: no member named 'PQuery' / 'outString' in Log",
"notes": "DISABLED: no member named 'PQuery' / 'outString' in Log \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3899,7 +3899,7 @@
"description": "",
"type": "cpp",
"category": "content",
"notes": "DISABLED: Missing config identifier",
"notes": "DISABLED: Missing config identifier \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3915,7 +3915,7 @@
"description": "Pushes server and player events into an influxdb instance.",
"type": "cpp",
"category": "scripting",
"notes": "DISABLED: Build fails - requires CURL library (missing: CURL_LIBRARY CURL_INCLUDE_DIR)",
"notes": "DISABLED: Build fails - requires CURL library (missing: CURL_LIBRARY CURL_INCLUDE_DIR) \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"block_reason": "CMake Error: Could NOT find CURL",
"order": 5000,
@@ -3931,7 +3931,7 @@
"description": "Spell Regulator module for AzerothCore",
"type": "cpp",
"category": "scripting",
"notes": "DISABLED: redefinition of 'AddSpellRegulatorScripts'",
"notes": "DISABLED: redefinition of 'AddSpellRegulatorScripts' \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3947,7 +3947,7 @@
"description": "Module for Azerothcore",
"type": "cpp",
"category": "progression",
"notes": "DISABLED: 'ChatHandler' is an incomplete type",
"notes": "DISABLED: 'ChatHandler' is an incomplete type \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -3979,7 +3979,7 @@
"description": "WIP reward system for AC ingame shop",
"type": "cpp",
"category": "economy",
"notes": "DISABLED: API incompatibility - ConfigMgr missing GetIntDefault method",
"notes": "DISABLED: API incompatibility - ConfigMgr missing GetIntDefault method \nDiscovered via GitHub topic 'azerothcore-module'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -4149,7 +4149,7 @@
"description": "ACore CMS based on Wordpress",
"type": "tool",
"category": "tooling",
"notes": "Disabled due to runtime error: Table 'acore_auth.acore_cms_subscriptions' doesn't exist - causes server abort",
"notes": "Disabled due to runtime error: Table 'acore_auth.acore_cms_subscriptions' doesn't exist - causes server abort \nDiscovered via GitHub topic 'azerothcore-tools'",
"status": "blocked",
"block_reason": "Runtime error: Missing database table",
"order": 5000,
@@ -5065,7 +5065,7 @@
"description": "",
"type": "lua",
"category": "scripting",
"notes": "DISABLED: Git clone fails with 'unknown switch E' error - likely due to hyphen in repo name",
"notes": "DISABLED: Git clone fails with 'unknown switch E' error - likely due to hyphen in repo name \nDiscovered via GitHub topic 'azerothcore-lua'",
"status": "blocked",
"block_reason": "Git clone error: unknown switch 'E'",
"order": 5000,
@@ -5233,7 +5233,7 @@
"description": "This module adds thematically appropriate green and blue loot drops to ALL 450 Classic rares in Kalimdor and the Eastern Kingdoms.",
"type": "sql",
"category": "database",
"notes": "DISABLED: only virtual member functions can be marked 'override'",
"notes": "DISABLED: only virtual member functions can be marked 'override' \nDiscovered via GitHub topic 'azerothcore-sql'",
"status": "blocked",
"order": 5000,
"requires": [],
@@ -5391,6 +5391,104 @@
"post_install_hooks": [],
"config_cleanup": [],
"last_modified": "2020-12-16T18:26:39Z"
},
{
"key": "MODULE_MOD_BOTS_LOGIN_FIX",
"name": "mod-bots-login-fix",
"repo": "https://github.com/BeardBear33/mod-bots-login-fix.git",
"description": "Oprava duplicitn\u00edho p\u0159ipojen\u00ed na AltBota pro modul Playerbots. // Fix for duplicate connections to an AltBot for the Playerbots module.",
"type": "cpp",
"category": "uncategorized",
"notes": "Discovered via GitHub topic 'azerothcore-module'",
"status": "active",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_MOD_MATERIAL_BANK",
"name": "mod-material-bank",
"repo": "https://github.com/BeardBear33/mod-material-bank.git",
"description": "Module for WoW 3.3.5a (AzerothCore \u2013 Playerbots). Tested on Ubuntu.",
"type": "cpp",
"category": "uncategorized",
"notes": "Discovered via GitHub topic 'azerothcore-module'",
"status": "active",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_MOD_PROGRESSION_BLIZZLIKE",
"name": "mod-progression-blizzlike",
"repo": "https://github.com/kambire/mod-progression-blizzlike.git",
"description": "Modular progression system for AzerothCore built around brackets (Vanilla/TBC/WotLK + Arena seasons). Each enabled bracket can load its own C++ scripts and SQL updates.",
"type": "cpp",
"category": "uncategorized",
"notes": "Discovered via GitHub topic 'azerothcore-module'",
"status": "active",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_MOD_PYTHON_ENGINE",
"name": "mod-python-engine",
"repo": "https://github.com/privatecore/mod-python-engine.git",
"description": "A Python Scripting Engine module for AzerothCore",
"type": "cpp",
"category": "uncategorized",
"notes": "Discovered via GitHub topic 'azerothcore-module'",
"status": "active",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_WRATH_OF_THE_VANILLA_V2",
"name": "wrath-of-the-vanilla-v2",
"repo": "https://github.com/Hextv/wrath-of-the-vanilla-v2.git",
"description": "Project that focuses on turning a server running AzerothCore into one limited to the original (vanilla) content.",
"type": "sql",
"category": "database",
"notes": "Discovered via GitHub topic 'azerothcore-module'",
"status": "active",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_AZEROTHMCP",
"name": "azerothMCP",
"repo": "https://github.com/blinkysc/azerothMCP.git",
"description": "MCP server for AzerothCore",
"type": "tool",
"category": "tooling",
"notes": "Discovered via GitHub topic 'azerothcore-tools' Not directly related to runtime functionality of AzerothCore",
"status": "blocked",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
},
{
"key": "MODULE_AZEROTHCORE_REALMMASTER",
"name": "AzerothCore-RealmMaster",
"repo": "https://github.com/uprightbass360/AzerothCore-RealmMaster.git",
"description": "Automated AzerothCore docker stack with tooling. ",
"type": "tool",
"category": "tooling",
"notes": "Discovered via GitHub topic 'azerothcore-tools'",
"status": "blocked",
"order": 5000,
"requires": [],
"post_install_hooks": [],
"config_cleanup": []
}
]
}

View File

@@ -2,7 +2,7 @@
"modules": [
],
"label": "\u2b50 AzerothCore Main - Mod Free",
"label": "\ud83d\udd30 AzerothCore Main - Mod Free",
"description": "Pure AzerothCore with no optional modules enabled",
"order": 3
}

View File

@@ -25,6 +25,7 @@ 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}
MYSQL_BINLOG_EXPIRE_LOGS_SECONDS: 86400
TZ: "${TZ}"
entrypoint:
- /usr/local/bin/mysql-entrypoint.sh
@@ -46,6 +47,9 @@ services:
- --innodb-buffer-pool-size=${MYSQL_INNODB_BUFFER_POOL_SIZE}
- --innodb-log-file-size=${MYSQL_INNODB_LOG_FILE_SIZE}
- --innodb-redo-log-capacity=${MYSQL_INNODB_REDO_LOG_CAPACITY}
- --expire_logs_days=0
- --binlog_expire_logs_seconds=86400
- --binlog_expire_logs_auto_purge=ON
restart: unless-stopped
logging: *logging-default
healthcheck:

View File

@@ -140,6 +140,147 @@ Restores user accounts and characters from backup while preserving world data.
- `acore_characters.sql[.gz]` - Character data (required)
- `acore_world.sql[.gz]` - World data (optional)
#### `scripts/bash/pdump-import.sh` - Character Import
Imports individual character dump files into the database.
```bash
# Import character from pdump file
./scripts/bash/pdump-import.sh --file character.pdump --account testuser --password azerothcore123
# Import with character rename
./scripts/bash/pdump-import.sh --file oldchar.pdump --account newuser --name "NewName" --password azerothcore123
# Validate pdump without importing (dry run)
./scripts/bash/pdump-import.sh --file character.pdump --account testuser --password azerothcore123 --dry-run
```
**Features:**
- Automatic GUID assignment or manual override with `--guid`
- Character renaming during import with `--name`
- Account validation and character name uniqueness checks
- Automatic database backup before import
- Safe server restart handling
#### `scripts/bash/import-pdumps.sh` - Batch Character Import
Processes multiple character dump files from the `import/pdumps/` directory.
```bash
# Import all pdumps with environment settings
./scripts/bash/import-pdumps.sh --password azerothcore123 --account defaultuser
# Non-interactive batch import
./scripts/bash/import-pdumps.sh --password azerothcore123 --non-interactive
```
**Directory Structure:**
```
import/pdumps/
├── character1.pdump # Character dump files
├── character2.sql # SQL dump files also supported
├── configs/ # Optional per-character configuration
│ ├── character1.conf # account=user1, name=NewName
│ └── character2.conf # account=user2, guid=5000
└── processed/ # Successfully imported files moved here
```
**Configuration Format (`.conf`):**
```ini
account=target_account_name_or_id
name=new_character_name # Optional: rename character
guid=force_specific_guid # Optional: force GUID
```
### Security Management Scripts
#### `scripts/bash/bulk-2fa-setup.sh` - Bulk 2FA Setup
Configures TOTP 2FA for multiple AzerothCore accounts using official SOAP API.
```bash
# Setup 2FA for all accounts without it
./scripts/bash/bulk-2fa-setup.sh --all
# Setup for specific accounts
./scripts/bash/bulk-2fa-setup.sh --account user1 --account user2
# Force regenerate with custom issuer
./scripts/bash/bulk-2fa-setup.sh --all --force --issuer "MyServer"
# Preview what would be done
./scripts/bash/bulk-2fa-setup.sh --all --dry-run
# Use custom SOAP credentials
./scripts/bash/bulk-2fa-setup.sh --all --soap-user admin --soap-pass adminpass
# Show help / options
./scripts/bash/bulk-2fa-setup.sh --help
```
**Features:**
- **Official AzerothCore API Integration**: Uses SOAP commands instead of direct database manipulation
- Generates AzerothCore-compatible 16-character Base32 TOTP secrets (longer secrets are rejected by SOAP)
- Automatic account discovery or specific targeting
- QR code generation for authenticator apps
- Force regeneration of existing 2FA secrets
- Comprehensive output with setup instructions
- Safe dry-run mode for testing
- SOAP connectivity validation
- Proper error handling and validation
**Requirements:**
- AzerothCore worldserver with SOAP enabled (SOAP.Enabled = 1)
- SOAP port exposed on 7778 (SOAP.Port = 7878, mapped to external 7778)
- Remote Access enabled (Ra.Enable = 1) in worldserver.conf
- SOAP.IP = "0.0.0.0" for external connectivity
- GM account with sufficient privileges (gmlevel 3)
- Provide SOAP credentials explicitly via `--soap-user` and `--soap-pass` (these are required; no env fallback)
**Output Structure:**
```
./2fa-setup-TIMESTAMP/
├── qr-codes/ # QR code images for each account
├── setup-report.txt # Complete setup summary
├── console-commands.txt # Manual verification commands
└── secrets-backup.csv # Secure backup of all secrets
```
**Security Notes:**
- Generated QR codes and backup files contain sensitive TOTP secrets
- Distribute QR codes securely to users
- Delete or encrypt backup files after distribution
- TOTP secrets are also stored in AzerothCore database
#### `scripts/bash/generate-2fa-qr.sh` / `generate-2fa-qr.py` - Individual 2FA Setup
Generate QR codes for individual account 2FA setup.
> Tip: each script supports `-h/--help` to see all options.
```bash
# Generate QR code for single account
./scripts/bash/generate-2fa-qr.sh -u username
# Use custom issuer and output path
./scripts/bash/generate-2fa-qr.sh -u username -i "MyServer" -o /tmp/qr.png
# Use existing secret
./scripts/bash/generate-2fa-qr.sh -u username -s JBSWY3DPEHPK3PXP
# Show help / options
./scripts/bash/generate-2fa-qr.sh -h
```
> AzerothCore's SOAP endpoint only accepts 16-character Base32 secrets (A-Z and 2-7). The generators enforce this length to avoid "The provided two-factor authentication secret is not valid" errors.
#### `scripts/bash/test-2fa-token.py` - Generate TOTP Test Codes
Quickly verify a 16-character Base32 secret produces valid 6-digit codes.
```bash
# Show help
./scripts/bash/test-2fa-token.py --help
# Generate two consecutive codes for a secret
./scripts/bash/test-2fa-token.py -s JBSWY3DPEHPK3PXP -c 2
```
### Module Management Scripts
#### `scripts/bash/stage-modules.sh` - Module Staging

View File

@@ -7,7 +7,8 @@ This directory allows you to easily import custom database files and configurati
```
import/
├── db/ # Database SQL files to import
── conf/ # Configuration file overrides
── conf/ # Configuration file overrides
└── pdumps/ # Character dump files to import
```
## 🗄️ Database Import (`import/db/`)
@@ -93,6 +94,31 @@ AiPlayerbot.MaxRandomBots = 200
See `config/CONFIG_MANAGEMENT.md` for detailed preset documentation.
## 🎮 Character Import (`import/pdumps/`)
Import character dump files from other AzerothCore servers.
### Supported Formats
- **`.pdump`** - Character dump files from `.pdump write` command
- **`.sql`** - SQL character dump files
### Quick Start
1. Place character dump files in `import/pdumps/`
2. Run the import script:
```bash
./scripts/bash/import-pdumps.sh --password your_mysql_password --account target_account
```
### Advanced Configuration
Create `import/pdumps/configs/filename.conf` for per-character settings:
```ini
account=target_account
name=NewCharacterName # Optional: rename
guid=5000 # Optional: force GUID
```
**📖 For complete character import documentation, see [import/pdumps/README.md](pdumps/README.md)**
## 🔄 Automated Import
Both database and configuration imports are automatically handled during:
@@ -118,6 +144,7 @@ Both database and configuration imports are automatically handled during:
## 📚 Related Documentation
- [Character Import Guide](pdumps/README.md) - Complete pdump import documentation
- [Database Management](../docs/DATABASE_MANAGEMENT.md)
- [Configuration Management](../config/CONFIG_MANAGEMENT.md)
- [Module Management](../docs/ADVANCED.md#module-management)

192
import/pdumps/README.md Normal file
View File

@@ -0,0 +1,192 @@
# Character PDump Import
This directory allows you to easily import character pdump files into your AzerothCore server.
## 📁 Directory Structure
```
import/pdumps/
├── README.md # This file
├── *.pdump # Place your character dump files here
├── *.sql # SQL dump files also supported
├── configs/ # Optional per-file configuration
│ ├── character1.conf
│ └── character2.conf
├── examples/ # Example files and configurations
└── processed/ # Successfully imported files are moved here
```
## 🎮 Character Dump Import
### Quick Start
1. **Place your pdump files** in this directory:
```bash
cp /path/to/mycharacter.pdump import/pdumps/
```
2. **Run the import script**:
```bash
./scripts/bash/import-pdumps.sh --password your_mysql_password --account target_account
```
3. **Login and play** - your characters are now available!
### Supported File Formats
- **`.pdump`** - Character dump files from AzerothCore `.pdump write` command
- **`.sql`** - SQL character dump files
### Configuration Options
#### Environment Variables (`.env`)
```bash
# Set default account for all imports
DEFAULT_IMPORT_ACCOUNT=testuser
# Database credentials (usually already set)
MYSQL_ROOT_PASSWORD=your_mysql_password
ACORE_DB_AUTH_NAME=acore_auth
ACORE_DB_CHARACTERS_NAME=acore_characters
```
#### Per-Character Configuration (`configs/filename.conf`)
Create a `.conf` file with the same name as your pdump file to specify custom import options:
**Example: `configs/mycharacter.conf`**
```ini
# Target account (required if not set globally)
account=testuser
# Rename character during import (optional)
name=NewCharacterName
# Force specific GUID (optional, auto-assigned if not specified)
guid=5000
```
### Command Line Usage
#### Import All Files
```bash
# Use environment settings
./scripts/bash/import-pdumps.sh
# Override settings
./scripts/bash/import-pdumps.sh --password mypass --account testuser
```
#### Import Single File
```bash
# Direct import with pdump-import.sh
./scripts/bash/pdump-import.sh --file character.pdump --account testuser --password mypass
# With character rename
./scripts/bash/pdump-import.sh --file oldchar.pdump --account newuser --name "NewName" --password mypass
# Validate before import (dry run)
./scripts/bash/pdump-import.sh --file character.pdump --account testuser --password mypass --dry-run
```
## 🛠️ Advanced Features
### Account Management
- **Account Validation**: Scripts automatically verify that target accounts exist
- **Account ID or Name**: You can use either account names or numeric IDs
- **Interactive Mode**: If no account is specified, you'll be prompted to enter one
### GUID Handling
- **Auto-Assignment**: Next available GUID is automatically assigned
- **Force GUID**: Use `--guid` parameter or config file to force specific GUID
- **Conflict Detection**: Import fails safely if GUID already exists
### Character Names
- **Validation**: Character names must follow WoW naming rules (2-12 letters)
- **Uniqueness**: Import fails if character name already exists on server
- **Renaming**: Use `--name` parameter or config file to rename during import
### Safety Features
- **Automatic Backup**: Characters database is backed up before each import
- **Server Management**: World server is safely stopped/restarted during import
- **Rollback Ready**: Backups are stored in `manual-backups/` directory
- **Dry Run**: Validate imports without actually importing
## 📋 Import Workflow
1. **Validation Phase**
- Check file format and readability
- Validate target account exists
- Verify character name availability (if specified)
- Check GUID conflicts
2. **Pre-Import Phase**
- Create automatic database backup
- Stop world server for safe import
3. **Processing Phase**
- Process SQL file (update account references, GUID, name)
- Import character data into database
4. **Post-Import Phase**
- Restart world server
- Verify import success
- Move processed files to `processed/` directory
## 🚨 Important Notes
### Before You Import
- **Backup Your Database**: Always backup before importing characters
- **Account Required**: Target account must exist in your auth database
- **Unique Names**: Character names must be unique across the entire server
- **Server Downtime**: World server is briefly restarted during import
### PDump Limitations
The AzerothCore pdump system has some known limitations:
- **Guild Data**: Guild information is not included in pdump files
- **Module Data**: Some module-specific data (transmog, reagent bank) may not transfer
- **Version Compatibility**: Pdump files from different database versions may have issues
### Troubleshooting
- **"Account not found"**: Verify account exists in auth database
- **"Character name exists"**: Use `--name` to rename or choose different name
- **"GUID conflicts"**: Use `--guid` to force different GUID or let system auto-assign
- **"Database errors"**: Check that pdump file is compatible with your database version
## 📚 Examples
### Basic Import
```bash
# Place file and import
cp character.pdump import/pdumps/
./scripts/bash/import-pdumps.sh --password mypass --account testuser
```
### Batch Import with Configuration
```bash
# Set up multiple characters
cp char1.pdump import/pdumps/
cp char2.pdump import/pdumps/
# Configure individual characters
echo "account=user1" > import/pdumps/configs/char1.conf
echo "account=user2
name=RenamedChar" > import/pdumps/configs/char2.conf
# Import all
./scripts/bash/import-pdumps.sh --password mypass
```
### Single Character Import
```bash
./scripts/bash/pdump-import.sh \
--file character.pdump \
--account testuser \
--name "MyNewCharacter" \
--password mypass
```
## 🔗 Related Documentation
- [Database Management](../../docs/DATABASE_MANAGEMENT.md)
- [Backup System](../../docs/TROUBLESHOOTING.md#backup-system)
- [Getting Started Guide](../../docs/GETTING_STARTED.md)

View File

@@ -0,0 +1,43 @@
#!/bin/bash
# Example batch import script
# This shows how to import multiple characters with different configurations
set -euo pipefail
MYSQL_PASSWORD="your_mysql_password_here"
echo "Setting up character import batch..."
# Create character-specific configurations
mkdir -p ../configs
# Character 1: Import to specific account
cat > ../configs/warrior.conf <<EOF
account=player1
EOF
# Character 2: Import with rename
cat > ../configs/mage.conf <<EOF
account=player2
name=NewMageName
EOF
# Character 3: Import with forced GUID
cat > ../configs/priest.conf <<EOF
account=player3
name=HolyPriest
guid=5000
EOF
echo "Configuration files created!"
echo ""
echo "Now place your pdump files:"
echo " warrior.pdump -> ../warrior.pdump"
echo " mage.pdump -> ../mage.pdump"
echo " priest.pdump -> ../priest.pdump"
echo ""
echo "Then run the import:"
echo " ../../../scripts/bash/import-pdumps.sh --password $MYSQL_PASSWORD"
echo ""
echo "Or import individually:"
echo " ../../../scripts/bash/pdump-import.sh --file ../warrior.pdump --account player1 --password $MYSQL_PASSWORD"

View File

@@ -0,0 +1,20 @@
# Example character import configuration
# Copy this file to configs/yourcharacter.conf and modify as needed
# Target account (required if DEFAULT_IMPORT_ACCOUNT is not set)
# Can be account name or account ID
account=testuser
# Rename character during import (optional)
# Must follow WoW naming rules: 2-12 letters, no numbers/special chars
name=NewCharacterName
# Force specific character GUID (optional)
# If not specified, next available GUID will be used automatically
# guid=5000
# Additional notes:
# - Account must exist in auth database before import
# - Character names must be unique across the server
# - GUID conflicts will cause import to fail
# - Use dry-run mode to test before actual import

584
scripts/bash/bulk-2fa-setup.sh Executable file
View File

@@ -0,0 +1,584 @@
#!/bin/bash
#
# AzerothCore Bulk 2FA Setup Script
# Generates and configures TOTP 2FA for multiple accounts
#
# Usage: ./scripts/bash/bulk-2fa-setup.sh [OPTIONS]
#
set -e
# Script directory for relative imports
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Source common utilities
source "$SCRIPT_DIR/lib/common.sh"
# Set environment paths
ENV_PATH="${ENV_PATH:-$PROJECT_ROOT/.env}"
DEFAULT_ENV_PATH="$PROJECT_ROOT/.env"
# =============================================================================
# GLOBAL VARIABLES
# =============================================================================
# Command line options
OPT_ALL=false
OPT_ACCOUNTS=()
OPT_FORCE=false
OPT_OUTPUT_DIR=""
OPT_DRY_RUN=false
OPT_ISSUER="AzerothCore"
OPT_FORMAT="qr"
# Container and database settings
WORLDSERVER_CONTAINER="ac-worldserver"
DATABASE_CONTAINER="ac-mysql"
MYSQL_PASSWORD=""
# SOAP settings for official AzerothCore API
SOAP_HOST="localhost"
SOAP_PORT="7778"
SOAP_USERNAME=""
SOAP_PASSWORD=""
# Output paths
OUTPUT_BASE_DIR=""
QR_CODES_DIR=""
SETUP_REPORT=""
CONSOLE_COMMANDS=""
SECRETS_BACKUP=""
# =============================================================================
# USAGE AND HELP
# =============================================================================
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Bulk 2FA setup for AzerothCore accounts using official SOAP API"
echo ""
echo "Options:"
echo " --all Process all non-bot accounts without 2FA"
echo " --account USERNAME Process specific account (can be repeated)"
echo " --force Regenerate 2FA even if already exists"
echo " --output-dir PATH Custom output directory"
echo " --dry-run Show what would be done without executing"
echo " --issuer NAME Issuer name for TOTP (default: AzerothCore)"
echo " --format [qr|manual] Output QR codes or manual setup info"
echo " --soap-user USERNAME SOAP API username (required)"
echo " --soap-pass PASSWORD SOAP API password (required)"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 --all # Setup 2FA for all accounts"
echo " $0 --account user1 --account user2 # Setup for specific accounts"
echo " $0 --all --force --issuer MyServer # Force regenerate with custom issuer"
echo " $0 --all --dry-run # Preview what would be done"
echo ""
echo "Requirements:"
echo " - AzerothCore worldserver with SOAP enabled on port 7778"
echo " - GM account with sufficient privileges for SOAP access"
echo " - Remote Access (Ra.Enable = 1) enabled in worldserver.conf"
}
# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================
# Check if required containers are running and healthy
check_containers() {
info "Checking container status..."
# Check worldserver container
if ! docker ps --format '{{.Names}}' | grep -q "^${WORLDSERVER_CONTAINER}$"; then
fatal "Container $WORLDSERVER_CONTAINER is not running"
fi
# Check if database container exists
if ! docker ps --format '{{.Names}}' | grep -q "^${DATABASE_CONTAINER}$"; then
fatal "Container $DATABASE_CONTAINER is not running"
fi
# Test database connectivity
if ! docker exec "$WORLDSERVER_CONTAINER" mysql -h "$DATABASE_CONTAINER" -u root -p"$MYSQL_PASSWORD" acore_auth -e "SELECT 1;" &>/dev/null; then
fatal "Cannot connect to AzerothCore database"
fi
# Test SOAP connectivity (only if credentials are available)
if [ -n "$SOAP_USERNAME" ] && [ -n "$SOAP_PASSWORD" ]; then
info "Testing SOAP API connectivity..."
if ! soap_result=$(soap_execute_command "server info"); then
fatal "Cannot connect to SOAP API: $soap_result"
fi
ok "SOAP API is accessible"
fi
ok "Containers are healthy and accessible"
}
# Execute MySQL query via container
mysql_query() {
local query="$1"
local database="${2:-acore_auth}"
docker exec "$WORLDSERVER_CONTAINER" mysql \
-h "$DATABASE_CONTAINER" \
-u root \
-p"$MYSQL_PASSWORD" \
"$database" \
-e "$query" \
2>/dev/null
}
# Execute SOAP command via AzerothCore official API
soap_execute_command() {
local command="$1"
local response
# Construct SOAP XML request
local soap_request='<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:ns1="urn:AC">
<SOAP-ENV:Body>
<ns1:executeCommand>
<command>'"$command"'</command>
</ns1:executeCommand>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
# Execute SOAP request
response=$(curl -s -X POST \
-H "Content-Type: text/xml" \
--user "$SOAP_USERNAME:$SOAP_PASSWORD" \
-d "$soap_request" \
"http://$SOAP_HOST:$SOAP_PORT/" 2>/dev/null)
# Flatten response for reliable parsing
local flat_response
flat_response=$(echo "$response" | tr -d '\n' | sed 's/\r//g')
# Check if response contains fault
if echo "$flat_response" | grep -q "SOAP-ENV:Fault"; then
# Extract fault string for error reporting
echo "$flat_response" | sed -n 's/.*<faultstring>\(.*\)<\/faultstring>.*/\1/p' | sed 's/&#xD;//g'
return 1
fi
# Extract successful result
echo "$flat_response" | sed -n 's/.*<result>\(.*\)<\/result>.*/\1/p' | sed 's/&#xD;//g'
return 0
}
# Generate Base32 TOTP secret
generate_totp_secret() {
# Use existing generation logic from generate-2fa-qr.sh
if command -v base32 >/dev/null 2>&1; then
openssl rand 10 | base32 -w0 | head -c16
else
# Fallback using Python
python3 -c "
import base64
import os
secret_bytes = os.urandom(10)
secret_b32 = base64.b32encode(secret_bytes).decode('ascii').rstrip('=')
print(secret_b32[:16])
"
fi
}
# Validate Base32 secret format
validate_base32_secret() {
local secret="$1"
if [[ ! "$secret" =~ ^[A-Z2-7]+$ ]]; then
return 1
fi
if [ ${#secret} -ne 16 ]; then
err "AzerothCore SOAP requires a 16-character Base32 secret (got ${#secret})"
return 1
fi
return 0
}
# =============================================================================
# ACCOUNT DISCOVERY FUNCTIONS
# =============================================================================
# Get all accounts that need 2FA setup
get_accounts_needing_2fa() {
local force="$1"
local query
if [ "$force" = "true" ]; then
# Include accounts that already have 2FA when force is enabled
query="SELECT username FROM account
WHERE username NOT LIKE 'rndbot%'
AND username NOT LIKE 'playerbot%'
ORDER BY username;"
else
# Only accounts without 2FA
query="SELECT username FROM account
WHERE (totp_secret IS NULL OR totp_secret = '')
AND username NOT LIKE 'rndbot%'
AND username NOT LIKE 'playerbot%'
ORDER BY username;"
fi
mysql_query "$query" | tail -n +2 # Remove header row
}
# Check if specific account exists
account_exists() {
local username="$1"
local result
result=$(mysql_query "SELECT COUNT(*) FROM account WHERE username = '$username';" | tail -n +2)
[ "$result" -eq 1 ]
}
# Check if account already has 2FA
account_has_2fa() {
local username="$1"
local result
result=$(mysql_query "SELECT COUNT(*) FROM account WHERE username = '$username' AND totp_secret IS NOT NULL AND totp_secret != '';" | tail -n +2)
[ "$result" -eq 1 ]
}
# =============================================================================
# 2FA SETUP FUNCTIONS
# =============================================================================
# Generate and set up 2FA for a single account
setup_2fa_for_account() {
local username="$1"
local force="$2"
local secret=""
local qr_output=""
info "Processing account: $username"
# Check if account exists
if ! account_exists "$username"; then
err "Account '$username' does not exist, skipping"
return 1
fi
# Check if account already has 2FA
if account_has_2fa "$username" && [ "$force" != "true" ]; then
warn "Account '$username' already has 2FA configured, use --force to regenerate"
return 0
fi
# Generate TOTP secret
secret=$(generate_totp_secret)
if [ -z "$secret" ] || ! validate_base32_secret "$secret"; then
err "Failed to generate valid TOTP secret for $username"
return 1
fi
if [ "$OPT_DRY_RUN" = "true" ]; then
log "DRY RUN: Would set 2FA secret for $username: $secret"
return 0
fi
# Set 2FA using official AzerothCore SOAP API
local soap_result
if ! soap_result=$(soap_execute_command ".account set 2fa $username $secret"); then
err "Failed to set 2FA for $username via SOAP API: $soap_result"
return 1
fi
# Verify success message
if ! echo "$soap_result" | grep -q "Successfully enabled two-factor authentication"; then
err "Unexpected SOAP response for $username: $soap_result"
return 1
fi
# Generate QR code if format is 'qr'
if [ "$OPT_FORMAT" = "qr" ]; then
qr_output="$QR_CODES_DIR/${username}_2fa_qr.png"
if ! "$SCRIPT_DIR/generate-2fa-qr.sh" -u "$username" -s "$secret" -i "$OPT_ISSUER" -o "$qr_output" >/dev/null; then
warn "Failed to generate QR code for $username, but secret was saved"
fi
fi
# Log setup information
echo "$username,$secret,$(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> "$SECRETS_BACKUP"
echo "account set 2fa $username $secret" >> "$CONSOLE_COMMANDS"
ok "2FA configured for account: $username"
return 0
}
# =============================================================================
# OUTPUT AND REPORTING FUNCTIONS
# =============================================================================
# Create output directory structure
create_output_structure() {
local timestamp
timestamp=$(date +"%Y%m%d%H%M%S")
if [ -n "$OPT_OUTPUT_DIR" ]; then
OUTPUT_BASE_DIR="$OPT_OUTPUT_DIR"
else
OUTPUT_BASE_DIR="$PROJECT_ROOT/2fa-setup-$timestamp"
fi
# Create directories
mkdir -p "$OUTPUT_BASE_DIR"
QR_CODES_DIR="$OUTPUT_BASE_DIR/qr-codes"
mkdir -p "$QR_CODES_DIR"
# Set up output files
SETUP_REPORT="$OUTPUT_BASE_DIR/setup-report.txt"
CONSOLE_COMMANDS="$OUTPUT_BASE_DIR/console-commands.txt"
SECRETS_BACKUP="$OUTPUT_BASE_DIR/secrets-backup.csv"
# Initialize files
echo "# AzerothCore 2FA Console Commands" > "$CONSOLE_COMMANDS"
echo "# Generated on $(date)" >> "$CONSOLE_COMMANDS"
echo "" >> "$CONSOLE_COMMANDS"
echo "username,secret,generated_date" > "$SECRETS_BACKUP"
info "Output directory: $OUTPUT_BASE_DIR"
}
# Generate final setup report
generate_setup_report() {
local total_processed="$1"
local successful="$2"
local failed="$3"
{
echo "AzerothCore Bulk 2FA Setup Report"
echo "================================="
echo ""
echo "Generated: $(date)"
echo "Command: $0 $*"
echo ""
echo "Summary:"
echo "--------"
echo "Total accounts processed: $total_processed"
echo "Successfully configured: $successful"
echo "Failed: $failed"
echo ""
echo "Output Files:"
echo "-------------"
echo "- QR Codes: $QR_CODES_DIR/"
echo "- Console Commands: $CONSOLE_COMMANDS"
echo "- Secrets Backup: $SECRETS_BACKUP"
echo ""
echo "Next Steps:"
echo "-----------"
echo "1. Distribute QR codes to users securely"
echo "2. Users scan QR codes with authenticator apps"
echo "3. Verify setup using console commands if needed"
echo "4. Store secrets backup securely and delete when no longer needed"
echo ""
echo "Security Notes:"
echo "--------------"
echo "- QR codes contain sensitive TOTP secrets"
echo "- Secrets backup file contains plaintext secrets"
echo "- Delete or encrypt these files after distribution"
echo "- Secrets are also stored in AzerothCore database"
} > "$SETUP_REPORT"
info "Setup report generated: $SETUP_REPORT"
}
# =============================================================================
# MAIN SCRIPT LOGIC
# =============================================================================
# Parse command line arguments
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
--all)
OPT_ALL=true
shift
;;
--account)
if [ -z "$2" ]; then
fatal "Option --account requires a username argument"
fi
OPT_ACCOUNTS+=("$2")
shift 2
;;
--force)
OPT_FORCE=true
shift
;;
--output-dir)
if [ -z "$2" ]; then
fatal "Option --output-dir requires a path argument"
fi
OPT_OUTPUT_DIR="$2"
shift 2
;;
--dry-run)
OPT_DRY_RUN=true
shift
;;
--issuer)
if [ -z "$2" ]; then
fatal "Option --issuer requires a name argument"
fi
OPT_ISSUER="$2"
shift 2
;;
--format)
if [ -z "$2" ]; then
fatal "Option --format requires qr or manual"
fi
if [[ "$2" != "qr" && "$2" != "manual" ]]; then
fatal "Format must be 'qr' or 'manual'"
fi
OPT_FORMAT="$2"
shift 2
;;
--soap-user)
if [ -z "$2" ]; then
fatal "Option --soap-user requires a username argument"
fi
SOAP_USERNAME="$2"
shift 2
;;
--soap-pass)
if [ -z "$2" ]; then
fatal "Option --soap-pass requires a password argument"
fi
SOAP_PASSWORD="$2"
shift 2
;;
-h|--help)
show_usage
exit 0
;;
*)
fatal "Unknown option: $1"
;;
esac
done
}
# Main execution function
main() {
local accounts_to_process=()
local total_processed=0
local successful=0
local failed=0
# Show help if no arguments were provided
if [ $# -eq 0 ]; then
show_usage
exit 1
fi
# Parse arguments
parse_arguments "$@"
# Validate options
if [ "$OPT_ALL" = "false" ] && [ ${#OPT_ACCOUNTS[@]} -eq 0 ]; then
fatal "Must specify either --all or --account USERNAME"
fi
if [ "$OPT_ALL" = "true" ] && [ ${#OPT_ACCOUNTS[@]} -gt 0 ]; then
fatal "Cannot use --all with specific --account options"
fi
# Load environment variables
MYSQL_PASSWORD=$(read_env "MYSQL_ROOT_PASSWORD" "")
if [ -z "$MYSQL_PASSWORD" ]; then
fatal "MYSQL_ROOT_PASSWORD not found in environment"
fi
# Require SOAP credentials via CLI flags
if [ -z "$SOAP_USERNAME" ] || [ -z "$SOAP_PASSWORD" ]; then
fatal "SOAP credentials required. Provide --soap-user and --soap-pass."
fi
# Check container health
check_containers
# Create output structure
create_output_structure
# Determine accounts to process
if [ "$OPT_ALL" = "true" ]; then
info "Discovering accounts that need 2FA setup..."
readarray -t accounts_to_process < <(get_accounts_needing_2fa "$OPT_FORCE")
if [ ${#accounts_to_process[@]} -eq 0 ]; then
if [ "$OPT_FORCE" = "true" ]; then
warn "No accounts found in database"
else
ok "All accounts already have 2FA configured"
fi
exit 0
fi
info "Found ${#accounts_to_process[@]} accounts to process"
else
accounts_to_process=("${OPT_ACCOUNTS[@]}")
fi
# Display dry run information
if [ "$OPT_DRY_RUN" = "true" ]; then
warn "DRY RUN MODE - No changes will be made"
info "Would process the following accounts:"
for account in "${accounts_to_process[@]}"; do
echo " - $account"
done
echo ""
fi
# Process each account
info "Processing ${#accounts_to_process[@]} accounts..."
for account in "${accounts_to_process[@]}"; do
total_processed=$((total_processed + 1))
if setup_2fa_for_account "$account" "$OPT_FORCE"; then
successful=$((successful + 1))
else
failed=$((failed + 1))
fi
done
# Generate final report
if [ "$OPT_DRY_RUN" = "false" ]; then
generate_setup_report "$total_processed" "$successful" "$failed"
# Summary
echo ""
ok "Bulk 2FA setup completed"
info "Processed: $total_processed accounts"
info "Successful: $successful"
info "Failed: $failed"
info "Output directory: $OUTPUT_BASE_DIR"
if [ "$failed" -gt 0 ]; then
warn "Some accounts failed to process. Check the output for details."
exit 1
fi
else
info "Dry run completed. Use without --dry-run to execute."
if [ "$failed" -gt 0 ]; then
warn "Some accounts would fail to process."
exit 1
fi
fi
}
# Execute main function with all arguments
main "$@"

View File

@@ -219,10 +219,12 @@ if [ -z "$backup_path" ]; then
echo "📦 Latest daily backup found: $latest_daily"
for backup_file in "$BACKUP_DIRS/daily/$latest_daily"/*.sql.gz; do
if [ -f "$backup_file" ] && [ -s "$backup_file" ]; then
if timeout 10 zcat "$backup_file" 2>/dev/null | head -20 | grep -q "CREATE DATABASE\|INSERT INTO\|CREATE TABLE"; then
if timeout 10 gzip -t "$backup_file" >/dev/null 2>&1; then
echo "✅ Valid daily backup file: $(basename "$backup_file")"
backup_path="$BACKUP_DIRS/daily/$latest_daily"
break 2
else
echo "⚠️ gzip validation failed for $(basename "$backup_file")"
fi
fi
done
@@ -237,10 +239,12 @@ if [ -z "$backup_path" ]; then
echo "📦 Latest hourly backup found: $latest_hourly"
for backup_file in "$BACKUP_DIRS/hourly/$latest_hourly"/*.sql.gz; do
if [ -f "$backup_file" ] && [ -s "$backup_file" ]; then
if timeout 10 zcat "$backup_file" >/dev/null 2>&1; then
if timeout 10 gzip -t "$backup_file" >/dev/null 2>&1; then
echo "✅ Valid hourly backup file: $(basename "$backup_file")"
backup_path="$BACKUP_DIRS/hourly/$latest_hourly"
break 2
else
echo "⚠️ gzip validation failed for $(basename "$backup_file")"
fi
fi
done
@@ -259,10 +263,12 @@ if [ -z "$backup_path" ]; then
echo "🔍 Validating timestamped backup content..."
for backup_file in "$BACKUP_DIRS/$latest_timestamped"/*.sql.gz; do
if [ -f "$backup_file" ] && [ -s "$backup_file" ]; then
if timeout 10 zcat "$backup_file" >/dev/null 2>&1; then
if timeout 10 gzip -t "$backup_file" >/dev/null 2>&1; then
echo "✅ Valid timestamped backup found: $(basename "$backup_file")"
backup_path="$BACKUP_DIRS/$latest_timestamped"
break 2
else
echo "⚠️ gzip validation failed for $(basename "$backup_file")"
fi
fi
done

116
scripts/bash/generate-2fa-qr.py Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
AzerothCore 2FA QR Code Generator (Python version)
Generates TOTP secrets and QR codes for AzerothCore accounts
"""
import argparse
import base64
import os
import sys
import re
def validate_base32(secret):
"""Validate Base32 secret format"""
if not re.match(r'^[A-Z2-7]+$', secret):
print("Error: Invalid Base32 secret. Only A-Z and 2-7 characters allowed.", file=sys.stderr)
return False
if len(secret) != 16:
print(f"Error: AzerothCore SOAP requires a 16-character Base32 secret (got {len(secret)}).", file=sys.stderr)
return False
return True
def generate_secret():
"""Generate a random 16-character Base32 secret (AzerothCore SOAP requirement)"""
secret_bytes = os.urandom(10)
secret_b32 = base64.b32encode(secret_bytes).decode('ascii').rstrip('=')
return secret_b32[:16]
def generate_qr_code(uri, output_path):
"""Generate QR code using available library"""
try:
import qrcode
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=6,
border=4,
)
qr.add_data(uri)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save(output_path)
return True
except ImportError:
print("Error: qrcode library not installed.", file=sys.stderr)
print("Install it with: pip3 install qrcode[pil]", file=sys.stderr)
return False
def main():
parser = argparse.ArgumentParser(
description="Generate TOTP secrets and QR codes for AzerothCore 2FA",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s -u john_doe
%(prog)s -u john_doe -o /tmp/qr.png
%(prog)s -u john_doe -s JBSWY3DPEHPK3PXP -i MyServer
"""
)
parser.add_argument('-u', '--username', required=True,
help='Target username for 2FA setup')
parser.add_argument('-o', '--output',
help='Path to save QR code image (default: ./USERNAME_2fa_qr.png)')
parser.add_argument('-s', '--secret',
help='Use existing 16-character Base32 secret (generates random if not provided)')
parser.add_argument('-i', '--issuer', default='AzerothCore',
help='Issuer name for the TOTP entry (default: AzerothCore)')
args = parser.parse_args()
# Set default output path
if not args.output:
args.output = f"./{args.username}_2fa_qr.png"
# Generate or validate secret
if args.secret:
print("Using provided secret...")
if not validate_base32(args.secret):
sys.exit(1)
secret = args.secret
else:
print("Generating new TOTP secret...")
secret = generate_secret()
print(f"Generated secret: {secret}")
# Create TOTP URI
uri = f"otpauth://totp/{args.issuer}:{args.username}?secret={secret}&issuer={args.issuer}"
# Generate QR code
print("Generating QR code...")
if generate_qr_code(uri, args.output):
print(f"✓ QR code generated successfully: {args.output}")
else:
print("\nManual setup information:")
print(f"Secret: {secret}")
print(f"URI: {uri}")
sys.exit(1)
# Display setup information
print("\n=== AzerothCore 2FA Setup Information ===")
print(f"Username: {args.username}")
print(f"Secret: {secret}")
print(f"QR Code: {args.output}")
print(f"Issuer: {args.issuer}")
print("\nNext steps:")
print("1. Share the QR code image with the user")
print("2. User scans QR code with authenticator app")
print("3. Run on AzerothCore console:")
print(f" account set 2fa {args.username} {secret}")
print("4. User can now use 6-digit codes for login")
print("\nSecurity Note: Keep the secret secure and delete the QR code after setup.")
if __name__ == "__main__":
main()

166
scripts/bash/generate-2fa-qr.sh Executable file
View File

@@ -0,0 +1,166 @@
#!/bin/bash
# AzerothCore 2FA QR Code Generator
# Generates TOTP secrets and QR codes for AzerothCore accounts
set -e
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to display usage
show_usage() {
echo "Usage: $0 -u USERNAME [-o OUTPUT_PATH] [-s SECRET] [-i ISSUER]"
echo ""
echo "Options:"
echo " -u USERNAME Target username for 2FA setup (required)"
echo " -o OUTPUT_PATH Path to save QR code image (default: ./USERNAME_2fa_qr.png)"
echo " -s SECRET Use existing 16-character Base32 secret (generates random if not provided)"
echo " -i ISSUER Issuer name for the TOTP entry (default: AzerothCore)"
echo " -h Show this help message"
echo ""
echo "Examples:"
echo " $0 -u john_doe"
echo " $0 -u john_doe -o /tmp/qr.png"
echo " $0 -u john_doe -s JBSWY3DPEHPK3PXP -i MyServer"
}
# Function to validate Base32
validate_base32() {
local secret="$1"
if [[ ! "$secret" =~ ^[A-Z2-7]+$ ]]; then
echo -e "${RED}Error: Invalid Base32 secret. Only A-Z and 2-7 characters allowed.${NC}" >&2
return 1
fi
if [ ${#secret} -ne 16 ]; then
echo -e "${RED}Error: AzerothCore SOAP requires a 16-character Base32 secret (got ${#secret}).${NC}" >&2
return 1
fi
}
# Function to generate Base32 secret
generate_secret() {
# Generate 10 random bytes and encode as 16-character Base32 (AzerothCore SOAP requirement)
if command -v base32 >/dev/null 2>&1; then
openssl rand 10 | base32 -w0 | head -c16
else
# Fallback using Python if base32 command not available
python3 -c "
import base64
import os
secret_bytes = os.urandom(10)
secret_b32 = base64.b32encode(secret_bytes).decode('ascii').rstrip('=')
print(secret_b32[:16])
"
fi
}
# Default values
USERNAME=""
OUTPUT_PATH=""
SECRET=""
ISSUER="AzerothCore"
# Parse command line arguments
while getopts "u:o:s:i:h" opt; do
case ${opt} in
u )
USERNAME="$OPTARG"
;;
o )
OUTPUT_PATH="$OPTARG"
;;
s )
SECRET="$OPTARG"
;;
i )
ISSUER="$OPTARG"
;;
h )
show_usage
exit 0
;;
\? )
echo -e "${RED}Invalid option: $OPTARG${NC}" 1>&2
show_usage
exit 1
;;
: )
echo -e "${RED}Invalid option: $OPTARG requires an argument${NC}" 1>&2
show_usage
exit 1
;;
esac
done
# Validate required parameters
if [ -z "$USERNAME" ]; then
echo -e "${RED}Error: Username is required.${NC}" >&2
show_usage
exit 1
fi
# Set default output path if not provided
if [ -z "$OUTPUT_PATH" ]; then
OUTPUT_PATH="./${USERNAME}_2fa_qr.png"
fi
# Generate secret if not provided
if [ -z "$SECRET" ]; then
echo -e "${BLUE}Generating new TOTP secret...${NC}"
SECRET=$(generate_secret)
if [ -z "$SECRET" ]; then
echo -e "${RED}Error: Failed to generate secret.${NC}" >&2
exit 1
fi
echo -e "${GREEN}Generated secret: $SECRET${NC}"
else
echo -e "${BLUE}Using provided secret...${NC}"
if ! validate_base32 "$SECRET"; then
exit 1
fi
fi
# Create TOTP URI
URI="otpauth://totp/${ISSUER}:${USERNAME}?secret=${SECRET}&issuer=${ISSUER}"
# Check if qrencode is available
if ! command -v qrencode >/dev/null 2>&1; then
echo -e "${RED}Error: qrencode is not installed.${NC}" >&2
echo "Install it with: sudo apt-get install qrencode (Ubuntu/Debian) or brew install qrencode (macOS)"
echo ""
echo -e "${BLUE}Manual setup information:${NC}"
echo "Secret: $SECRET"
echo "URI: $URI"
exit 1
fi
# Generate QR code
echo -e "${BLUE}Generating QR code...${NC}"
if echo "$URI" | qrencode -s 6 -o "$OUTPUT_PATH"; then
echo -e "${GREEN}✓ QR code generated successfully: $OUTPUT_PATH${NC}"
else
echo -e "${RED}Error: Failed to generate QR code.${NC}" >&2
exit 1
fi
# Display setup information
echo ""
echo -e "${YELLOW}=== AzerothCore 2FA Setup Information ===${NC}"
echo "Username: $USERNAME"
echo "Secret: $SECRET"
echo "QR Code: $OUTPUT_PATH"
echo "Issuer: $ISSUER"
echo ""
echo -e "${BLUE}Next steps:${NC}"
echo "1. Share the QR code image with the user"
echo "2. User scans QR code with authenticator app"
echo "3. Run on AzerothCore console:"
echo -e " ${GREEN}account set 2fa $USERNAME $SECRET${NC}"
echo "4. User can now use 6-digit codes for login"
echo ""
echo -e "${YELLOW}Security Note: Keep the secret secure and delete the QR code after setup.${NC}"

283
scripts/bash/import-pdumps.sh Executable file
View File

@@ -0,0 +1,283 @@
#!/bin/bash
# Process and import character pdump files from import/pdumps/ directory
set -euo pipefail
INVOCATION_DIR="$PWD"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/../.." # Go to project root
COLOR_RED='\033[0;31m'
COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[1;33m'
COLOR_BLUE='\033[0;34m'
COLOR_RESET='\033[0m'
log(){ printf '%b\n' "${COLOR_GREEN}$*${COLOR_RESET}"; }
warn(){ printf '%b\n' "${COLOR_YELLOW}$*${COLOR_RESET}"; }
err(){ printf '%b\n' "${COLOR_RED}$*${COLOR_RESET}"; }
info(){ printf '%b\n' "${COLOR_BLUE}$*${COLOR_RESET}"; }
fatal(){ err "$*"; exit 1; }
# Source environment variables
if [ -f ".env" ]; then
set -a
source .env
set +a
fi
IMPORT_DIR="./import/pdumps"
MYSQL_PW="${MYSQL_ROOT_PASSWORD:-}"
AUTH_DB="${ACORE_DB_AUTH_NAME:-acore_auth}"
CHARACTERS_DB="${ACORE_DB_CHARACTERS_NAME:-acore_characters}"
DEFAULT_ACCOUNT="${DEFAULT_IMPORT_ACCOUNT:-}"
INTERACTIVE=${INTERACTIVE:-true}
usage(){
cat <<'EOF'
Usage: ./import-pdumps.sh [options]
Automatically process and import all character pdump files from import/pdumps/ directory.
Options:
--password PASS MySQL root password (overrides env)
--account ACCOUNT Default account for imports (overrides env)
--auth-db NAME Auth database name (overrides env)
--characters-db NAME Characters database name (overrides env)
--non-interactive Don't prompt for missing information
-h, --help Show this help and exit
Directory Structure:
import/pdumps/
├── character1.pdump # Will be imported with default settings
├── character2.sql # SQL dump files also supported
└── configs/ # Optional: per-file configuration
├── character1.conf # account=testuser, name=NewName
└── character2.conf # account=12345, guid=5000
Configuration File Format (.conf):
account=target_account_name_or_id
name=new_character_name # Optional: rename character
guid=force_specific_guid # Optional: force GUID
Environment Variables:
MYSQL_ROOT_PASSWORD # MySQL root password
DEFAULT_IMPORT_ACCOUNT # Default account for imports
ACORE_DB_AUTH_NAME # Auth database name
ACORE_DB_CHARACTERS_NAME # Characters database name
Examples:
# Import all pdumps with environment settings
./import-pdumps.sh
# Import with specific password and account
./import-pdumps.sh --password mypass --account testuser
EOF
}
check_dependencies(){
if ! docker ps >/dev/null 2>&1; then
fatal "Docker is not running or accessible"
fi
if ! docker exec ac-mysql mysql --version >/dev/null 2>&1; then
fatal "MySQL container (ac-mysql) is not running or accessible"
fi
}
parse_config_file(){
local config_file="$1"
local -A config=()
if [[ -f "$config_file" ]]; then
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "$key" ]] && continue
# Remove leading/trailing whitespace
key=$(echo "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
value=$(echo "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
config["$key"]="$value"
done < "$config_file"
fi
# Export as variables for the calling function
export CONFIG_ACCOUNT="${config[account]:-}"
export CONFIG_NAME="${config[name]:-}"
export CONFIG_GUID="${config[guid]:-}"
}
prompt_for_account(){
local filename="$1"
if [[ "$INTERACTIVE" != "true" ]]; then
fatal "No account specified for $filename and running in non-interactive mode"
fi
echo ""
warn "No account specified for: $filename"
echo "Available options:"
echo " 1. Provide account name or ID"
echo " 2. Skip this file"
echo ""
while true; do
read -p "Enter account name/ID (or 'skip'): " account_input
case "$account_input" in
skip|Skip|SKIP)
return 1
;;
"")
warn "Please enter an account name/ID or 'skip'"
continue
;;
*)
echo "$account_input"
return 0
;;
esac
done
}
process_pdump_file(){
local pdump_file="$1"
local filename
filename=$(basename "$pdump_file")
local config_file="$IMPORT_DIR/configs/${filename%.*}.conf"
info "Processing: $filename"
# Parse configuration file if it exists
parse_config_file "$config_file"
# Determine account
local target_account="${CONFIG_ACCOUNT:-$DEFAULT_ACCOUNT}"
if [[ -z "$target_account" ]]; then
if ! target_account=$(prompt_for_account "$filename"); then
warn "Skipping $filename (no account provided)"
return 0
fi
fi
# Build command arguments
local cmd_args=(
--file "$pdump_file"
--account "$target_account"
--password "$MYSQL_PW"
--auth-db "$AUTH_DB"
--characters-db "$CHARACTERS_DB"
)
# Add optional parameters if specified in config
[[ -n "$CONFIG_NAME" ]] && cmd_args+=(--name "$CONFIG_NAME")
[[ -n "$CONFIG_GUID" ]] && cmd_args+=(--guid "$CONFIG_GUID")
log "Importing $filename to account $target_account"
[[ -n "$CONFIG_NAME" ]] && log " Character name: $CONFIG_NAME"
[[ -n "$CONFIG_GUID" ]] && log " Forced GUID: $CONFIG_GUID"
# Execute the import
if "./scripts/bash/pdump-import.sh" "${cmd_args[@]}"; then
log "✅ Successfully imported: $filename"
# Move processed file to processed/ subdirectory
local processed_dir="$IMPORT_DIR/processed"
mkdir -p "$processed_dir"
mv "$pdump_file" "$processed_dir/"
[[ -f "$config_file" ]] && mv "$config_file" "$processed_dir/"
else
err "❌ Failed to import: $filename"
return 1
fi
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--password)
[[ $# -ge 2 ]] || fatal "--password requires a value"
MYSQL_PW="$2"
shift 2
;;
--account)
[[ $# -ge 2 ]] || fatal "--account requires a value"
DEFAULT_ACCOUNT="$2"
shift 2
;;
--auth-db)
[[ $# -ge 2 ]] || fatal "--auth-db requires a value"
AUTH_DB="$2"
shift 2
;;
--characters-db)
[[ $# -ge 2 ]] || fatal "--characters-db requires a value"
CHARACTERS_DB="$2"
shift 2
;;
--non-interactive)
INTERACTIVE=false
shift
;;
-h|--help)
usage
exit 0
;;
*)
fatal "Unknown option: $1"
;;
esac
done
# Validate required parameters
[[ -n "$MYSQL_PW" ]] || fatal "MySQL password required (use --password or set MYSQL_ROOT_PASSWORD)"
# Check dependencies
check_dependencies
# Check if import directory exists and has files
if [[ ! -d "$IMPORT_DIR" ]]; then
info "Import directory doesn't exist: $IMPORT_DIR"
info "Create the directory and place your .pdump or .sql files there."
exit 0
fi
# Find pdump files
shopt -s nullglob
pdump_files=("$IMPORT_DIR"/*.pdump "$IMPORT_DIR"/*.sql)
shopt -u nullglob
if [[ ${#pdump_files[@]} -eq 0 ]]; then
info "No pdump files found in $IMPORT_DIR"
info "Place your .pdump or .sql files in this directory to import them."
exit 0
fi
log "Found ${#pdump_files[@]} pdump file(s) to process"
# Create configs directory if it doesn't exist
mkdir -p "$IMPORT_DIR/configs"
# Process each file
processed=0
failed=0
for pdump_file in "${pdump_files[@]}"; do
if process_pdump_file "$pdump_file"; then
((processed++))
else
((failed++))
fi
done
echo ""
log "Import summary:"
log " ✅ Processed: $processed"
[[ $failed -gt 0 ]] && err " ❌ Failed: $failed"
if [[ $processed -gt 0 ]]; then
log ""
log "Character imports completed! Processed files moved to $IMPORT_DIR/processed/"
log "You can now log in and access your imported characters."
fi

View File

@@ -174,7 +174,18 @@ install_enabled_modules(){
continue
fi
if [ -d "$dir/.git" ]; then
info "$dir already present; skipping clone"
info "$dir already present; checking for updates"
(cd "$dir" && git fetch origin >/dev/null 2>&1 || warn "Failed to fetch updates for $dir")
local current_branch
current_branch=$(cd "$dir" && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "master")
if (cd "$dir" && git pull origin "$current_branch" 2>&1 | grep -q "Already up to date"); then
info "$dir is already up to date"
else
ok "$dir updated from remote"
fi
if [ -n "$ref" ]; then
(cd "$dir" && git checkout "$ref") || warn "Unable to checkout ref $ref for $dir"
fi
elif [ -d "$dir" ]; then
warn "$dir exists but is not a git repository; leaving in place"
else
@@ -467,6 +478,7 @@ load_sql_helper(){
# Module SQL is now staged at runtime by stage-modules.sh which copies files to
# /azerothcore/data/sql/updates/ (core directory) where they ARE scanned and processed.
track_module_state(){
echo 'Checking for module changes that require rebuild...'

344
scripts/bash/pdump-import.sh Executable file
View File

@@ -0,0 +1,344 @@
#!/bin/bash
# Import character pdump files into AzerothCore database
set -euo pipefail
INVOCATION_DIR="$PWD"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
COLOR_RED='\033[0;31m'
COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[1;33m'
COLOR_BLUE='\033[0;34m'
COLOR_RESET='\033[0m'
log(){ printf '%b\n' "${COLOR_GREEN}$*${COLOR_RESET}"; }
warn(){ printf '%b\n' "${COLOR_YELLOW}$*${COLOR_RESET}"; }
err(){ printf '%b\n' "${COLOR_RED}$*${COLOR_RESET}"; }
info(){ printf '%b\n' "${COLOR_BLUE}$*${COLOR_RESET}"; }
fatal(){ err "$*"; exit 1; }
MYSQL_PW=""
PDUMP_FILE=""
TARGET_ACCOUNT=""
NEW_CHARACTER_NAME=""
FORCE_GUID=""
AUTH_DB="acore_auth"
CHARACTERS_DB="acore_characters"
DRY_RUN=false
BACKUP_BEFORE=true
usage(){
cat <<'EOF'
Usage: ./pdump-import.sh [options]
Import character pdump files into AzerothCore database.
Required Options:
-f, --file FILE Pdump file to import (.pdump or .sql format)
-a, --account ACCOUNT Target account name or ID for character import
-p, --password PASS MySQL root password
Optional:
-n, --name NAME New character name (if different from dump)
-g, --guid GUID Force specific character GUID
--auth-db NAME Auth database schema name (default: acore_auth)
--characters-db NAME Characters database schema name (default: acore_characters)
--dry-run Validate pdump without importing
--no-backup Skip pre-import backup (not recommended)
-h, --help Show this help and exit
Examples:
# Import character from pdump file
./pdump-import.sh --file character.pdump --account testaccount --password azerothcore123
# Import with new character name
./pdump-import.sh --file oldchar.pdump --account newaccount --name "NewCharName" --password azerothcore123
# Validate pdump file without importing
./pdump-import.sh --file character.pdump --account testaccount --password azerothcore123 --dry-run
Notes:
- Account must exist in the auth database before import
- Character names must be unique across the server
- Pre-import backup is created automatically (can be disabled with --no-backup)
- Use --dry-run to validate pdump structure before actual import
EOF
}
validate_account(){
local account="$1"
if [[ "$account" =~ ^[0-9]+$ ]]; then
# Account ID provided
local count
count=$(docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT COUNT(*) FROM ${AUTH_DB}.account WHERE id = $account;")
[[ "$count" -eq 1 ]] || fatal "Account ID $account not found in auth database"
else
# Account name provided
local count
count=$(docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT COUNT(*) FROM ${AUTH_DB}.account WHERE username = '$account';")
[[ "$count" -eq 1 ]] || fatal "Account '$account' not found in auth database"
fi
}
get_account_id(){
local account="$1"
if [[ "$account" =~ ^[0-9]+$ ]]; then
echo "$account"
else
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT id FROM ${AUTH_DB}.account WHERE username = '$account';"
fi
}
validate_character_name(){
local name="$1"
# Check character name format (WoW naming rules)
if [[ ! "$name" =~ ^[A-Za-z]{2,12}$ ]]; then
fatal "Invalid character name: '$name'. Must be 2-12 letters, no numbers or special characters."
fi
# Check if character name already exists
local count
count=$(docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT COUNT(*) FROM ${CHARACTERS_DB}.characters WHERE name = '$name';")
[[ "$count" -eq 0 ]] || fatal "Character name '$name' already exists in database"
}
get_next_guid(){
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT COALESCE(MAX(guid), 0) + 1 FROM ${CHARACTERS_DB}.characters;"
}
validate_pdump_format(){
local file="$1"
if [[ ! -f "$file" ]]; then
fatal "Pdump file not found: $file"
fi
# Check if file is readable and has SQL-like content
if ! head -10 "$file" | grep -q -i "INSERT\|UPDATE\|CREATE\|ALTER"; then
warn "File does not appear to contain SQL statements. Continuing anyway..."
fi
info "Pdump file validation: OK"
}
backup_characters(){
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="manual-backups/characters-pre-pdump-import-${timestamp}.sql"
mkdir -p manual-backups
log "Creating backup: $backup_file"
docker exec ac-mysql mysqldump -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" > "$backup_file"
echo "$backup_file"
}
process_pdump_sql(){
local file="$1"
local account_id="$2"
local new_guid="${3:-}"
local new_name="${4:-}"
# Create temporary processed file
local temp_file
temp_file=$(mktemp)
# Process the pdump SQL file
# Replace account references and optionally GUID/name
if [[ -n "$new_guid" && -n "$new_name" ]]; then
sed -e "s/\([^0-9]\)[0-9]\+\([^0-9].*account.*=\)/\1${account_id}\2/g" \
-e "s/\([^0-9]\)[0-9]\+\([^0-9].*guid.*=\)/\1${new_guid}\2/g" \
-e "s/'[^']*'\([^']*name.*=\)/'${new_name}'\1/g" \
"$file" > "$temp_file"
elif [[ -n "$new_guid" ]]; then
sed -e "s/\([^0-9]\)[0-9]\+\([^0-9].*account.*=\)/\1${account_id}\2/g" \
-e "s/\([^0-9]\)[0-9]\+\([^0-9].*guid.*=\)/\1${new_guid}\2/g" \
"$file" > "$temp_file"
elif [[ -n "$new_name" ]]; then
sed -e "s/\([^0-9]\)[0-9]\+\([^0-9].*account.*=\)/\1${account_id}\2/g" \
-e "s/'[^']*'\([^']*name.*=\)/'${new_name}'\1/g" \
"$file" > "$temp_file"
else
sed -e "s/\([^0-9]\)[0-9]\+\([^0-9].*account.*=\)/\1${account_id}\2/g" \
"$file" > "$temp_file"
fi
echo "$temp_file"
}
import_pdump(){
local processed_file="$1"
log "Importing character data into $CHARACTERS_DB database"
if docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" < "$processed_file"; then
log "Character import completed successfully"
else
fatal "Character import failed. Check MySQL logs for details."
fi
}
case "${1:-}" in
-h|--help) usage; exit 0;;
esac
# Parse command line arguments
POSITIONAL=()
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file)
[[ $# -ge 2 ]] || fatal "--file requires a file path"
PDUMP_FILE="$2"
shift 2
;;
-a|--account)
[[ $# -ge 2 ]] || fatal "--account requires an account name or ID"
TARGET_ACCOUNT="$2"
shift 2
;;
-p|--password)
[[ $# -ge 2 ]] || fatal "--password requires a value"
MYSQL_PW="$2"
shift 2
;;
-n|--name)
[[ $# -ge 2 ]] || fatal "--name requires a character name"
NEW_CHARACTER_NAME="$2"
shift 2
;;
-g|--guid)
[[ $# -ge 2 ]] || fatal "--guid requires a GUID number"
FORCE_GUID="$2"
shift 2
;;
--auth-db)
[[ $# -ge 2 ]] || fatal "--auth-db requires a value"
AUTH_DB="$2"
shift 2
;;
--characters-db)
[[ $# -ge 2 ]] || fatal "--characters-db requires a value"
CHARACTERS_DB="$2"
shift 2
;;
--dry-run)
DRY_RUN=true
shift
;;
--no-backup)
BACKUP_BEFORE=false
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
while [[ $# -gt 0 ]]; do
POSITIONAL+=("$1")
shift
done
break
;;
-*)
fatal "Unknown option: $1"
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
# Validate required arguments
[[ -n "$PDUMP_FILE" ]] || fatal "Pdump file is required. Use --file FILE"
[[ -n "$TARGET_ACCOUNT" ]] || fatal "Target account is required. Use --account ACCOUNT"
[[ -n "$MYSQL_PW" ]] || fatal "MySQL password is required. Use --password PASS"
# Resolve relative paths
if [[ ! "$PDUMP_FILE" =~ ^/ ]]; then
PDUMP_FILE="$INVOCATION_DIR/$PDUMP_FILE"
fi
# Validate inputs
log "Validating pdump file..."
validate_pdump_format "$PDUMP_FILE"
log "Validating target account..."
validate_account "$TARGET_ACCOUNT"
ACCOUNT_ID=$(get_account_id "$TARGET_ACCOUNT")
log "Target account ID: $ACCOUNT_ID"
if [[ -n "$NEW_CHARACTER_NAME" ]]; then
log "Validating new character name..."
validate_character_name "$NEW_CHARACTER_NAME"
fi
# Determine GUID
if [[ -n "$FORCE_GUID" ]]; then
CHARACTER_GUID="$FORCE_GUID"
log "Using forced GUID: $CHARACTER_GUID"
else
CHARACTER_GUID=$(get_next_guid)
log "Using next available GUID: $CHARACTER_GUID"
fi
# Process pdump file
log "Processing pdump file..."
PROCESSED_FILE=$(process_pdump_sql "$PDUMP_FILE" "$ACCOUNT_ID" "$CHARACTER_GUID" "$NEW_CHARACTER_NAME")
if $DRY_RUN; then
info "DRY RUN: Pdump processing completed successfully"
info "Processed file saved to: $PROCESSED_FILE"
info "Account ID: $ACCOUNT_ID"
info "Character GUID: $CHARACTER_GUID"
[[ -n "$NEW_CHARACTER_NAME" ]] && info "Character name: $NEW_CHARACTER_NAME"
info "Run without --dry-run to perform actual import"
rm -f "$PROCESSED_FILE"
exit 0
fi
# Create backup before import
BACKUP_FILE=""
if $BACKUP_BEFORE; then
BACKUP_FILE=$(backup_characters)
fi
# Stop world server to prevent issues during import
log "Stopping world server for safe import..."
docker stop ac-worldserver >/dev/null 2>&1 || warn "World server was not running"
# Perform import
trap 'rm -f "$PROCESSED_FILE"' EXIT
import_pdump "$PROCESSED_FILE"
# Restart world server
log "Restarting world server..."
docker start ac-worldserver >/dev/null 2>&1
# Wait for server to initialize
log "Waiting for world server to initialize..."
for i in {1..30}; do
if docker exec ac-worldserver pgrep worldserver >/dev/null 2>&1; then
log "World server is running"
break
fi
if [ $i -eq 30 ]; then
warn "World server took longer than expected to start"
fi
sleep 2
done
# Verify import
CHARACTER_COUNT=$(docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B -e \
"SELECT COUNT(*) FROM ${CHARACTERS_DB}.characters WHERE account = $ACCOUNT_ID;")
log "Import completed successfully!"
log "Characters on account $TARGET_ACCOUNT: $CHARACTER_COUNT"
[[ -n "$BACKUP_FILE" ]] && log "Backup created: $BACKUP_FILE"
info "Character import from pdump completed. You can now log in and play!"

65
scripts/bash/test-2fa-token.py Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
Test TOTP token generation for AzerothCore 2FA
"""
import base64
import hmac
import hashlib
import struct
import time
import argparse
def generate_totp(secret, timestamp=None, interval=30):
"""Generate TOTP token from Base32 secret"""
if timestamp is None:
timestamp = int(time.time())
# Calculate time counter
counter = timestamp // interval
# Decode Base32 secret
# Add padding if needed
secret = secret.upper()
missing_padding = len(secret) % 8
if missing_padding:
secret += '=' * (8 - missing_padding)
key = base64.b32decode(secret)
# Pack counter as big-endian 8-byte integer
counter_bytes = struct.pack('>Q', counter)
# Generate HMAC-SHA1 hash
hmac_hash = hmac.new(key, counter_bytes, hashlib.sha1).digest()
# Dynamic truncation
offset = hmac_hash[-1] & 0xf
code = struct.unpack('>I', hmac_hash[offset:offset + 4])[0]
code &= 0x7fffffff
code %= 1000000
return f"{code:06d}"
def main():
parser = argparse.ArgumentParser(description="Generate TOTP tokens for testing")
parser.add_argument('-s', '--secret', required=True, help='Base32 secret')
parser.add_argument('-t', '--time', type=int, help='Unix timestamp (default: current time)')
parser.add_argument('-c', '--count', type=int, default=1, help='Number of tokens to generate')
args = parser.parse_args()
timestamp = args.time or int(time.time())
print(f"Secret: {args.secret}")
print(f"Timestamp: {timestamp} ({time.ctime(timestamp)})")
print(f"Interval: 30 seconds")
print()
for i in range(args.count):
current_time = timestamp + (i * 30)
token = generate_totp(args.secret, current_time)
print(f"Time: {time.ctime(current_time)} | Token: {token}")
if __name__ == "__main__":
main()

View File

@@ -1,5 +1,6 @@
#!/bin/bash
# Module-specific hook for mod-ale compatibility patches
# NOTE: These patches are primarily needed for the AzerothCore playerbots fork
set -e
# Hook environment
@@ -7,12 +8,34 @@ MODULE_KEY="${MODULE_KEY:-}"
MODULE_DIR="${MODULE_DIR:-}"
MODULE_NAME="${MODULE_NAME:-}"
# Detect if we're building with playerbots fork
IS_PLAYERBOTS_FORK=0
if [ -n "$MODULE_DIR" ]; then
# Check if the parent directory structure indicates playerbots fork
if echo "$MODULE_DIR" | grep -q "azerothcore-playerbots"; then
IS_PLAYERBOTS_FORK=1
fi
fi
# Feature flags (set to 0 to disable specific patches)
APPLY_MOVEPATH_PATCH="${APPLY_MOVEPATH_PATCH:-0}" # Disabled by default - appears unnecessary
# SendTrainerList patch: auto-detect based on fork, but can be overridden
if [ -z "${APPLY_SENDTRAINERLIST_PATCH:-}" ]; then
APPLY_SENDTRAINERLIST_PATCH="$IS_PLAYERBOTS_FORK" # Only needed for playerbots fork
else
APPLY_SENDTRAINERLIST_PATCH="${APPLY_SENDTRAINERLIST_PATCH}"
fi
if [ -z "$MODULE_DIR" ] || [ ! -d "$MODULE_DIR" ]; then
echo "❌ mod-ale-patches: Invalid module directory: $MODULE_DIR"
exit 2
fi
echo "🔧 mod-ale-patches: Applying compatibility fixes to $MODULE_NAME"
if [ "$IS_PLAYERBOTS_FORK" = "1" ]; then
echo "🔧 mod-ale-patches: Applying playerbots fork compatibility fixes to $MODULE_NAME"
else
echo "🔧 mod-ale-patches: Checking compatibility fixes for $MODULE_NAME"
fi
# Apply MovePath compatibility patch
apply_movepath_patch() {
@@ -37,10 +60,42 @@ apply_movepath_patch() {
fi
}
# Apply SendTrainerList compatibility patch
apply_sendtrainerlist_patch() {
local target_file="$MODULE_DIR/src/LuaEngine/methods/PlayerMethods.h"
if [ ! -f "$target_file" ]; then
echo " ⚠️ SendTrainerList patch target file missing: $target_file"
return 1
fi
# Check if the buggy code exists (without GET_GUID())
if grep -q 'player->GetSession()->SendTrainerList(obj);' "$target_file"; then
# Apply the fix by adding ->GET_GUID()
if sed -i 's/player->GetSession()->SendTrainerList(obj);/player->GetSession()->SendTrainerList(obj->GET_GUID());/' "$target_file"; then
echo " ✅ Applied SendTrainerList compatibility fix"
return 0
else
echo " ❌ Failed to apply SendTrainerList compatibility fix"
return 2
fi
else
echo " ✅ SendTrainerList compatibility fix already present"
return 0
fi
}
# Apply all patches
patch_count=0
if apply_movepath_patch; then
patch_count=$((patch_count + 1))
if [ "$APPLY_MOVEPATH_PATCH" = "1" ]; then
if apply_movepath_patch; then
patch_count=$((patch_count + 1))
fi
fi
if [ "$APPLY_SENDTRAINERLIST_PATCH" = "1" ]; then
if apply_sendtrainerlist_patch; then
patch_count=$((patch_count + 1))
fi
fi
if [ $patch_count -eq 0 ]; then

View File

@@ -371,12 +371,7 @@ def build_state(env_path: Path, manifest_path: Path) -> ModuleCollectionState:
for unknown_key in extra_env_modules:
warnings.append(f".env defines {unknown_key} but it is missing from the manifest")
# Warn if manifest entry lacks .env toggle
for module in modules:
if module.key not in env_map and module.key not in os.environ:
warnings.append(
f"Manifest includes {module.key} but .env does not define it (defaulting to 0)"
)
# Skip warnings for missing modules - they default to disabled (0) as intended
return ModuleCollectionState(
manifest_path=manifest_path,

View File

@@ -50,6 +50,9 @@ def clean(value: str) -> str:
def cmd_keys(manifest_path: str) -> None:
manifest = load_manifest(manifest_path)
for entry in iter_modules(manifest):
# Skip blocked modules
if entry.get("status") == "blocked":
continue
print(entry["key"])
@@ -96,7 +99,7 @@ def cmd_metadata(manifest_path: str) -> None:
def cmd_sorted_keys(manifest_path: str) -> None:
manifest = load_manifest(manifest_path)
modules = list(iter_modules(manifest))
modules = [entry for entry in iter_modules(manifest) if entry.get("status") != "blocked"]
modules.sort(
key=lambda item: (
# Primary sort by order (default to 5000 if not specified)

View File

@@ -28,8 +28,9 @@ def normalize_modules(raw_modules: Iterable[str], profile: Path) -> List[str]:
if not value:
continue
modules.append(value)
if not modules:
raise ValueError(f"Profile {profile.name}: modules list cannot be empty")
# Allow empty modules list for vanilla/minimal profiles
if not modules and "vanilla" not in profile.stem.lower() and "minimal" not in profile.stem.lower():
raise ValueError(f"Profile {profile.name}: modules list cannot be empty (except for vanilla/minimal profiles)")
return modules
@@ -79,7 +80,7 @@ def cmd_list(directory: Path) -> int:
profiles.sort(key=lambda item: item[4])
for name, modules, label, description, order in profiles:
modules_csv = ",".join(modules)
modules_csv = ",".join(modules) if modules else "-"
print("\t".join([name, modules_csv, label, description, str(order)]))
return 0

View File

@@ -18,6 +18,7 @@ import re
import sys
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Sequence
from urllib import error, parse, request
@@ -87,6 +88,16 @@ def parse_args(argv: Sequence[str]) -> argparse.Namespace:
action="store_true",
help="Print verbose progress information",
)
parser.add_argument(
"--update-template",
default=".env.template",
help="Update .env.template with missing module variables (default: %(default)s)",
)
parser.add_argument(
"--skip-template",
action="store_true",
help="Skip updating .env.template",
)
return parser.parse_args(argv)
@@ -273,6 +284,117 @@ def collect_repositories(
return list(seen.values())
def update_env_template(manifest_path: str, template_path: str) -> bool:
"""Update .env.template with module variables for active modules only.
Args:
manifest_path: Path to the module manifest JSON file
template_path: Path to .env.template file
Returns:
True if template was updated, False if no changes needed
"""
# Load manifest to get all module keys
manifest = load_manifest(manifest_path)
modules = manifest.get("modules", [])
if not modules:
return False
# Extract only active module keys
active_module_keys = set()
disabled_module_keys = set()
for module in modules:
key = module.get("key")
status = module.get("status", "active")
if key:
if status == "active":
active_module_keys.add(key)
else:
disabled_module_keys.add(key)
if not active_module_keys and not disabled_module_keys:
return False
# Check if template file exists
template_file = Path(template_path)
if not template_file.exists():
print(f"Warning: .env.template not found at {template_path}")
return False
# Read current template content
try:
current_content = template_file.read_text(encoding="utf-8")
current_lines = current_content.splitlines()
except Exception as exc:
print(f"Error reading .env.template: {exc}")
return False
# Find which module variables are currently in the template
existing_vars = set()
current_module_lines = []
non_module_lines = []
for line in current_lines:
stripped = line.strip()
if "=" in stripped and not stripped.startswith("#"):
var_name = stripped.split("=", 1)[0].strip()
if var_name.startswith("MODULE_"):
existing_vars.add(var_name)
current_module_lines.append((var_name, line))
else:
non_module_lines.append(line)
else:
non_module_lines.append(line)
# Determine what needs to change
missing_vars = active_module_keys - existing_vars
vars_to_remove = disabled_module_keys & existing_vars
vars_to_keep = active_module_keys & existing_vars
changes_made = False
# Report what will be done
if missing_vars:
print(f"📝 Adding {len(missing_vars)} active module variable(s) to .env.template:")
for var in sorted(missing_vars):
print(f" + {var}=0")
changes_made = True
if vars_to_remove:
print(f"🗑️ Removing {len(vars_to_remove)} disabled module variable(s) from .env.template:")
for var in sorted(vars_to_remove):
print(f" - {var}")
changes_made = True
if not changes_made:
print("✅ .env.template is up to date with active modules")
return False
# Build new content: non-module lines + active module lines
new_lines = non_module_lines[:]
# Add existing active module variables (preserve their current values)
for var_name, original_line in current_module_lines:
if var_name in vars_to_keep:
new_lines.append(original_line)
# Add new active module variables
for var in sorted(missing_vars):
new_lines.append(f"{var}=0")
# Write updated content
try:
new_content = "\n".join(new_lines) + "\n"
template_file.write_text(new_content, encoding="utf-8")
print("✅ .env.template updated successfully")
print(f" Active modules: {len(active_module_keys)}")
print(f" Disabled modules removed: {len(vars_to_remove)}")
return True
except Exception as exc:
print(f"Error writing .env.template: {exc}")
return False
def main(argv: Sequence[str]) -> int:
args = parse_args(argv)
topics = args.topics or DEFAULT_TOPICS
@@ -291,6 +413,13 @@ def main(argv: Sequence[str]) -> int:
handle.write("\n")
print(f"Updated manifest {args.manifest}: added {added}, refreshed {updated}")
# Update .env.template if requested (always run to clean up disabled modules)
if not args.skip_template:
template_updated = update_env_template(args.manifest, args.update_template)
if template_updated:
print(f"Updated {args.update_template} with active modules only")
return 0

264
setup.sh
View File

@@ -3,9 +3,9 @@ set -e
clear
# ==============================================
# azerothcore-rm - Interactive .env generator
# AzerothCore-RealmMaster - Interactive .env generator
# ==============================================
# Mirrors options from scripts/setup-server.sh but targets azerothcore-rm/.env
# Mirrors options from scripts/setup-server.sh but targets .env
# Get script directory for template reading
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -16,6 +16,12 @@ TEMPLATE_FILE="$SCRIPT_DIR/.env.template"
source "$SCRIPT_DIR/scripts/bash/project_name.sh"
DEFAULT_PROJECT_NAME="$(project_name::resolve "$ENV_FILE" "$TEMPLATE_FILE")"
# ==============================================
# Feature Flags
# ==============================================
# Set to 0 to disable server configuration preset selection
ENABLE_CONFIG_PRESETS="${ENABLE_CONFIG_PRESETS:-0}"
# ==============================================
# Constants (auto-loaded from .env.template)
# ==============================================
@@ -331,25 +337,57 @@ show_wow_header() {
echo -e "${RED}"
cat <<'EOF'
:::. :::::::::.,:::::: :::::::.. ... :::::::::::: :: .: .,-::::: ... :::::::.. .,::::::
;;`;; '`````;;;;;;;'''' ;;;;``;;;; .;;;;;;;.;;;;;;;;'''',;; ;;, ,;;;'````' .;;;;;;;. ;;;;``;;;; ;;;;''''
,[[ '[[, .n[[' [[cccc [[[,/[[[' ,[[ \[[, [[ ,[[[,,,[[[ [[[ ,[[ \[[,[[[,/[[[' [[cccc
c$$$cc$$$c ,$$P" $$"""" $$$$$$c $$$, $$$ $$ "$$$"""$$$ $$$ $$$, $$$$$$$$$c $$""""
888 888,,888bo,_ 888oo,__ 888b "88bo,"888,_ _,88P 88, 888 "88o`88bo,__,o,"888,_ _,88P888b "88bo,888oo,__
YMM ""` `""*UMM """"YUMMMMMMM "W" "YMMMMMP" MMM MMM YMM "YUMMMMMP" "YMMMMMP" MMMM "W" """"\MMM
___ ___ ___ ___ ___ ___ ___
.'`~ ``. .'`~ ``. .'`~ ``. .'`~ ``. .'`~ ``. .'`~ ``. .'`~ ``.
)`_ ._ ( )`_ ._ ( )`_ ._ ( )`_ ._ ( )`_ ._ ( )`_ ._ ( )`_ ._ (
|(_/^\_)| |(_/^\_)| |(_/^\_)| |(_/^\_)| |(_/^\_)| |(_/^\_)| |(_/^\_)|
`-.`''.-' `-.`''.-' `-.`''.-' `-.`''.-' `-.`''.-' `-.`''.-' `-.`''.-'
""" """ """ """ """ """ """
.')'=.'_`.='(`. .')'=.'_`.='(`. .')'=.'_`.='(`. .')'=.'_`.='(`. .')'=.'_`.='(`. .')'=.'_`.='(`. .')'=.'_`.='(`.
:| -.._H_,.- |: :| -.._H_,.- |: :| -.._H_,.- |: :| -.._H_,.- |: :| -.._H_,.- |: :| -.._H_,.- |: :| -.._H_,.- |:
|: -.__H__.- :| |: -.__H__.- :| |: -.__H__.- :| |: -.__H__.- :| |: -.__H__.- :| |: -.__H__.- :| |: -.__H__.- :|
<' `--V--' `> <' `--V--' `> <' `--V--' `> <' `--V--' `> <' `--V--' `> <' `--V--' `> <' `--V--' `>
art: littlebitspace@https://littlebitspace.com/
##
### :*
##### .**#
###### ***##
****###* *****##.
******##- ******###.
.*********###= ********###
************##### #****###:+* ********####
***********+****##########**********##**# ********#####
********=+***********######**********######*#**+*******###+
-+*****=**************#######*******####**#####**##*****####-
++**++****************#########**####***####***#####****####:
:++*******************#*******####*****#****######***##*****#######
*= -++++++******************************###**********###******######
.+***. :++++++++***************************#+*#*-*******************#**+
++*****= =+++=+++***************************+**###**************++*#####*
-++*****+++- -=++++++++*********+++++**###**+++=+*###**+*********##+++*+++##
+++*********+++=-=+++++++++****+++***+++++*####***+++**=**#*==***#####*++***+*+
+++++***********++=-=++++++++*++****=++*++*#######**+=-=+****+*#########***==+*#*
=+++++++*****++++===-++++++++=+++++=++*+=-+#**#**=####****#**+-+**************##*
++++++++++++++======++++++++=====+++++=-+++*+##########*****==*######*****####
+++++++=++++++====++++++++++========---++++*****#######**==***#*******####*
++===++++++++=====+++++++=+++:::--:::.++++++*****####**+=**************#
=+++++=: =+=====-+++++++++++++++++++++==+++--==----:-++++++****####****+=+*+*******:
++++++++++++++++==+++++++++++++++++++++=+=-===-----:+++++++++**+++****####***+++
=++++++++++++++++++++++++++++++++++++=++++======----==+++++++=+************:
:++++++++++++++=+++++++++++++++++++======-------:-====+****************.
=----=+++-==++++++*******++++++++++++++===============****************=
-=---==-=====--+++++++++++++++++++++++++++===+++++++********++#***#++******
+++++========+=====----++++++++++++++++===+++++===--=**********+=++*++********
+++==========-=============-----:-=++=====+++++++++++++++=-=***********+*********
==----=+===+=================+++++++++++++++++++++++++=-********************
.======++++++===============---:::::==++++++++++++++++++++++=**********++*******:
+++==--::-=+++++++++++++========+===--=+- :::=-=++++++++++++++++++++++ +*****++**+***
.-----::::-=++++++++++++++++++==::-----++. :=+++++++++++++++++++*..-+*********=
:=+++++++++++++++++==.:--===-+++++++++++**++++++:::-********
++++++++++++++++++=+++++++++++++**+++++*****==******
.++++++++++++=-:.-+++++++++***++++************+
+++=========:.=+=-::++*****+*************
-++++++++==+: ..::=-. ..::::=********
.+========+==+++==========---::-+*-
++++++++++++=======-======
++++++++++++++======++
-=======++++++:
...
:::. :::::::::.,:::::: :::::::.. ... :::::::::::: :: .: .,-::::: ... :::::::.. .,::::::
;;`;; '`````;;;;;;;'''' ;;;;``;;;; .;;;;;;;.;;;;;;;;'''',;; ;;, ,;;;'````' .;;;;;;;. ;;;;``;;;; ;;;;''''
,[[ '[[, .n[[' [[cccc [[[,/[[[' ,[[ \[[, [[ ,[[[,,,[[[ [[[ ,[[ \[[,[[[,/[[[' [[cccc
c$$$cc$$$c ,$$P" $$"""" $$$$$$c $$$, $$$ $$ "$$$"""$$$ $$$ $$$, $$$$$$$$$c $$""""
888 888,,888bo,_ 888oo,__ 888b "88bo,"888,_ _,88P 88, 888 "88o`88bo,__,o,"888,_ _,88P888b "88bo,888oo,__
YMM ""` `""*UMM """"YUMMMMMMM "W" "YMMMMMP" MMM MMM YMM "YUMMMMMP" "YMMMMMP" MMMM "W" """"\MMM
EOF
echo -e "${NC}"
}
@@ -591,7 +629,7 @@ main(){
Usage: ./setup.sh [options]
Description:
Interactive wizard that generates azerothcore-rm/.env for the
Interactive wizard that generates .env for the
profiles-based compose. Prompts for deployment type, ports, storage,
MySQL credentials, backup retention, and module presets or manual
toggles.
@@ -809,7 +847,7 @@ EOF
fi
show_wow_header
say INFO "This will create azerothcore-rm/.env for compose profiles."
say INFO "This will create .env for compose profiles."
# Deployment type
say HEADER "DEPLOYMENT TYPE"
@@ -963,58 +1001,65 @@ fi
BACKUP_DAILY_TIME=$(ask "Daily backup hour (00-23, UTC)" "${CLI_BACKUP_TIME:-$DEFAULT_BACKUP_TIME}" validate_number)
# Server configuration
say HEADER "SERVER CONFIGURATION PRESET"
local SERVER_CONFIG_PRESET
if [ -n "$CLI_CONFIG_PRESET" ]; then
SERVER_CONFIG_PRESET="$CLI_CONFIG_PRESET"
say INFO "Using preset from command line: $SERVER_CONFIG_PRESET"
if [ "$ENABLE_CONFIG_PRESETS" = "1" ]; then
say HEADER "SERVER CONFIGURATION PRESET"
if [ -n "$CLI_CONFIG_PRESET" ]; then
SERVER_CONFIG_PRESET="$CLI_CONFIG_PRESET"
say INFO "Using preset from command line: $SERVER_CONFIG_PRESET"
else
declare -A CONFIG_PRESET_NAMES=()
declare -A CONFIG_PRESET_DESCRIPTIONS=()
declare -A CONFIG_MENU_INDEX=()
local config_dir="$SCRIPT_DIR/config/presets"
local menu_index=1
echo "Choose a server configuration preset:"
if [ -x "$SCRIPT_DIR/scripts/python/parse-config-presets.py" ] && [ -d "$config_dir" ]; then
while IFS=$'\t' read -r preset_key preset_name preset_desc; do
[ -n "$preset_key" ] || continue
CONFIG_PRESET_NAMES["$preset_key"]="$preset_name"
CONFIG_PRESET_DESCRIPTIONS["$preset_key"]="$preset_desc"
CONFIG_MENU_INDEX[$menu_index]="$preset_key"
echo "$menu_index) $preset_name"
echo " $preset_desc"
menu_index=$((menu_index + 1))
done < <(python3 "$SCRIPT_DIR/scripts/python/parse-config-presets.py" list --presets-dir "$config_dir")
else
# Fallback if parser script not available
CONFIG_MENU_INDEX[1]="none"
CONFIG_PRESET_NAMES["none"]="Default (No Preset)"
CONFIG_PRESET_DESCRIPTIONS["none"]="Use default AzerothCore settings"
echo "1) Default (No Preset)"
echo " Use default AzerothCore settings without any modifications"
fi
local max_config_option=$((menu_index - 1))
if [ "$NON_INTERACTIVE" = "1" ]; then
SERVER_CONFIG_PRESET="none"
say INFO "Non-interactive mode: Using default configuration preset"
else
while true; do
read -p "$(echo -e "${YELLOW}🎯 Select server configuration [1-$max_config_option]: ${NC}")" choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$max_config_option" ]; then
SERVER_CONFIG_PRESET="${CONFIG_MENU_INDEX[$choice]}"
local chosen_name="${CONFIG_PRESET_NAMES[$SERVER_CONFIG_PRESET]}"
say INFO "Selected: $chosen_name"
break
else
say ERROR "Please select a number between 1 and $max_config_option"
fi
done
fi
fi
else
declare -A CONFIG_PRESET_NAMES=()
declare -A CONFIG_PRESET_DESCRIPTIONS=()
declare -A CONFIG_MENU_INDEX=()
local config_dir="$SCRIPT_DIR/config/presets"
local menu_index=1
echo "Choose a server configuration preset:"
if [ -x "$SCRIPT_DIR/scripts/python/parse-config-presets.py" ] && [ -d "$config_dir" ]; then
while IFS=$'\t' read -r preset_key preset_name preset_desc; do
[ -n "$preset_key" ] || continue
CONFIG_PRESET_NAMES["$preset_key"]="$preset_name"
CONFIG_PRESET_DESCRIPTIONS["$preset_key"]="$preset_desc"
CONFIG_MENU_INDEX[$menu_index]="$preset_key"
echo "$menu_index) $preset_name"
echo " $preset_desc"
menu_index=$((menu_index + 1))
done < <(python3 "$SCRIPT_DIR/scripts/python/parse-config-presets.py" list --presets-dir "$config_dir")
else
# Fallback if parser script not available
CONFIG_MENU_INDEX[1]="none"
CONFIG_PRESET_NAMES["none"]="Default (No Preset)"
CONFIG_PRESET_DESCRIPTIONS["none"]="Use default AzerothCore settings"
echo "1) Default (No Preset)"
echo " Use default AzerothCore settings without any modifications"
fi
local max_config_option=$((menu_index - 1))
if [ "$NON_INTERACTIVE" = "1" ]; then
SERVER_CONFIG_PRESET="none"
say INFO "Non-interactive mode: Using default configuration preset"
else
while true; do
read -p "$(echo -e "${YELLOW}🎯 Select server configuration [1-$max_config_option]: ${NC}")" choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$max_config_option" ]; then
SERVER_CONFIG_PRESET="${CONFIG_MENU_INDEX[$choice]}"
local chosen_name="${CONFIG_PRESET_NAMES[$SERVER_CONFIG_PRESET]}"
say INFO "Selected: $chosen_name"
break
else
say ERROR "Please select a number between 1 and $max_config_option"
fi
done
fi
# Config presets disabled - use default
SERVER_CONFIG_PRESET="none"
say INFO "Server configuration presets disabled - using default settings"
fi
local MODE_SELECTION=""
@@ -1094,12 +1139,29 @@ fi
MODE_PRESET_NAME="$CLI_MODULE_PRESET"
fi
# Function to determine source branch for a preset
get_preset_source_branch() {
local preset_name="$1"
local preset_modules="${MODULE_PRESET_CONFIGS[$preset_name]:-}"
# Check if playerbots module is in the preset
if [[ "$preset_modules" == *"MODULE_PLAYERBOTS"* ]]; then
echo "azerothcore-playerbots"
else
echo "azerothcore-wotlk"
fi
}
# Module config
say HEADER "MODULE PRESET"
echo "1) ${MODULE_PRESET_LABELS[$DEFAULT_PRESET_SUGGESTED]:-⭐ Suggested Modules}"
echo "2) ${MODULE_PRESET_LABELS[$DEFAULT_PRESET_PLAYERBOTS]:-🤖 Playerbots + Suggested modules}"
echo "3) ⚙️ Manual selection"
echo "4) 🚫 No modules"
printf " %s) %s\n" "1" "⭐ Suggested Modules"
printf " %s (%s)\n" "Baseline solo-friendly quality of life mix" "azerothcore-wotlk"
printf " %s) %s\n" "2" "🤖 Playerbots + Suggested modules"
printf " %s (%s)\n" "Suggested stack plus playerbots enabled" "azerothcore-playerbots"
printf " %s) %s\n" "3" "⚙️ Manual selection"
printf " %s (%s)\n" "Choose individual modules manually" "(depends on modules)"
printf " %s) %s\n" "4" "🚫 No modules"
printf " %s (%s)\n" "Pure AzerothCore with no modules" "azerothcore-wotlk"
local menu_index=5
declare -A MENU_PRESET_INDEX=()
@@ -1118,13 +1180,16 @@ fi
for entry in "${ORDERED_PRESETS[@]}"; do
local preset_name="${entry#*::}"
[ -n "${MODULE_PRESET_CONFIGS[$preset_name]:-}" ] || continue
local pretty_name
local pretty_name preset_desc
if [ -n "${MODULE_PRESET_LABELS[$preset_name]:-}" ]; then
pretty_name="${MODULE_PRESET_LABELS[$preset_name]}"
else
pretty_name=$(echo "$preset_name" | tr '_-' ' ' | awk '{for(i=1;i<=NF;i++){$i=toupper(substr($i,1,1)) substr($i,2)}}1')
fi
echo "${menu_index}) ${pretty_name} (config/module-profiles/${preset_name}.json)"
preset_desc="${MODULE_PRESET_DESCRIPTIONS[$preset_name]:-No description available}"
local source_branch=$(get_preset_source_branch "$preset_name")
printf " %s) %s\n" "$menu_index" "$pretty_name"
printf " %s (%s)\n" "$preset_desc" "$source_branch"
MENU_PRESET_INDEX[$menu_index]="$preset_name"
menu_index=$((menu_index + 1))
done
@@ -1522,7 +1587,7 @@ fi
fi
local default_source_rel="${LOCAL_STORAGE_ROOT}/source/azerothcore"
if [ "$NEEDS_CXX_REBUILD" = "1" ] || [ "$MODULE_PLAYERBOTS" = "1" ]; then
if [ "$MODULE_PLAYERBOTS" = "1" ]; then
default_source_rel="${LOCAL_STORAGE_ROOT}/source/azerothcore-playerbots"
fi
@@ -1604,7 +1669,7 @@ fi
{
cat <<EOF
# Generated by azerothcore-rm/setup.sh
# Generated by setup.sh
# Compose overrides (set to 1 to include matching file under compose-overrides/)
# mysql-expose.yml -> exposes MySQL externally via COMPOSE_OVERRIDE_MYSQL_EXPOSE_ENABLED
@@ -1616,6 +1681,15 @@ COMPOSE_PROJECT_NAME=$DEFAULT_COMPOSE_PROJECT_NAME
STORAGE_PATH=$STORAGE_PATH
STORAGE_PATH_LOCAL=$LOCAL_STORAGE_ROOT
STORAGE_CONFIG_PATH=$(get_template_value "STORAGE_CONFIG_PATH")
STORAGE_LOGS_PATH=$(get_template_value "STORAGE_LOGS_PATH")
STORAGE_MODULES_PATH=$(get_template_value "STORAGE_MODULES_PATH")
STORAGE_LUA_SCRIPTS_PATH=$(get_template_value "STORAGE_LUA_SCRIPTS_PATH")
STORAGE_MODULES_META_PATH=$(get_template_value "STORAGE_MODULES_META_PATH")
STORAGE_MODULE_SQL_PATH=$(get_template_value "STORAGE_MODULE_SQL_PATH")
STORAGE_INSTALL_MARKERS_PATH=$(get_template_value "STORAGE_INSTALL_MARKERS_PATH")
STORAGE_CLIENT_DATA_PATH=$(get_template_value "STORAGE_CLIENT_DATA_PATH")
STORAGE_LOCAL_SOURCE_PATH=$(get_template_value "STORAGE_LOCAL_SOURCE_PATH")
BACKUP_PATH=$BACKUP_PATH
TZ=$DEFAULT_TZ
@@ -1684,10 +1758,31 @@ CONTAINER_USER=$CONTAINER_USER
CONTAINER_MYSQL=$DEFAULT_CONTAINER_MYSQL
CONTAINER_DB_IMPORT=$DEFAULT_CONTAINER_DB_IMPORT
CONTAINER_DB_INIT=$DEFAULT_CONTAINER_DB_INIT
CONTAINER_DB_GUARD=$(get_template_value "CONTAINER_DB_GUARD")
CONTAINER_BACKUP=$DEFAULT_CONTAINER_BACKUP
CONTAINER_MODULES=$DEFAULT_CONTAINER_MODULES
CONTAINER_POST_INSTALL=$DEFAULT_CONTAINER_POST_INSTALL
# Database Guard Defaults
DB_GUARD_RECHECK_SECONDS=$(get_template_value "DB_GUARD_RECHECK_SECONDS")
DB_GUARD_RETRY_SECONDS=$(get_template_value "DB_GUARD_RETRY_SECONDS")
DB_GUARD_WAIT_ATTEMPTS=$(get_template_value "DB_GUARD_WAIT_ATTEMPTS")
DB_GUARD_HEALTH_MAX_AGE=$(get_template_value "DB_GUARD_HEALTH_MAX_AGE")
DB_GUARD_HEALTHCHECK_INTERVAL=$(get_template_value "DB_GUARD_HEALTHCHECK_INTERVAL")
DB_GUARD_HEALTHCHECK_TIMEOUT=$(get_template_value "DB_GUARD_HEALTHCHECK_TIMEOUT")
DB_GUARD_HEALTHCHECK_RETRIES=$(get_template_value "DB_GUARD_HEALTHCHECK_RETRIES")
DB_GUARD_VERIFY_INTERVAL_SECONDS=$(get_template_value "DB_GUARD_VERIFY_INTERVAL_SECONDS")
# Module SQL staging
STAGE_PATH_MODULE_SQL=$(get_template_value "STAGE_PATH_MODULE_SQL")
# Modules rebuild source path
MODULES_REBUILD_SOURCE_PATH=$MODULES_REBUILD_SOURCE_PATH
# SQL Source Overlay
SOURCE_DIR=$(get_template_value "SOURCE_DIR")
AC_SQL_SOURCE_PATH=$(get_template_value "AC_SQL_SOURCE_PATH")
# Ports
AUTH_EXTERNAL_PORT=$AUTH_EXTERNAL_PORT
AUTH_PORT=$DEFAULT_AUTH_INTERNAL_PORT
@@ -1704,6 +1799,8 @@ REALM_PORT=$REALM_PORT
BACKUP_RETENTION_DAYS=$BACKUP_RETENTION_DAYS
BACKUP_RETENTION_HOURS=$BACKUP_RETENTION_HOURS
BACKUP_DAILY_TIME=$BACKUP_DAILY_TIME
BACKUP_INTERVAL_MINUTES=$(get_template_value "BACKUP_INTERVAL_MINUTES")
BACKUP_EXTRA_DATABASES=$(get_template_value "BACKUP_EXTRA_DATABASES")
BACKUP_HEALTHCHECK_MAX_MINUTES=$BACKUP_HEALTHCHECK_MAX_MINUTES
BACKUP_HEALTHCHECK_GRACE_SECONDS=$BACKUP_HEALTHCHECK_GRACE_SECONDS
@@ -1711,10 +1808,13 @@ EOF
echo
echo "# Modules"
for module_key in "${MODULE_KEYS[@]}"; do
printf "%s=%s\n" "$module_key" "${!module_key:-0}"
local module_value="${!module_key:-0}"
# Only write enabled modules (value=1) to .env
if [ "$module_value" = "1" ]; then
printf "%s=%s\n" "$module_key" "$module_value"
fi
done
cat <<EOF
MODULES_REBUILD_SOURCE_PATH=$MODULES_REBUILD_SOURCE_PATH
# Client data
CLIENT_DATA_VERSION=${CLIENT_DATA_VERSION:-$DEFAULT_CLIENT_DATA_VERSION}