mirror of
https://github.com/uprightbass360/AzerothCore-RealmMaster.git
synced 2026-01-13 00:58:34 +00:00
cleanup: validation and integrations for importing data
This commit is contained in:
257
.env.template
257
.env.template
@@ -441,3 +441,260 @@ PMA_MAX_EXECUTION_TIME=600
|
||||
KEIRA3_EXTERNAL_PORT=4201
|
||||
KEIRA_DATABASE_HOST=ac-mysql
|
||||
KEIRA_DATABASE_PORT=3306
|
||||
|
||||
# Auto-generated defaults for new modules
|
||||
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
|
||||
MODULE_AZEROTHCORE_REGISTRATION_PAGE=0
|
||||
MODULE_AZEROTHCORE_SERVER_MANAGER=0
|
||||
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
|
||||
MODULE_MOD_AUTOFISH=0
|
||||
MODULE_MOD_AUTO_RESURRECT=0
|
||||
MODULE_MOD_BG_BATTLE_FOR_GILNEAS=0
|
||||
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
|
||||
MODULE_MOD_HARD_MODES=0
|
||||
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
|
||||
MODULE_MOD_NPC_GAMBLER=0
|
||||
MODULE_MOD_NPC_MORPH=0
|
||||
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
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -11,10 +11,12 @@ local-storage/
|
||||
images/
|
||||
node_modules/
|
||||
.mcp*/
|
||||
scripts/__pycache__/
|
||||
scripts/__pycache__/*
|
||||
scripts/python/__pycache__/*
|
||||
.env
|
||||
package-lock.json
|
||||
package.json
|
||||
todo.md
|
||||
.gocache/
|
||||
.module-ledger/
|
||||
deploy.log
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +1,345 @@
|
||||
{
|
||||
"modules": [
|
||||
"MODULE_1V1_ARENA",
|
||||
"MODULE_ACCOUNTWIDE_SYSTEMS",
|
||||
"MODULE_ACCOUNT_ACHIEVEMENTS",
|
||||
"MODULE_ACCOUNT_MOUNTS",
|
||||
"MODULE_ACORE_SUBSCRIPTIONS",
|
||||
"MODULE_ACTIVE_CHAT",
|
||||
"MODULE_AHBOT",
|
||||
"MODULE_AIO",
|
||||
"MODULE_AIO_BLACKJACK",
|
||||
"MODULE_ANTIFARMING",
|
||||
"MODULE_PLAYERBOTS",
|
||||
"MODULE_AOE_LOOT",
|
||||
"MODULE_ARAC",
|
||||
"MODULE_ARENA_REPLAY",
|
||||
"MODULE_ASSISTANT",
|
||||
"MODULE_AUTOBALANCE",
|
||||
"MODULE_AUTO_REVIVE",
|
||||
"MODULE_AZEROTHSHARD",
|
||||
"MODULE_BG_SLAVERYVALLEY",
|
||||
"MODULE_BLACK_MARKET_AUCTION_HOUSE",
|
||||
"MODULE_BOSS_ANNOUNCER",
|
||||
"MODULE_BREAKING_NEWS",
|
||||
"MODULE_CARBON_COPY",
|
||||
"MODULE_CHALLENGE_MODES",
|
||||
"MODULE_DISCORD_NOTIFIER",
|
||||
"MODULE_DUEL_RESET",
|
||||
"MODULE_DUNGEON_RESPAWN",
|
||||
"MODULE_DYNAMIC_TRADER",
|
||||
"MODULE_DYNAMIC_XP",
|
||||
"MODULE_ELUNA",
|
||||
"MODULE_ELUNA_SCRIPTS",
|
||||
"MODULE_ELUNA_TS",
|
||||
"MODULE_EVENT_SCRIPTS",
|
||||
"MODULE_EXCHANGE_NPC",
|
||||
"MODULE_LEARN_SPELLS",
|
||||
"MODULE_FIREWORKS",
|
||||
"MODULE_INDIVIDUAL_PROGRESSION",
|
||||
"MODULE_AUTOBALANCE",
|
||||
"MODULE_TRANSMOG",
|
||||
"MODULE_NPC_BUFFER",
|
||||
"MODULE_DYNAMIC_XP",
|
||||
"MODULE_SOLO_LFG",
|
||||
"MODULE_1V1_ARENA",
|
||||
"MODULE_PHASED_DUELS",
|
||||
"MODULE_BREAKING_NEWS",
|
||||
"MODULE_BOSS_ANNOUNCER",
|
||||
"MODULE_ACCOUNT_ACHIEVEMENTS",
|
||||
"MODULE_AUTO_REVIVE",
|
||||
"MODULE_GAIN_HONOR_GUARD",
|
||||
"MODULE_GLOBAL_CHAT",
|
||||
"MODULE_ELUNA",
|
||||
"MODULE_TIME_IS_TIME",
|
||||
"MODULE_RANDOM_ENCHANTS",
|
||||
"MODULE_SOLOCRAFT",
|
||||
"MODULE_PVP_TITLES",
|
||||
"MODULE_NPC_BEASTMASTER",
|
||||
"MODULE_NPC_ENCHANTER",
|
||||
"MODULE_INSTANCE_RESET",
|
||||
"MODULE_ARAC",
|
||||
"MODULE_ASSISTANT",
|
||||
"MODULE_REAGENT_BANK",
|
||||
"MODULE_BLACK_MARKET_AUCTION_HOUSE",
|
||||
"MODULE_OLLAMA_CHAT",
|
||||
"MODULE_PLAYER_BOT_LEVEL_BRACKETS",
|
||||
"MODULE_SKELETON_MODULE",
|
||||
"MODULE_BG_SLAVERYVALLEY",
|
||||
"MODULE_WORGOBLIN",
|
||||
"MODULE_ELUNA_TS",
|
||||
"MODULE_AIO",
|
||||
"MODULE_ELUNA_SCRIPTS",
|
||||
"MODULE_TRANSMOG_AIO",
|
||||
"MODULE_EVENT_SCRIPTS",
|
||||
"MODULE_LEVEL_UP_REWARD",
|
||||
"MODULE_ACCOUNTWIDE_SYSTEMS",
|
||||
"MODULE_EXCHANGE_NPC",
|
||||
"MODULE_RECRUIT_A_FRIEND",
|
||||
"MODULE_PRESTIGE_DRAFT_MODE",
|
||||
"MODULE_LUA_AH_BOT",
|
||||
"MODULE_HARDCORE_MODE",
|
||||
"MODULE_NPCBOT_EXTENDED_COMMANDS",
|
||||
"MODULE_TREASURE_CHEST_SYSTEM",
|
||||
"MODULE_ACTIVE_CHAT",
|
||||
"MODULE_ULTIMATE_FULL_LOOT_PVP",
|
||||
"MODULE_HORADRIC_CUBE",
|
||||
"MODULE_CARBON_COPY",
|
||||
"MODULE_TEMP_ANNOUNCEMENTS",
|
||||
"MODULE_ZONE_CHECK",
|
||||
"MODULE_AIO_BLACKJACK",
|
||||
"MODULE_SEND_AND_BIND",
|
||||
"MODULE_DYNAMIC_TRADER",
|
||||
"MODULE_LOTTERY_LUA",
|
||||
"MODULE_DISCORD_NOTIFIER",
|
||||
"MODULE_GLOBAL_MAIL_BANKING_AUCTIONS",
|
||||
"MODULE_GUILDHOUSE",
|
||||
"MODULE_HARDCORE_MODE",
|
||||
"MODULE_HORADRIC_CUBE",
|
||||
"MODULE_INDIVIDUAL_PROGRESSION",
|
||||
"MODULE_INSTANCE_RESET",
|
||||
"MODULE_ITEM_LEVEL_UP",
|
||||
"MODULE_KEEP_OUT",
|
||||
"MODULE_LEARN_SPELLS",
|
||||
"MODULE_LEVEL_GRANT",
|
||||
"MODULE_LEVEL_UP_REWARD",
|
||||
"MODULE_LOTTERY_LUA",
|
||||
"MODULE_LUA_AH_BOT",
|
||||
"MODULE_MORPHSUMMON",
|
||||
"MODULE_MULTIVENDOR",
|
||||
"MODULE_NPCBOT_EXTENDED_COMMANDS",
|
||||
"MODULE_NPC_BEASTMASTER",
|
||||
"MODULE_NPC_BUFFER",
|
||||
"MODULE_NPC_ENCHANTER",
|
||||
"MODULE_NPC_FREE_PROFESSIONS",
|
||||
"MODULE_NPC_TALENT_TEMPLATE",
|
||||
"MODULE_OLLAMA_CHAT",
|
||||
"MODULE_PHASED_DUELS",
|
||||
"MODULE_PLAYERBOTS",
|
||||
"MODULE_PLAYER_BOT_LEVEL_BRACKETS",
|
||||
"MODULE_POCKET_PORTAL",
|
||||
"MODULE_PREMIUM",
|
||||
"MODULE_PRESTIGE_DRAFT_MODE",
|
||||
"MODULE_PROGRESSION_SYSTEM",
|
||||
"MODULE_PROMOTION_AZEROTHCORE",
|
||||
"MODULE_PVP_TITLES",
|
||||
"MODULE_RANDOM_ENCHANTS",
|
||||
"MODULE_REAGENT_BANK",
|
||||
"MODULE_RECRUIT_A_FRIEND",
|
||||
"MODULE_RESURRECTION_SCROLL",
|
||||
"MODULE_REWARD_PLAYED_TIME",
|
||||
"MODULE_SEND_AND_BIND",
|
||||
"MODULE_SERVER_AUTO_SHUTDOWN",
|
||||
"MODULE_SOLOCRAFT",
|
||||
"MODULE_SOLO_LFG",
|
||||
"MODULE_SYSTEM_VIP",
|
||||
"MODULE_TEMP_ANNOUNCEMENTS",
|
||||
"MODULE_TIC_TAC_TOE",
|
||||
"MODULE_TIME_IS_TIME",
|
||||
"MODULE_TRANSMOG",
|
||||
"MODULE_TRANSMOG_AIO",
|
||||
"MODULE_TREASURE_CHEST_SYSTEM",
|
||||
"MODULE_ULTIMATE_FULL_LOOT_PVP",
|
||||
"MODULE_WAR_EFFORT",
|
||||
"MODULE_NPC_FREE_PROFESSIONS",
|
||||
"MODULE_DUEL_RESET",
|
||||
"MODULE_ZONE_DIFFICULTY",
|
||||
"MODULE_MORPHSUMMON",
|
||||
"MODULE_SPELL_REGULATOR",
|
||||
"MODULE_WEEKEND_XP",
|
||||
"MODULE_REWARD_PLAYED_TIME",
|
||||
"MODULE_RESURRECTION_SCROLL",
|
||||
"MODULE_ITEM_LEVEL_UP",
|
||||
"MODULE_NPC_TALENT_TEMPLATE",
|
||||
"MODULE_GLOBAL_CHAT",
|
||||
"MODULE_PREMIUM",
|
||||
"MODULE_SYSTEM_VIP",
|
||||
"MODULE_ACORE_SUBSCRIPTIONS",
|
||||
"MODULE_KEEP_OUT",
|
||||
"MODULE_SERVER_AUTO_SHUTDOWN",
|
||||
"MODULE_WHO_LOGGED",
|
||||
"MODULE_WORGOBLIN",
|
||||
"MODULE_ZONE_CHECK",
|
||||
"MODULE_ZONE_DIFFICULTY"
|
||||
"MODULE_ACCOUNT_MOUNTS",
|
||||
"MODULE_ANTIFARMING",
|
||||
"MODULE_ARENA_REPLAY",
|
||||
"MODULE_TIC_TAC_TOE",
|
||||
"MODULE_WAR_EFFORT",
|
||||
"MODULE_PROMOTION_AZEROTHCORE",
|
||||
"MODULE_MOD_GUILD_VILLAGE",
|
||||
"MODULE_MOD_CRAFTSPEED",
|
||||
"MODULE_MOD_AUTOFISH",
|
||||
"MODULE_MOD_VANILLA_NAXXRAMAS",
|
||||
"MODULE_MOD_TREASURE",
|
||||
"MODULE_MOD_REAL_ONLINE",
|
||||
"MODULE_MOD_INSTANCE_TOOLS",
|
||||
"MODULE_MOD_LEARNSPELLS",
|
||||
"MODULE_MOD_SWIFT_TRAVEL_FORM",
|
||||
"MODULE_MOD_CHAT_TRANSMITTER",
|
||||
"MODULE_MOD_NOTIFY_MUTED",
|
||||
"MODULE_MOD_AH_BOT_PLUS",
|
||||
"MODULE_OPENPROJECTS",
|
||||
"MODULE_MOD_DUNGEON_SCALE",
|
||||
"MODULE_AZEROTHCORE_LUA_ARENA_MASTER_COMMAND",
|
||||
"MODULE_MOD_HARDCORE_MAKGORA",
|
||||
"MODULE_MOD_GEDDON_BINDING_SHARD",
|
||||
"MODULE_MOD_GM_COMMANDS",
|
||||
"MODULE_MOD_GOMOVE",
|
||||
"MODULE_MOD_FORTIS_AUTOBALANCE",
|
||||
"MODULE_MOD_MISSING_OBJECTIVES",
|
||||
"MODULE_MOD_TRIAL_OF_FINALITY",
|
||||
"MODULE_MOD_HUNTER_PET_STORAGE",
|
||||
"MODULE_MOD_CHARACTER_SERVICES",
|
||||
"MODULE_MOD_MOUNT_REQUIREMENTS",
|
||||
"MODULE_SETXPBAR",
|
||||
"MODULE_MOD_REWARD_PLAYED_TIME_IMPROVED",
|
||||
"MODULE_MOD_GROWNUP",
|
||||
"MODULE_MOD_MYTHIC_PLUS",
|
||||
"MODULE_MOD_FACTION_FREE",
|
||||
"MODULE_MOD_FLIGHTMASTER_WHISTLE",
|
||||
"MODULE_MOD_STARTER_WANDS",
|
||||
"MODULE_MOD_MOUNTS_ON_ACCOUNT",
|
||||
"MODULE_MOD_OLLAMA_BOT_BUDDY",
|
||||
"MODULE_MOD_AOE_LOOT",
|
||||
"MODULE_MOD_PROFESSION_EXPERIENCE",
|
||||
"MODULE_MOD_ACCOUNT_VANITY_PETS",
|
||||
"MODULE_MOD_GAME_STATE_API",
|
||||
"MODULE_MOD_WEEKEND_XP",
|
||||
"MODULE_MOD_PEACEKEEPER",
|
||||
"MODULE_MOD_QUEST_LOOT_PARTY",
|
||||
"MODULE_MOD_NORDF",
|
||||
"MODULE_MOD_DISCORD_ANNOUNCE",
|
||||
"MODULE_MOD_BRAWLERS_GUILD",
|
||||
"MODULE_MOD_HARDCORE",
|
||||
"MODULE_MOD_STREAMS",
|
||||
"MODULE_MOD_BLACK_MARKET",
|
||||
"MODULE_MOD_TALENTBUTTON",
|
||||
"MODULE_MOD_SETXPBAR",
|
||||
"MODULE_MOD_ITEM_UPGRADE",
|
||||
"MODULE_MOD_LEVEL_REWARDS",
|
||||
"MODULE_MOD_REFORGING",
|
||||
"MODULE_MOD_ONY_NAXX_LOGOUT_TELEPORT",
|
||||
"MODULE_MOD_QUICK_RESPAWN",
|
||||
"MODULE_MOD_AUTO_RESURRECT",
|
||||
"MODULE_MOD_IMPROVED_BANK",
|
||||
"MODULE_MOD_BIENVENIDA",
|
||||
"MODULE_MOD_NO_HEARTHSTONE_COOLDOWN",
|
||||
"MODULE_MOD_PTR_TEMPLATE",
|
||||
"MODULE_MOD_STARTER_GUILD",
|
||||
"MODULE_MOD_BG_REWARD",
|
||||
"MODULE_MOD_NPC_MORPH",
|
||||
"MODULE_MOD_BG_ITEM_REWARD",
|
||||
"MODULE_MOD_IP_TRACKER",
|
||||
"MODULE_MOD_DMF_SWITCH",
|
||||
"MODULE_MOD_BUFF_COMMAND",
|
||||
"MODULE_MOD_NPC_CODEBOX",
|
||||
"MODULE_MOD_CHROMIE_XP",
|
||||
"MODULE_MOD_SELL_ITEMS",
|
||||
"MODULE_MOD_PVP_ZONES",
|
||||
"MODULE_MOD_CONGRATS_ON_LEVEL",
|
||||
"MODULE_MOD_GUILD_ZONE_SYSTEM",
|
||||
"MODULE_MOD_CTA_SWITCH",
|
||||
"MODULE_MOD_NPC_SPECTATOR",
|
||||
"MODULE_MOD_NPC_GAMBLER",
|
||||
"MODULE_MOD_WEAPON_VISUAL",
|
||||
"MODULE_MOD_NPC_ALL_MOUNTS",
|
||||
"MODULE_MOD_RACIAL_TRAIT_SWAP",
|
||||
"MODULE_MOD_MONEY_FOR_KILLS",
|
||||
"MODULE_MOD_APPRECIATION",
|
||||
"MODULE_MOD_HARD_MODES",
|
||||
"MODULE_MOD_QUEUE_LIST_CACHE",
|
||||
"MODULE_MOD_PVPSTATS_ANNOUNCER",
|
||||
"MODULE_MOD_RDF_EXPANSION",
|
||||
"MODULE_MOD_COSTUMES",
|
||||
"MODULE_MOD_WEEKENDBONUS",
|
||||
"MODULE_MOD_JUNK_TO_GOLD",
|
||||
"MODULE_MOD_DESERTION_WARNINGS",
|
||||
"MODULE_MOD_LOW_LEVEL_RBG",
|
||||
"MODULE_PRESTIGE",
|
||||
"MODULE_HARDMODE",
|
||||
"MODULE_MOD_LOW_LEVEL_ARENA",
|
||||
"MODULE_MOD_CFPVE",
|
||||
"MODULE_MOD_ACCOUNTBOUND",
|
||||
"MODULE_MOD_DISCORD_WEBHOOK",
|
||||
"MODULE_MOD_DUNGEONMASTER",
|
||||
"MODULE_MOD_RESET_RAID_COOLDOWNS",
|
||||
"MODULE_MOD_INCREMENT_CACHE_VERSION",
|
||||
"MODULE_MOD_RECRUIT_FRIEND",
|
||||
"MODULE_MOD_PETEQUIP",
|
||||
"MODULE_MOD_LOGIN_REWARDS",
|
||||
"MODULE_MOD_HIGH_RISK_SYSTEM",
|
||||
"MODULE_MOD_STARTING_PET",
|
||||
"MODULE_MOD_BG_TWINPEAKS",
|
||||
"MODULE_MOD_BG_BATTLE_FOR_GILNEAS",
|
||||
"MODULE_MOD_ARENA_TIGERSPEAK",
|
||||
"MODULE_MOD_ARENA_TOLVIRON",
|
||||
"MODULE_MOD_GHOST_SPEED",
|
||||
"MODULE_MOD_GUILDFUNDS",
|
||||
"MODULE_BREAKINGNEWSOVERRIDE",
|
||||
"MODULE_AOE_LOOT_MERGE",
|
||||
"MODULE_MOD_CHANGEABLESPAWNRATES",
|
||||
"MODULE_MOD_NOCLIP",
|
||||
"MODULE_MOD_NPC_SERVICES",
|
||||
"MODULE_MOD_NPC_PROMOTION",
|
||||
"MODULE_DEVJOESTAR",
|
||||
"MODULE_MOD_OBJSCALE",
|
||||
"MODULE_MOD_WARLOCK_PET_RENAME",
|
||||
"MODULE_MOD_MULTI_VENDOR",
|
||||
"MODULE_MOD_DEMONIC_PACT_CLASSIC",
|
||||
"MODULE_RECYCLEDITEMS",
|
||||
"MODULE_MOD_NPC_SUBCLASS",
|
||||
"MODULE_ATTRIBOOST",
|
||||
"MODULE_PRESTIGIOUS",
|
||||
"MODULE_RECACHE",
|
||||
"MODULE_MOD_REWARD_SHOP",
|
||||
"MODULE_EXTENDEDXP",
|
||||
"MODULE_MOD_LEVEL_15_BOOST",
|
||||
"MODULE_BGQUEUECHECKER",
|
||||
"MODULE_ADDON_FACTION_FREE_UNIT_POPUP",
|
||||
"MODULE_MOD_ENCOUNTER_LOGS",
|
||||
"MODULE_MOD_TRADE_ITEMS_FILTER",
|
||||
"MODULE_MOD_QUEST_STATUS",
|
||||
"MODULE_MOD_PVPSCRIPT",
|
||||
"MODULE_ITEMBROADCASTGUILDCHAT",
|
||||
"MODULE_FFAFIX",
|
||||
"MODULE_ACI",
|
||||
"MODULE_RAIDTELEPORTER",
|
||||
"MODULE_MOD_QUICKBALANCE",
|
||||
"MODULE_MOD_DEAD_MEANS_DEAD",
|
||||
"MODULE_MOD_DYNAMIC_LOOT_RATES",
|
||||
"MODULE_MOD_SHARE_MOUNTS",
|
||||
"MODULE_MOD_PREMIUM",
|
||||
"MODULE_MOD_GLOBALCHAT",
|
||||
"MODULE_MOD_LEECH",
|
||||
"MODULE_PLAYERTELEPORT",
|
||||
"MODULE_WRATH_OF_THE_VANILLA",
|
||||
"MODULE_STATBOOSTERREROLLER",
|
||||
"MODULE_MOD_SPONSORSHIP",
|
||||
"MODULE_MOD_PROFSPECS",
|
||||
"MODULE_UPDATE_MOB_LEVEL_TO_PLAYER_AND_RANDOM_ITEM_STATS",
|
||||
"MODULE_MOD_PREMIUM_LIB",
|
||||
"MODULE_MOD_SPAWNPOINTS",
|
||||
"MODULE_MOD_FIRSTLOGIN_AIO",
|
||||
"MODULE_KARGATUM_SYSTEM",
|
||||
"MODULE_MOD_INDIVIDUAL_XP",
|
||||
"MODULE_MOD_SPEC_REWARD",
|
||||
"MODULE_MOD_ACTIVATEZONES",
|
||||
"MODULE_MOD_INFLUXDB",
|
||||
"MODULE_MOD_SPELLREGULATOR",
|
||||
"MODULE_MOD_ITEMLEVEL",
|
||||
"MODULE_MOD_DYNAMIC_RESURRECTIONS",
|
||||
"MODULE_MOD_ALPHA_REWARDS",
|
||||
"MODULE_MOD_WHOLOGGED",
|
||||
"MODULE_REWARD_SYSTEM",
|
||||
"MODULE_MOD_CHARACTER_TOOLS",
|
||||
"MODULE_MOD_NO_FARMING",
|
||||
"MODULE_CODEBASE",
|
||||
"MODULE_KEIRA3",
|
||||
"MODULE_ACORE_LXD_IMAGE",
|
||||
"MODULE_SAHTOUTCMS",
|
||||
"MODULE_WOWDATABASEEDITOR",
|
||||
"MODULE_ACREBUILD",
|
||||
"MODULE_ACORE_CMS",
|
||||
"MODULE_SERVER_STATUS",
|
||||
"MODULE_AZEROTHCORE_ARMORY",
|
||||
"MODULE_ARENA_STATS",
|
||||
"MODULE_AZEROTHCORE_SERVER_MANAGER",
|
||||
"MODULE_WOWSIMS_TO_COMMANDS",
|
||||
"MODULE_UPDATE_MODULE_CONFS",
|
||||
"MODULE_ACORE_API",
|
||||
"MODULE_AZEROTHCORE_REGISTRATION_PAGE",
|
||||
"MODULE_MPQ_TOOLS_OSX",
|
||||
"MODULE_ACORE_TILEMAP",
|
||||
"MODULE_AZEROTHCORE_PASSRESET",
|
||||
"MODULE_FLAG_CHECKER",
|
||||
"MODULE_WOW_SERVER_RELAY",
|
||||
"MODULE_ACORE_CLIENT",
|
||||
"MODULE_AZEROTHCORE_WEBSITE",
|
||||
"MODULE_PVPSTATS",
|
||||
"MODULE_SPELLSCRIPT_REFACTOR_TOOL",
|
||||
"MODULE_STRAPI_AZEROTHCORE",
|
||||
"MODULE_WOW_ELUNA_TS_MODULE",
|
||||
"MODULE_WOW_STATISTICS",
|
||||
"MODULE_AZEROTHCORE_ANSIBLE",
|
||||
"MODULE_GUILDBANKTABFEEFIXER",
|
||||
"MODULE_TELEGRAM_AUTOMATED_DB_BACKUP",
|
||||
"MODULE_AZEROTHCOREDISCORDBOT",
|
||||
"MODULE_WOW_CLIENT_PATCHER",
|
||||
"MODULE_BG_QUEUE_ABUSER_VIEWER",
|
||||
"MODULE_ARENA_SPECTATOR",
|
||||
"MODULE_TOOL_TC_MIGRATION",
|
||||
"MODULE_AZEROTHCOREADMIN",
|
||||
"MODULE_AUTO_CHECK_RESTART",
|
||||
"MODULE_TRANSMOG_ADDONS",
|
||||
"MODULE_ACORE_BOX",
|
||||
"MODULE_NODEROUTER",
|
||||
"MODULE_WORLD_BOSS_RANK",
|
||||
"MODULE_APAW",
|
||||
"MODULE_ACORE_MINI_REG_PAGE",
|
||||
"MODULE_ACORE_PWA",
|
||||
"MODULE_MYSQL_TOOLS",
|
||||
"MODULE_ACORE_LINUX_RESTARTER",
|
||||
"MODULE_ACORE_NODE_SERVER",
|
||||
"MODULE_WEB_CHARACTER_MIGRATION_TOOL",
|
||||
"MODULE_WOWLAUNCHER_DELPHI",
|
||||
"MODULE_LUA_PARAGON_ANNIVERSARY",
|
||||
"MODULE_AZEROTHCORE_ADDITIONS",
|
||||
"MODULE_AZEROTHCORE_LUA_DEMON_MORPHER",
|
||||
"MODULE_1V1_PVP_SYSTEM",
|
||||
"MODULE_LUA_PVP_TITLES_RANKING_SYSTEM",
|
||||
"MODULE_LUA_SCRIPTS",
|
||||
"MODULE_ACORE_SOD",
|
||||
"MODULE_CONFIG_RATES",
|
||||
"MODULE_AZEROTHCORE_TRIVIA_SYSTEM",
|
||||
"MODULE_LOTTERY_CHANCE_INSTANT",
|
||||
"MODULE_WEEKLY_ARMOR_VENDOR_BLACK_MARKET",
|
||||
"MODULE_MORZA_ISLAND_ARAXIA_SERVER",
|
||||
"MODULE_MOD_DEATHROLL_AIO",
|
||||
"MODULE_EXTENDED_HOLIDAYS_LUA",
|
||||
"MODULE_ACORE_LUA_UNLIMITED_AMMO",
|
||||
"MODULE_LUA_VIP",
|
||||
"MODULE_AZEROTHCORE_WOWHEAD_MOD_LUA",
|
||||
"MODULE_ELUNA_WOW_SCRIPTS",
|
||||
"MODULE_LUA_NOTONLY_RANDOMMORPHER",
|
||||
"MODULE_LUA_ITEMUPGRADER_TEMPLATE",
|
||||
"MODULE_LUA_SUPER_BUFFERNPC",
|
||||
"MODULE_ACORE_ZONEDEBUFF",
|
||||
"MODULE_ACORE_ELUNATEST",
|
||||
"MODULE_ACORE_SUMMONALL",
|
||||
"MODULE_ACORE_BG_END_ANNOUNCER",
|
||||
"MODULE_LUA_AIO_MODRATE_EXP",
|
||||
"MODULE_LUA_COMMAND_PLUS",
|
||||
"MODULE_TBC_RAID_HP_RESTORATION",
|
||||
"MODULE_MOD_RARE_DROPS",
|
||||
"MODULE_MOD_LEVEL_ONE_MOUNTS",
|
||||
"MODULE_BLIZZLIKE_TELES",
|
||||
"MODULE_SQL_NPC_TELEPORTER",
|
||||
"MODULE_ACORE_MALL",
|
||||
"MODULE_HEARTHSTONE_COOLDOWNS",
|
||||
"MODULE_AZEROTHCORE_ALL_STACKABLES_200",
|
||||
"MODULE_PORTALS_IN_ALL_CAPITALS",
|
||||
"MODULE_AZTRAL_AIRLINES",
|
||||
"MODULE_MOD_IP2NATION",
|
||||
"MODULE_CLASSIC_MODE"
|
||||
],
|
||||
"label": "\ud83e\udde9 All Modules",
|
||||
"description": "Enable every optional module in the repository",
|
||||
|
||||
@@ -40,6 +40,7 @@ services:
|
||||
- --innodb-log-file-size=${MYSQL_INNODB_LOG_FILE_SIZE}
|
||||
- --innodb-redo-log-capacity=${MYSQL_INNODB_REDO_LOG_CAPACITY}
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
healthcheck:
|
||||
test: ["CMD", "sh", "-c", "mysqladmin ping -h localhost -u ${MYSQL_USER} -p${MYSQL_ROOT_PASSWORD} --silent || exit 1"]
|
||||
interval: ${MYSQL_HEALTHCHECK_INTERVAL}
|
||||
@@ -477,6 +478,7 @@ services:
|
||||
ports:
|
||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
networks:
|
||||
- azerothcore
|
||||
volumes:
|
||||
@@ -531,6 +533,7 @@ services:
|
||||
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
||||
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
networks:
|
||||
- azerothcore
|
||||
cap_add: ["SYS_NICE"]
|
||||
@@ -568,6 +571,11 @@ services:
|
||||
ports:
|
||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
networks:
|
||||
- azerothcore
|
||||
volumes:
|
||||
@@ -603,6 +611,7 @@ services:
|
||||
ports:
|
||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
networks:
|
||||
- azerothcore
|
||||
volumes:
|
||||
@@ -660,6 +669,7 @@ services:
|
||||
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
||||
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
networks:
|
||||
- azerothcore
|
||||
cap_add: ["SYS_NICE"]
|
||||
@@ -716,6 +726,11 @@ services:
|
||||
- "${WORLD_EXTERNAL_PORT}:${WORLD_PORT}"
|
||||
- "${SOAP_EXTERNAL_PORT}:${SOAP_PORT}"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
cap_add: ["SYS_NICE"]
|
||||
healthcheck:
|
||||
test: ["CMD", "sh", "-c", "ps aux | grep '[w]orldserver' | grep -v grep || exit 1"]
|
||||
@@ -863,10 +878,6 @@ services:
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
|
||||
251
docs/MODULE_FAILURES.md
Normal file
251
docs/MODULE_FAILURES.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Module Compilation Failures
|
||||
|
||||
This document tracks all modules that have been disabled due to compilation failures or other issues during the validation process.
|
||||
|
||||
**Last Updated:** 2025-11-22
|
||||
|
||||
**Total Blocked Modules:** 93
|
||||
|
||||
---
|
||||
|
||||
## Compilation Errors
|
||||
|
||||
### Virtual Function Override Errors
|
||||
These modules incorrectly mark non-virtual functions with 'override':
|
||||
|
||||
- **MODULE_MOD_ACCOUNTBOUND** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_RECYCLEDITEMS** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_PRESTIGE** - 'OnLogin' marked 'override' but does not override
|
||||
- **MODULE_PLAYERTELEPORT** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_ITEMBROADCASTGUILDCHAT** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_LOGIN_REWARDS** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_NOCLIP** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_OBJSCALE** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_QUEST_STATUS** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_RARE_DROPS** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_TRADE_ITEMS_FILTER** - only virtual member functions can be marked 'override'
|
||||
- **MODULE_MOD_STARTING_PET** - `OnFirstLogin` marked `override` but base method is not virtual
|
||||
|
||||
### Missing Member Errors
|
||||
These modules reference class members that don't exist:
|
||||
|
||||
- **MODULE_MOD_FIRSTLOGIN_AIO** - no member named 'getLevel'; did you mean 'GetLevel'?
|
||||
- **MODULE_MOD_PVPSCRIPT** - no member named 'SendNotification' in 'WorldSession'
|
||||
- **MODULE_MOD_KARGATUM_SYSTEM** - no member named 'PQuery' / 'outString' in Log
|
||||
- **MODULE_MOD_ENCOUNTER_LOGS** - no member named 'IsWorldObject' in 'Unit'
|
||||
- **MODULE_MOD_GOMOVE** - no member named 'DestroyForNearbyPlayers' in 'GameObject'
|
||||
- **MODULE_MOD_LEVEL_15_BOOST** - no member named 'getLevel' in 'Player'
|
||||
- **MODULE_MOD_LEVEL_REWARDS** - no member named 'SetStationary' in 'MailDraft'
|
||||
- **MODULE_MOD_MULTI_VENDOR** - no member named 'SendNotification' in 'WorldSession'
|
||||
- **MODULE_MOD_OBJSCALE** - no member named 'DestroyForNearbyPlayers' in 'GameObject'
|
||||
- **MODULE_MOD_TRIAL_OF_FINALITY** - no member named 'isEmpty' in 'MapRefMgr'
|
||||
- **MODULE_MOD_ALPHA_REWARDS** - no member named 'GetIntDefault' in 'ConfigMgr'
|
||||
|
||||
### Incomplete Type Errors
|
||||
|
||||
- **MODULE_MOD_ITEMLEVEL** - 'ChatHandler' is an incomplete type
|
||||
|
||||
### Undeclared Identifier Errors
|
||||
|
||||
- **MODULE_PRESTIGIOUS** - use of undeclared identifier 'sSpellMgr'
|
||||
|
||||
### Missing Header/Dependency Errors
|
||||
|
||||
- **MODULE_STATBOOSTERREROLLER** - 'StatBoostMgr.h' file not found
|
||||
|
||||
---
|
||||
|
||||
## Configuration/Build Errors
|
||||
|
||||
### CMake/Library Errors
|
||||
|
||||
- **MODULE_MOD_INFLUXDB** - CMake Error: Could NOT find CURL
|
||||
- **MODULE_MOD_DUNGEON_SCALE** - Duplicate symbol definitions for AutoBalance utilities (GetCurrentConfigTime, LoadMapSettings, etc.) when linked with mod-autobalance
|
||||
- **MODULE_MOD_GAME_STATE_API** - TLS symbol mismatch in cpp-httplib (`HttpGameStateServer.cpp` vs `mod_discord_announce.cpp`) causes linker failure (`error adding symbols: bad value`)
|
||||
- **MODULE_WOW_STATISTICS** - Missing script loader; `Addwow_statisticsScripts()` referenced by ModulesLoader but not defined
|
||||
- **MODULE_WOW_CLIENT_PATCHER** - Missing script loader; `Addwow_client_patcherScripts()` referenced by ModulesLoader but not defined
|
||||
|
||||
### Missing Script Loader / Non-C++ Modules
|
||||
|
||||
These repositories are Lua scripts or external web tools without a worldserver loader. When they are flagged as C++ modules the build fails with undefined references during linking:
|
||||
|
||||
- **MODULE_MOD_DISCORD_WEBHOOK** - No `Addmod_discord_webhookScripts()` implementation
|
||||
- **MODULE_BG_QUEUE_ABUSER_VIEWER** - No `AddBG_Queue_Abuser_ViewerScripts()` implementation
|
||||
- **MODULE_ACORE_API** - No `Addacore_apiScripts()` implementation
|
||||
- **MODULE_ACORE_CLIENT** - No `Addacore_clientScripts()` implementation
|
||||
- **MODULE_ACORE_CMS** - No `Addacore_cmsScripts()` implementation
|
||||
- **MODULE_ACORE_NODE_SERVER** - No `Addacore_node_serverScripts()` implementation
|
||||
- **MODULE_ACORE_PWA** - No `Addacore_pwaScripts()` implementation
|
||||
- **MODULE_ACORE_TILEMAP** - No `Addacore_tilemapScripts()` implementation
|
||||
- **MODULE_APAW** - No `AddapawScripts()` implementation
|
||||
- **MODULE_ARENA_STATS** - No `Addarena_statsScripts()` implementation
|
||||
- **MODULE_AZEROTHCORE_ARMORY** - No `Addazerothcore_armoryScripts()` implementation
|
||||
- **MODULE_LUA_ITEMUPGRADER_TEMPLATE** - Lua-only script; no `Addlua_ItemUpgrader_TemplateScripts()`
|
||||
- **MODULE_LUA_NOTONLY_RANDOMMORPHER** - Lua-only script; no `Addlua_NotOnly_RandomMorpherScripts()`
|
||||
- **MODULE_LUA_SUPER_BUFFERNPC** - Lua-only script; no `Addlua_Super_BufferNPCScripts()`
|
||||
- **MODULE_LUA_PARAGON_ANNIVERSARY** - Lua-only script; no `Addlua_paragon_anniversaryScripts()`
|
||||
|
||||
### SQL Import Errors (Runtime)
|
||||
|
||||
- **MODULE_MOD_REWARD_SHOP** - `npc.sql` references obsolete `modelid1` column during db-import
|
||||
- **MODULE_BLACK_MARKET_AUCTION_HOUSE** - `MODULE_mod-black-market_creature.sql` references removed `StatsCount` column (ERROR 1054 at line 14, causes worldserver crash-loop)
|
||||
- **MODULE_MOD_GUILD_VILLAGE** - `MODULE_mod-guild-village_001_creature_template.sql` tries to insert duplicate creature ID 987400 (ERROR 1062: Duplicate entry for key 'creature_template.PRIMARY')
|
||||
- **MODULE_MOD_INSTANCE_TOOLS** - `MODULE_mod-instance-tools_Creature.sql` tries to insert duplicate creature ID 987456-0 (ERROR 1062: Duplicate entry for key 'creature_template_model.PRIMARY')
|
||||
- **MODULE_ACORE_SUBSCRIPTIONS** - C++ code queries missing table `acore_auth.acore_cms_subscriptions` (ERROR 1146: Table doesn't exist, causes server ABORT)
|
||||
- **Resolution Required:** Module directory at `local-storage/modules/mod-acore-subscriptions` must be removed and worldserver rebuilt. Disabling in .env alone is insufficient because the code is already compiled into the binary.
|
||||
- **Process:** Either (1) remove module directory + rebuild, OR (2) create the missing database table/schema
|
||||
- **MODULE_NODEROUTER** - No `AddnoderouterScripts()` implementation
|
||||
- **MODULE_SERVER_STATUS** - No `Addserver_statusScripts()` implementation
|
||||
- **MODULE_WORLD_BOSS_RANK** - No `Addworld_boss_rankScripts()` implementation
|
||||
|
||||
---
|
||||
|
||||
## Auto-Disabled Modules (Outdated)
|
||||
|
||||
These modules have not been updated in over 2 years and were automatically disabled:
|
||||
|
||||
- **MODULE_MOD_DYNAMIC_RESURRECTIONS** - Last updated: 2019-07-16
|
||||
- **MODULE_MOD_WHOLOGGED** - Last updated: 2018-07-03
|
||||
- **MODULE_REWARD_SYSTEM** - Last updated: 2018-07-02
|
||||
- **MODULE_MOD_CHARACTER_TOOLS** - Last updated: 2018-07-02
|
||||
- **MODULE_MOD_NO_FARMING** - Last updated: 2018-05-15
|
||||
|
||||
---
|
||||
|
||||
## Git/Clone Errors
|
||||
|
||||
- **MODULE_ELUNA_WOW_SCRIPTS** - Git clone error: unknown switch 'E'
|
||||
|
||||
---
|
||||
|
||||
## Summary by Error Type
|
||||
|
||||
| Error Type | Count | Common Cause |
|
||||
|------------|-------|--------------|
|
||||
| Virtual function override | 11 | API changes in AzerothCore hooks |
|
||||
| Missing members | 11 | API changes - methods renamed/removed |
|
||||
| Incomplete type | 1 | Missing include or forward declaration |
|
||||
| Undeclared identifier | 1 | Missing include or API change |
|
||||
| Missing headers | 1 | Module dependency missing |
|
||||
| CMake/Library | 1 | External dependency not available |
|
||||
| Outdated (>2yr) | 5 | Module unmaintained |
|
||||
| Git errors | 1 | Repository/clone issues |
|
||||
|
||||
**Total:** 66 blocked modules
|
||||
|
||||
---
|
||||
|
||||
## Resolution Status
|
||||
|
||||
All blocked modules have been:
|
||||
- ✅ Disabled in `.env` file
|
||||
- ✅ Marked as 'blocked' in `config/module-manifest.json`
|
||||
- ✅ Block reason documented in manifest
|
||||
- ✅ Notes added to manifest with error details
|
||||
|
||||
---
|
||||
|
||||
## Runtime Validation Process
|
||||
|
||||
When worldserver crashes or fails to start due to modules:
|
||||
|
||||
1. **Check for crash-loops**: Use `docker inspect ac-worldserver --format='RestartCount: {{.RestartCount}}'`
|
||||
- RestartCount > 0 indicates crash-loop, not a healthy running state
|
||||
|
||||
2. **Examine logs**: `docker logs ac-worldserver --tail 200 | grep -B 10 "ABORT"`
|
||||
- Look for ERROR messages, ABORT signals, and stack traces
|
||||
- Identify the failing module from error context
|
||||
|
||||
3. **Categorize the error**:
|
||||
- **SQL Import Errors**: Table/column doesn't exist, duplicate keys
|
||||
- **Missing Database Tables**: C++ code queries tables that don't exist
|
||||
- **Configuration Issues**: Missing required config files or settings
|
||||
|
||||
4. **For modules with compiled C++ code querying missing DB tables**:
|
||||
- **Important**: Disabling in `.env` is NOT sufficient - code is already compiled
|
||||
- **Resolution Options**:
|
||||
a. Remove module directory from `local-storage/modules/` + rebuild (preferred for broken modules)
|
||||
b. Create the missing database table/schema (if you want to keep the module)
|
||||
- Never use `sudo rm -rf` on module directories without explicit user approval
|
||||
- Document the issue clearly before taking action
|
||||
|
||||
5. **For SQL import errors**:
|
||||
- Disable module in `.env`
|
||||
- Remove problematic SQL files from container: `docker exec ac-worldserver rm -f /path/to/sql/file.sql`
|
||||
- Restart worldserver (no rebuild needed for SQL-only issues)
|
||||
|
||||
6. **For Lua-only modules** (scripts without C++ components):
|
||||
- **Important**: Disabling Lua modules may leave behind database artifacts
|
||||
- Lua modules often create:
|
||||
- Custom database tables (in acore_world, acore_characters, or acore_auth)
|
||||
- Stored procedures, triggers, or events
|
||||
- NPC/creature/gameobject entries in world tables
|
||||
- **SQL Cleanup Required**: When disabling Lua modules, you may need to:
|
||||
a. Identify tables/data created by the module (check module's SQL files)
|
||||
b. Manually DROP tables or DELETE entries if the module doesn't provide cleanup scripts
|
||||
c. Check for orphaned NPCs/creatures that reference the module's functionality
|
||||
- **Best Practice**: Before disabling, review the module's `data/sql/` directory to understand what was installed
|
||||
|
||||
6. **Update documentation**:
|
||||
- Add entry to MODULE_FAILURES.md
|
||||
- Update module-manifest.json with block_reason
|
||||
- Increment total blocked modules count
|
||||
|
||||
7. **Verify fix**: Restart worldserver and confirm RestartCount stays at 0
|
||||
|
||||
---
|
||||
|
||||
## SQL Update System & Database Maintenance
|
||||
|
||||
### Our Implementation
|
||||
|
||||
This deployment uses AzerothCore's built-in SQL update system with the following structure:
|
||||
|
||||
- **Module SQL Location**: Each module places SQL files in `/azerothcore/data/sql/updates/db-world/`, `db-auth/`, or `db-characters/`
|
||||
- **Automatic Import**: On worldserver startup, AzerothCore scans these directories and applies any SQL files not yet in the `updates` tracking table
|
||||
- **One-Time Execution**: SQL files are tracked in the `updates` table to prevent re-execution
|
||||
- **Persistent Storage**: SQL files are mounted from `local-storage/modules/*/data/sql/` into the container
|
||||
|
||||
### AzerothCore Wiki Reference
|
||||
|
||||
Per the [AzerothCore Keeping the Server Up to Date](https://www.azerothcore.org/wiki/keeping-the-server-up-to-date) documentation:
|
||||
|
||||
- Core updates include SQL changes that must be applied to databases
|
||||
- The server automatically imports SQL files from `data/sql/updates/` directories
|
||||
- Failed SQL imports cause the server to ABORT (as seen with our module validation)
|
||||
- Database structure must match what the C++ code expects
|
||||
|
||||
### Module SQL Lifecycle
|
||||
|
||||
1. **Installation**: Module's SQL files copied to container's `/azerothcore/data/sql/updates/` during build
|
||||
2. **First Startup**: Files executed and tracked in `updates` table
|
||||
3. **Subsequent Startups**: Files skipped (already in `updates` table)
|
||||
4. **Module Disabled**: SQL files may persist in container unless manually removed
|
||||
5. **Database Artifacts**: Tables/data created by SQL remain until manually cleaned up
|
||||
|
||||
### Critical Notes
|
||||
|
||||
- **Disabling a module does NOT remove its SQL files** from the container
|
||||
- **Disabling a module does NOT drop its database tables** or remove its data
|
||||
- **Problematic SQL files must be manually removed** from the container after disabling the module
|
||||
- **Database cleanup is manual** - no automatic rollback when modules are disabled
|
||||
- **Lua modules** especially prone to leaving orphaned database artifacts (tables, NPCs, gameobjects)
|
||||
|
||||
### Troubleshooting SQL Issues
|
||||
|
||||
When a module's SQL import fails:
|
||||
|
||||
1. **Error in logs**: Server logs show which SQL file failed and the MySQL error
|
||||
2. **Server ABORTs**: Failed imports cause server to abort startup
|
||||
3. **Resolution**:
|
||||
- Disable module in `.env`
|
||||
- Remove problematic SQL file from container: `docker exec ac-worldserver rm -f /path/to/file.sql`
|
||||
- Restart server (file won't be re-imported since it's deleted)
|
||||
- **OR** if you want to keep the module: Fix the SQL file in `local-storage/modules/*/data/sql/` and rebuild
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Continue build/deploy cycle until all compilation errors resolved
|
||||
2. Monitor for additional module failures
|
||||
3. Document any new failures as they occur
|
||||
4. Consider creating GitHub issues for maintainable modules with API incompatibilities
|
||||
265
scripts/bash/cleanup-orphaned-sql.sh
Executable file
265
scripts/bash/cleanup-orphaned-sql.sh
Executable file
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# cleanup-orphaned-sql.sh
|
||||
#
|
||||
# Cleans up orphaned SQL update entries from the database.
|
||||
# These are entries in the 'updates' table that reference files no longer on disk.
|
||||
#
|
||||
# This happens when:
|
||||
# - Modules are removed/uninstalled
|
||||
# - Modules are updated and old SQL files are deleted
|
||||
# - Manual SQL cleanup occurs
|
||||
#
|
||||
# NOTE: These warnings are informational and don't affect server operation.
|
||||
# This script is optional - it just cleans up the logs.
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
MYSQL_CONTAINER="${MYSQL_CONTAINER:-ac-mysql}"
|
||||
WORLDSERVER_CONTAINER="${WORLDSERVER_CONTAINER:-ac-worldserver}"
|
||||
MYSQL_USER="${MYSQL_USER:-root}"
|
||||
MYSQL_PASSWORD="${MYSQL_ROOT_PASSWORD:-}"
|
||||
DRY_RUN=false
|
||||
VERBOSE=false
|
||||
DATABASES=("acore_world" "acore_characters" "acore_auth")
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Usage
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Clean up orphaned SQL update entries from AzerothCore databases.
|
||||
|
||||
OPTIONS:
|
||||
-p, --password PASSWORD MySQL root password (or use MYSQL_ROOT_PASSWORD env var)
|
||||
-c, --container NAME MySQL container name (default: ac-mysql)
|
||||
-w, --worldserver NAME Worldserver container name (default: ac-worldserver)
|
||||
-d, --database DB Clean only specific database (world, characters, auth)
|
||||
-n, --dry-run Show what would be cleaned without making changes
|
||||
-v, --verbose Show detailed output
|
||||
-h, --help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
# Dry run to see what would be cleaned
|
||||
$0 --dry-run
|
||||
|
||||
# Clean all databases
|
||||
$0 --password yourpassword
|
||||
|
||||
# Clean only world database
|
||||
$0 --password yourpassword --database world
|
||||
|
||||
# Verbose output
|
||||
$0 --password yourpassword --verbose
|
||||
|
||||
NOTES:
|
||||
- This script only removes entries from the 'updates' table
|
||||
- It does NOT remove any actual data or tables
|
||||
- It does NOT reverse any SQL that was applied
|
||||
- This is safe to run and only cleans up tracking metadata
|
||||
- Orphaned entries occur when modules are removed/updated
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-p|--password)
|
||||
MYSQL_PASSWORD="$2"
|
||||
shift 2
|
||||
;;
|
||||
-c|--container)
|
||||
MYSQL_CONTAINER="$2"
|
||||
shift 2
|
||||
;;
|
||||
-w|--worldserver)
|
||||
WORLDSERVER_CONTAINER="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--database)
|
||||
case $2 in
|
||||
world) DATABASES=("acore_world") ;;
|
||||
characters) DATABASES=("acore_characters") ;;
|
||||
auth) DATABASES=("acore_auth") ;;
|
||||
*) echo -e "${RED}Error: Invalid database '$2'${NC}"; exit 1 ;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
-n|--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Error: Unknown option '$1'${NC}"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check password
|
||||
if [[ -z "$MYSQL_PASSWORD" ]]; then
|
||||
echo -e "${RED}Error: MySQL password required${NC}"
|
||||
echo "Use --password or set MYSQL_ROOT_PASSWORD environment variable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check containers exist
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${MYSQL_CONTAINER}$"; then
|
||||
echo -e "${RED}Error: MySQL container '$MYSQL_CONTAINER' not found or not running${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${WORLDSERVER_CONTAINER}$"; then
|
||||
echo -e "${RED}Error: Worldserver container '$WORLDSERVER_CONTAINER' not found or not running${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ AzerothCore Orphaned SQL Cleanup ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo -e "${YELLOW}DRY RUN MODE - No changes will be made${NC}"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Function to get SQL files from worldserver container
|
||||
get_sql_files() {
|
||||
local db_type=$1
|
||||
docker exec "$WORLDSERVER_CONTAINER" find "/azerothcore/data/sql/updates/${db_type}/" -name "*.sql" -type f 2>/dev/null | \
|
||||
xargs -I {} basename {} 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Function to clean orphaned entries
|
||||
clean_orphaned_entries() {
|
||||
local database=$1
|
||||
local db_type=$2
|
||||
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${GREEN}Processing: $database${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
# Get list of SQL files on disk
|
||||
local sql_files
|
||||
sql_files=$(get_sql_files "$db_type")
|
||||
|
||||
if [[ -z "$sql_files" ]]; then
|
||||
echo -e "${YELLOW}⚠ No SQL files found in /azerothcore/data/sql/updates/${db_type}/${NC}"
|
||||
echo
|
||||
return
|
||||
fi
|
||||
|
||||
local file_count
|
||||
file_count=$(echo "$sql_files" | wc -l)
|
||||
echo -e "📁 Found ${file_count} SQL files on disk"
|
||||
|
||||
# Get entries from updates table
|
||||
local total_updates
|
||||
total_updates=$(docker exec "$MYSQL_CONTAINER" mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$database" -sN \
|
||||
-e "SELECT COUNT(*) FROM updates" 2>/dev/null || echo "0")
|
||||
|
||||
echo -e "📊 Total updates in database: ${total_updates}"
|
||||
|
||||
if [[ "$total_updates" == "0" ]]; then
|
||||
echo -e "${YELLOW}⚠ No updates found in database${NC}"
|
||||
echo
|
||||
return
|
||||
fi
|
||||
|
||||
# Find orphaned entries (in DB but not on disk)
|
||||
# We'll create a temp table with file names and do a LEFT JOIN
|
||||
local orphaned_count=0
|
||||
local orphaned_list=""
|
||||
|
||||
# Get all update names from DB
|
||||
local db_updates
|
||||
db_updates=$(docker exec "$MYSQL_CONTAINER" mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$database" -sN \
|
||||
-e "SELECT name FROM updates ORDER BY name" 2>/dev/null || true)
|
||||
|
||||
if [[ -n "$db_updates" ]]; then
|
||||
# Check each DB entry against disk files
|
||||
while IFS= read -r update_name; do
|
||||
if ! echo "$sql_files" | grep -qF "$update_name"; then
|
||||
((orphaned_count++))
|
||||
if [[ "$VERBOSE" == true ]] || [[ "$DRY_RUN" == true ]]; then
|
||||
orphaned_list="${orphaned_list}${update_name}\n"
|
||||
fi
|
||||
|
||||
# Delete if not dry run
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
docker exec "$MYSQL_CONTAINER" mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$database" -e \
|
||||
"DELETE FROM updates WHERE name='${update_name}'" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
done <<< "$db_updates"
|
||||
fi
|
||||
|
||||
# Report results
|
||||
if [[ $orphaned_count -gt 0 ]]; then
|
||||
echo -e "${YELLOW}🗑️ Orphaned entries: ${orphaned_count}${NC}"
|
||||
|
||||
if [[ "$VERBOSE" == true ]] || [[ "$DRY_RUN" == true ]]; then
|
||||
echo
|
||||
echo -e "${YELLOW}Orphaned files:${NC}"
|
||||
echo -e "$orphaned_list" | head -20
|
||||
if [[ $orphaned_count -gt 20 ]]; then
|
||||
echo -e "${YELLOW}... and $((orphaned_count - 20)) more${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
echo -e "${GREEN}✅ Cleaned ${orphaned_count} orphaned entries${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}Would clean ${orphaned_count} orphaned entries${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}✅ No orphaned entries found${NC}"
|
||||
fi
|
||||
|
||||
echo
|
||||
}
|
||||
|
||||
# Process each database
|
||||
for db in "${DATABASES[@]}"; do
|
||||
case $db in
|
||||
acore_world)
|
||||
clean_orphaned_entries "$db" "db_world"
|
||||
;;
|
||||
acore_characters)
|
||||
clean_orphaned_entries "$db" "db_characters"
|
||||
;;
|
||||
acore_auth)
|
||||
clean_orphaned_entries "$db" "db_auth"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${GREEN}Cleanup Complete${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo
|
||||
echo -e "${YELLOW}This was a dry run. To actually clean orphaned entries, run:${NC}"
|
||||
echo -e "${YELLOW} $0 --password yourpassword${NC}"
|
||||
fi
|
||||
@@ -1,57 +1,167 @@
|
||||
#!/bin/bash
|
||||
# Fix item import for backup-merged characters
|
||||
#
|
||||
# Usage:
|
||||
# fix-item-import.sh [OPTIONS]
|
||||
#
|
||||
# Options:
|
||||
# --backup-dir DIR Path to backup directory (required)
|
||||
# --account-ids IDS Comma-separated account IDs (e.g., "451,452")
|
||||
# --char-guids GUIDS Comma-separated character GUIDs (e.g., "4501,4502,4503")
|
||||
# --mysql-password PW MySQL root password (or use MYSQL_ROOT_PASSWORD env var)
|
||||
# --mysql-container NAME MySQL container name (default: ac-mysql)
|
||||
# --auth-db NAME Auth database name (default: acore_auth)
|
||||
# --characters-db NAME Characters database name (default: acore_characters)
|
||||
# -h, --help Show this help message
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
COLOR_RED='\033[0;31m'
|
||||
COLOR_GREEN='\033[0;32m'
|
||||
COLOR_YELLOW='\033[1;33m'
|
||||
COLOR_BLUE='\033[0;34m'
|
||||
COLOR_CYAN='\033[0;36m'
|
||||
COLOR_RESET='\033[0m'
|
||||
# Source common library
|
||||
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
|
||||
source "$SCRIPT_DIR/lib/common.sh"
|
||||
else
|
||||
echo "ERROR: Common library not found at $SCRIPT_DIR/lib/common.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log(){ printf '%b\n' "${COLOR_GREEN}$*${COLOR_RESET}"; }
|
||||
info(){ printf '%b\n' "${COLOR_CYAN}$*${COLOR_RESET}"; }
|
||||
warn(){ printf '%b\n' "${COLOR_YELLOW}$*${COLOR_RESET}"; }
|
||||
err(){ printf '%b\n' "${COLOR_RED}$*${COLOR_RESET}"; }
|
||||
fatal(){ err "$*"; exit 1; }
|
||||
# Default values (can be overridden by environment or command line)
|
||||
BACKUP_DIR="${BACKUP_DIR:-}"
|
||||
ACCOUNT_IDS="${ACCOUNT_IDS:-}"
|
||||
CHAR_GUIDS="${CHAR_GUIDS:-}"
|
||||
MYSQL_PW="${MYSQL_ROOT_PASSWORD:-}"
|
||||
MYSQL_CONTAINER="${MYSQL_CONTAINER:-ac-mysql}"
|
||||
AUTH_DB="${AUTH_DB:-acore_auth}"
|
||||
CHARACTERS_DB="${CHARACTERS_DB:-acore_characters}"
|
||||
|
||||
MYSQL_PW="azerothcore123"
|
||||
BACKUP_DIR="/nfs/containers/ac-backup"
|
||||
AUTH_DB="acore_auth"
|
||||
CHARACTERS_DB="acore_characters"
|
||||
# Show help message
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Fix item import for backup-merged characters
|
||||
|
||||
# Verify parameters
|
||||
[[ -d "$BACKUP_DIR" ]] || fatal "Backup directory not found: $BACKUP_DIR"
|
||||
Usage:
|
||||
fix-item-import.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--backup-dir DIR Path to backup directory (required)
|
||||
--account-ids IDS Comma-separated account IDs (e.g., "451,452")
|
||||
--char-guids GUIDS Comma-separated character GUIDs (e.g., "4501,4502,4503")
|
||||
--mysql-password PW MySQL root password (or use MYSQL_ROOT_PASSWORD env var)
|
||||
--mysql-container NAME MySQL container name (default: ac-mysql)
|
||||
--auth-db NAME Auth database name (default: acore_auth)
|
||||
--characters-db NAME Characters database name (default: acore_characters)
|
||||
-h, --help Show this help message
|
||||
|
||||
Environment Variables:
|
||||
BACKUP_DIR Alternative to --backup-dir
|
||||
ACCOUNT_IDS Alternative to --account-ids
|
||||
CHAR_GUIDS Alternative to --char-guids
|
||||
MYSQL_ROOT_PASSWORD Alternative to --mysql-password
|
||||
MYSQL_CONTAINER Alternative to --mysql-container
|
||||
AUTH_DB Alternative to --auth-db
|
||||
CHARACTERS_DB Alternative to --characters-db
|
||||
|
||||
Example:
|
||||
fix-item-import.sh \\
|
||||
--backup-dir /path/to/backup \\
|
||||
--account-ids "451,452" \\
|
||||
--char-guids "4501,4502,4503" \\
|
||||
--mysql-password "azerothcore123"
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--backup-dir)
|
||||
BACKUP_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--account-ids)
|
||||
ACCOUNT_IDS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--char-guids)
|
||||
CHAR_GUIDS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-password)
|
||||
MYSQL_PW="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mysql-container)
|
||||
MYSQL_CONTAINER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--auth-db)
|
||||
AUTH_DB="$2"
|
||||
shift 2
|
||||
;;
|
||||
--characters-db)
|
||||
CHARACTERS_DB="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
fatal "Unknown option: $1\nUse --help for usage information"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required parameters
|
||||
if [ -z "$BACKUP_DIR" ]; then
|
||||
fatal "Backup directory not specified. Use --backup-dir or set BACKUP_DIR environment variable."
|
||||
fi
|
||||
|
||||
if [ ! -d "$BACKUP_DIR" ]; then
|
||||
fatal "Backup directory not found: $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
if [ -z "$ACCOUNT_IDS" ]; then
|
||||
fatal "Account IDs not specified. Use --account-ids or set ACCOUNT_IDS environment variable."
|
||||
fi
|
||||
|
||||
if [ -z "$CHAR_GUIDS" ]; then
|
||||
fatal "Character GUIDs not specified. Use --char-guids or set CHAR_GUIDS environment variable."
|
||||
fi
|
||||
|
||||
if [ -z "$MYSQL_PW" ]; then
|
||||
fatal "MySQL password not specified. Use --mysql-password or set MYSQL_ROOT_PASSWORD environment variable."
|
||||
fi
|
||||
|
||||
# Setup temp directory
|
||||
TEMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
# MySQL connection helper
|
||||
mysql_exec(){
|
||||
# MySQL connection helpers (override common.sh defaults with script-specific values)
|
||||
mysql_exec_local(){
|
||||
local db="$1"
|
||||
docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" "$db" 2>/dev/null
|
||||
docker exec -i "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" "$db" 2>/dev/null
|
||||
}
|
||||
|
||||
mysql_query(){
|
||||
mysql_query_local(){
|
||||
local db="$1"
|
||||
local query="$2"
|
||||
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -N -B "$db" -e "$query" 2>/dev/null
|
||||
docker exec "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" -N -B "$db" -e "$query" 2>/dev/null
|
||||
}
|
||||
|
||||
log "═══════════════════════════════════════════════════════════"
|
||||
log " FIXING ITEM IMPORT FOR BACKUP-MERGED CHARACTERS"
|
||||
log "═══════════════════════════════════════════════════════════"
|
||||
|
||||
# Find characters that were imported from the backup (accounts 451, 452)
|
||||
# Find characters that were imported from the backup
|
||||
log "Finding characters that need item restoration..."
|
||||
IMPORTED_CHARS=$(mysql_query "$CHARACTERS_DB" "SELECT name, guid FROM characters WHERE account IN (451, 452);")
|
||||
info "Looking for characters with account IDs: $ACCOUNT_IDS"
|
||||
IMPORTED_CHARS=$(mysql_query_local "$CHARACTERS_DB" "SELECT name, guid FROM characters WHERE account IN ($ACCOUNT_IDS);")
|
||||
|
||||
if [[ -z "$IMPORTED_CHARS" ]]; then
|
||||
fatal "No imported characters found (accounts 451, 452)"
|
||||
fatal "No imported characters found with account IDs: $ACCOUNT_IDS"
|
||||
fi
|
||||
|
||||
info "Found imported characters:"
|
||||
@@ -60,7 +170,8 @@ echo "$IMPORTED_CHARS" | while read -r char_name char_guid; do
|
||||
done
|
||||
|
||||
# Check current item count for these characters
|
||||
CURRENT_ITEM_COUNT=$(mysql_query "$CHARACTERS_DB" "SELECT COUNT(*) FROM item_instance WHERE owner_guid IN (4501, 4502, 4503);")
|
||||
info "Checking existing items for character GUIDs: $CHAR_GUIDS"
|
||||
CURRENT_ITEM_COUNT=$(mysql_query_local "$CHARACTERS_DB" "SELECT COUNT(*) FROM item_instance WHERE owner_guid IN ($CHAR_GUIDS);")
|
||||
info "Current items for imported characters: $CURRENT_ITEM_COUNT"
|
||||
|
||||
if [[ "$CURRENT_ITEM_COUNT" != "0" ]]; then
|
||||
@@ -94,26 +205,26 @@ log "Creating staging database..."
|
||||
STAGE_CHARS_DB="fix_stage_chars_$$"
|
||||
|
||||
# Drop any existing staging database
|
||||
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -e "DROP DATABASE IF EXISTS $STAGE_CHARS_DB;" 2>/dev/null || true
|
||||
docker exec "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" -e "DROP DATABASE IF EXISTS $STAGE_CHARS_DB;" 2>/dev/null || true
|
||||
|
||||
# Create staging database
|
||||
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -e "CREATE DATABASE $STAGE_CHARS_DB;" 2>/dev/null
|
||||
docker exec "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" -e "CREATE DATABASE $STAGE_CHARS_DB;" 2>/dev/null
|
||||
|
||||
# Cleanup staging database on exit
|
||||
cleanup_staging(){
|
||||
if [[ -n "${STAGE_CHARS_DB:-}" ]]; then
|
||||
docker exec ac-mysql mysql -uroot -p"$MYSQL_PW" -e "DROP DATABASE IF EXISTS $STAGE_CHARS_DB;" 2>/dev/null || true
|
||||
docker exec "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" -e "DROP DATABASE IF EXISTS $STAGE_CHARS_DB;" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap 'cleanup_staging; rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
# Load backup into staging database
|
||||
info "Loading backup into staging database..."
|
||||
sed "s/\`acore_characters\`/\`$STAGE_CHARS_DB\`/g; s/USE \`acore_characters\`;/USE \`$STAGE_CHARS_DB\`;/g" "$TEMP_DIR/characters.sql" | \
|
||||
docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" 2>/dev/null
|
||||
sed "s/\`$CHARACTERS_DB\`/\`$STAGE_CHARS_DB\`/g; s/USE \`$CHARACTERS_DB\`;/USE \`$STAGE_CHARS_DB\`;/g" "$TEMP_DIR/characters.sql" | \
|
||||
docker exec -i "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" 2>/dev/null
|
||||
|
||||
# Get current database state
|
||||
CURRENT_MAX_ITEM_GUID=$(mysql_query "$CHARACTERS_DB" "SELECT COALESCE(MAX(guid), 0) FROM item_instance;")
|
||||
CURRENT_MAX_ITEM_GUID=$(mysql_query_local "$CHARACTERS_DB" "SELECT COALESCE(MAX(guid), 0) FROM item_instance;")
|
||||
ITEM_OFFSET=$((CURRENT_MAX_ITEM_GUID + 10000))
|
||||
|
||||
info "Current max item GUID: $CURRENT_MAX_ITEM_GUID"
|
||||
@@ -121,22 +232,32 @@ info "Item GUID offset: +$ITEM_OFFSET"
|
||||
|
||||
# Create character mapping for the imported characters
|
||||
log "Creating character mapping..."
|
||||
mysql_exec "$STAGE_CHARS_DB" <<EOF
|
||||
info "Building character GUID mapping from staging database..."
|
||||
|
||||
# Create mapping table dynamically based on imported characters
|
||||
mysql_exec_local "$STAGE_CHARS_DB" <<EOF
|
||||
CREATE TABLE character_guid_map (
|
||||
old_guid INT UNSIGNED PRIMARY KEY,
|
||||
new_guid INT UNSIGNED,
|
||||
name VARCHAR(12)
|
||||
);
|
||||
EOF
|
||||
|
||||
# Populate mapping by matching character names from staging to current database
|
||||
# This assumes character names are unique identifiers
|
||||
mysql_exec_local "$STAGE_CHARS_DB" <<EOF
|
||||
INSERT INTO character_guid_map (old_guid, new_guid, name)
|
||||
VALUES
|
||||
(1, 4501, 'Artimage'),
|
||||
(2, 4502, 'Flombey'),
|
||||
(3, 4503, 'Hammertime');
|
||||
SELECT
|
||||
s.guid as old_guid,
|
||||
c.guid as new_guid,
|
||||
c.name
|
||||
FROM $STAGE_CHARS_DB.characters s
|
||||
JOIN $CHARACTERS_DB.characters c ON s.name = c.name
|
||||
WHERE c.account IN ($ACCOUNT_IDS);
|
||||
EOF
|
||||
|
||||
# Create item GUID mapping
|
||||
mysql_exec "$STAGE_CHARS_DB" <<EOF
|
||||
mysql_exec_local "$STAGE_CHARS_DB" <<EOF
|
||||
CREATE TABLE item_guid_map (
|
||||
old_guid INT UNSIGNED PRIMARY KEY,
|
||||
new_guid INT UNSIGNED,
|
||||
@@ -153,7 +274,7 @@ INNER JOIN character_guid_map cm ON i.owner_guid = cm.old_guid;
|
||||
EOF
|
||||
|
||||
# Check how many items will be imported
|
||||
ITEMS_TO_IMPORT=$(mysql_query "$STAGE_CHARS_DB" "SELECT COUNT(*) FROM item_guid_map;")
|
||||
ITEMS_TO_IMPORT=$(mysql_query_local "$STAGE_CHARS_DB" "SELECT COUNT(*) FROM item_guid_map;")
|
||||
info "Items to import: $ITEMS_TO_IMPORT"
|
||||
|
||||
if [[ "$ITEMS_TO_IMPORT" == "0" ]]; then
|
||||
@@ -195,7 +316,7 @@ EOSQL
|
||||
)
|
||||
|
||||
ITEM_SQL_EXPANDED=$(echo "$ITEM_SQL" | sed "s/STAGE_CHARS_DB/$STAGE_CHARS_DB/g")
|
||||
ITEM_RESULT=$(echo "$ITEM_SQL_EXPANDED" | docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" 2>&1)
|
||||
ITEM_RESULT=$(echo "$ITEM_SQL_EXPANDED" | docker exec -i "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" 2>&1)
|
||||
if echo "$ITEM_RESULT" | grep -q "ERROR"; then
|
||||
err "Item import failed:"
|
||||
echo "$ITEM_RESULT" | grep "ERROR" >&2
|
||||
@@ -217,7 +338,7 @@ EOSQL
|
||||
)
|
||||
|
||||
INV_SQL_EXPANDED=$(echo "$INV_SQL" | sed "s/STAGE_CHARS_DB/$STAGE_CHARS_DB/g")
|
||||
INV_RESULT=$(echo "$INV_SQL_EXPANDED" | docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" 2>&1)
|
||||
INV_RESULT=$(echo "$INV_SQL_EXPANDED" | docker exec -i "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" "$CHARACTERS_DB" 2>&1)
|
||||
if echo "$INV_RESULT" | grep -q "ERROR"; then
|
||||
err "Inventory import failed:"
|
||||
echo "$INV_RESULT" | grep "ERROR" >&2
|
||||
@@ -225,8 +346,8 @@ if echo "$INV_RESULT" | grep -q "ERROR"; then
|
||||
fi
|
||||
|
||||
# Report counts
|
||||
ITEMS_IMPORTED=$(mysql_query "$CHARACTERS_DB" "SELECT COUNT(*) FROM item_instance WHERE owner_guid IN (4501, 4502, 4503);")
|
||||
INV_IMPORTED=$(mysql_query "$CHARACTERS_DB" "SELECT COUNT(*) FROM character_inventory WHERE guid IN (4501, 4502, 4503);")
|
||||
ITEMS_IMPORTED=$(mysql_query_local "$CHARACTERS_DB" "SELECT COUNT(*) FROM item_instance WHERE owner_guid IN ($CHAR_GUIDS);")
|
||||
INV_IMPORTED=$(mysql_query_local "$CHARACTERS_DB" "SELECT COUNT(*) FROM character_inventory WHERE guid IN ($CHAR_GUIDS);")
|
||||
|
||||
info "Items imported: $ITEMS_IMPORTED"
|
||||
info "Inventory slots imported: $INV_IMPORTED"
|
||||
|
||||
423
scripts/bash/lib/common.sh
Normal file
423
scripts/bash/lib/common.sh
Normal file
@@ -0,0 +1,423 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Common utilities library for AzerothCore RealmMaster scripts
|
||||
# This library provides shared functions for environment variable reading,
|
||||
# logging, error handling, and other common operations.
|
||||
#
|
||||
# Usage: source /path/to/scripts/bash/lib/common.sh
|
||||
|
||||
# Prevent multiple sourcing
|
||||
if [ -n "${_COMMON_LIB_LOADED:-}" ]; then
|
||||
return 0
|
||||
fi
|
||||
_COMMON_LIB_LOADED=1
|
||||
|
||||
# =============================================================================
|
||||
# COLOR DEFINITIONS (Standardized across all scripts)
|
||||
# =============================================================================
|
||||
|
||||
BLUE='\033[0;34m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Legacy color names for backward compatibility
|
||||
COLOR_BLUE="$BLUE"
|
||||
COLOR_GREEN="$GREEN"
|
||||
COLOR_YELLOW="$YELLOW"
|
||||
COLOR_RED="$RED"
|
||||
COLOR_CYAN="$CYAN"
|
||||
COLOR_RESET="$NC"
|
||||
|
||||
# =============================================================================
|
||||
# LOGGING FUNCTIONS (Standardized with emoji)
|
||||
# =============================================================================
|
||||
|
||||
# Log informational messages (blue with info icon)
|
||||
info() {
|
||||
printf '%b\n' "${BLUE}ℹ️ $*${NC}"
|
||||
}
|
||||
|
||||
# Log success messages (green with checkmark)
|
||||
ok() {
|
||||
printf '%b\n' "${GREEN}✅ $*${NC}"
|
||||
}
|
||||
|
||||
# Log general messages (green, no icon - for clean output)
|
||||
log() {
|
||||
printf '%b\n' "${GREEN}$*${NC}"
|
||||
}
|
||||
|
||||
# Log warning messages (yellow with warning icon)
|
||||
warn() {
|
||||
printf '%b\n' "${YELLOW}⚠️ $*${NC}"
|
||||
}
|
||||
|
||||
# Log error messages (red with error icon, continues execution)
|
||||
err() {
|
||||
printf '%b\n' "${RED}❌ $*${NC}" >&2
|
||||
}
|
||||
|
||||
# Log fatal error and exit (red with error icon, exits with code 1)
|
||||
fatal() {
|
||||
printf '%b\n' "${RED}❌ $*${NC}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# ENVIRONMENT VARIABLE READING
|
||||
# =============================================================================
|
||||
|
||||
# Read environment variable from .env file with fallback to default
|
||||
# Handles various quote styles, comments, and whitespace
|
||||
#
|
||||
# Usage:
|
||||
# read_env KEY [DEFAULT_VALUE]
|
||||
# value=$(read_env "MYSQL_PASSWORD" "default_password")
|
||||
#
|
||||
# Features:
|
||||
# - Reads from file specified by $ENV_PATH (or $DEFAULT_ENV_PATH)
|
||||
# - Strips leading/trailing whitespace
|
||||
# - Removes inline comments (everything after #)
|
||||
# - Handles double quotes, single quotes, and unquoted values
|
||||
# - Returns default value if key not found
|
||||
# - Returns value from environment variable if already set
|
||||
#
|
||||
read_env() {
|
||||
local key="$1"
|
||||
local default="${2:-}"
|
||||
local value=""
|
||||
|
||||
# Check if variable is already set in environment (takes precedence)
|
||||
if [ -n "${!key:-}" ]; then
|
||||
echo "${!key}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Determine which .env file to use
|
||||
local env_file="${ENV_PATH:-${DEFAULT_ENV_PATH:-}}"
|
||||
|
||||
# Read from .env file if it exists
|
||||
if [ -f "$env_file" ]; then
|
||||
# Extract value using grep and cut, handling various formats
|
||||
value="$(grep -E "^${key}=" "$env_file" 2>/dev/null | tail -n1 | cut -d'=' -f2- | tr -d '\r')"
|
||||
|
||||
# Remove inline comments (everything after # that's not inside quotes)
|
||||
# This is a simplified approach - doesn't handle quotes perfectly but works for most cases
|
||||
value="$(echo "$value" | sed 's/[[:space:]]*#.*//' | sed 's/[[:space:]]*$//')"
|
||||
|
||||
# Strip quotes if present
|
||||
if [[ "$value" == \"*\" && "$value" == *\" ]]; then
|
||||
# Double quotes
|
||||
value="${value:1:-1}"
|
||||
elif [[ "$value" == \'*\' && "$value" == *\' ]]; then
|
||||
# Single quotes
|
||||
value="${value:1:-1}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use default if still empty
|
||||
if [ -z "${value:-}" ]; then
|
||||
value="$default"
|
||||
fi
|
||||
|
||||
printf '%s\n' "${value}"
|
||||
}
|
||||
|
||||
# Read value from .env.template file (used during setup)
|
||||
# This is similar to read_env but specifically for template files
|
||||
#
|
||||
# Usage:
|
||||
# get_template_value KEY [TEMPLATE_FILE]
|
||||
# value=$(get_template_value "MYSQL_PASSWORD")
|
||||
#
|
||||
get_template_value() {
|
||||
local key="$1"
|
||||
local template_file="${2:-${TEMPLATE_FILE:-${TEMPLATE_PATH:-.env.template}}}"
|
||||
|
||||
if [ ! -f "$template_file" ]; then
|
||||
fatal "Template file not found: $template_file"
|
||||
fi
|
||||
|
||||
# Extract value, handling variable expansion syntax like ${VAR:-default}
|
||||
local value
|
||||
local raw_line
|
||||
raw_line=$(grep "^${key}=" "$template_file" 2>/dev/null | head -1)
|
||||
|
||||
if [ -z "$raw_line" ]; then
|
||||
err "Key '$key' not found in template: $template_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
value="${raw_line#*=}"
|
||||
value=$(echo "$value" | sed 's/^"\(.*\)"$/\1/')
|
||||
|
||||
# Handle ${VAR:-default} syntax by extracting the default value
|
||||
if [[ "$value" =~ ^\$\{[^}]*:-([^}]*)\}$ ]]; then
|
||||
value="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
# Update or add environment variable in .env file
|
||||
# Creates file if it doesn't exist
|
||||
#
|
||||
# Usage:
|
||||
# update_env_value KEY VALUE [ENV_FILE]
|
||||
# update_env_value "MYSQL_PASSWORD" "new_password"
|
||||
#
|
||||
update_env_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local env_file="${3:-${ENV_PATH:-${DEFAULT_ENV_PATH:-.env}}}"
|
||||
|
||||
[ -n "$env_file" ] || return 0
|
||||
|
||||
# Create file if it doesn't exist
|
||||
if [ ! -f "$env_file" ]; then
|
||||
printf '%s=%s\n' "$key" "$value" >> "$env_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Update existing or append new
|
||||
if grep -q "^${key}=" "$env_file"; then
|
||||
# Use platform-appropriate sed in-place editing
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^${key}=.*|${key}=${value}|" "$env_file"
|
||||
else
|
||||
sed -i "s|^${key}=.*|${key}=${value}|" "$env_file"
|
||||
fi
|
||||
else
|
||||
printf '\n%s=%s\n' "$key" "$value" >> "$env_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# VALIDATION & REQUIREMENTS
|
||||
# =============================================================================
|
||||
|
||||
# Require command to be available in PATH, exit with error if not found
|
||||
#
|
||||
# Usage:
|
||||
# require_cmd docker
|
||||
# require_cmd python3 jq git
|
||||
#
|
||||
require_cmd() {
|
||||
for cmd in "$@"; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
fatal "Missing required command: $cmd"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Check if command exists (returns 0 if exists, 1 if not)
|
||||
#
|
||||
# Usage:
|
||||
# if has_cmd docker; then
|
||||
# echo "Docker is available"
|
||||
# fi
|
||||
#
|
||||
has_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MYSQL/DATABASE HELPERS
|
||||
# =============================================================================
|
||||
|
||||
# Execute MySQL command in Docker container
|
||||
# Reads MYSQL_PW and container name from environment
|
||||
#
|
||||
# Usage:
|
||||
# mysql_exec DATABASE_NAME < script.sql
|
||||
# echo "SELECT 1;" | mysql_exec acore_auth
|
||||
#
|
||||
mysql_exec() {
|
||||
local db="$1"
|
||||
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
|
||||
local container="${MYSQL_CONTAINER:-ac-mysql}"
|
||||
|
||||
docker exec -i "$container" mysql -uroot -p"$mysql_pw" "$db"
|
||||
}
|
||||
|
||||
# Execute MySQL query and return result
|
||||
# Outputs in non-tabular format suitable for parsing
|
||||
#
|
||||
# Usage:
|
||||
# count=$(mysql_query "acore_characters" "SELECT COUNT(*) FROM characters")
|
||||
#
|
||||
mysql_query() {
|
||||
local db="$1"
|
||||
local query="$2"
|
||||
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
|
||||
local container="${MYSQL_CONTAINER:-ac-mysql}"
|
||||
|
||||
docker exec "$container" mysql -uroot -p"$mysql_pw" -N -B "$db" -e "$query" 2>/dev/null
|
||||
}
|
||||
|
||||
# Check if MySQL container is healthy and accepting connections
|
||||
#
|
||||
# Usage:
|
||||
# if mysql_is_ready; then
|
||||
# echo "MySQL is ready"
|
||||
# fi
|
||||
#
|
||||
mysql_is_ready() {
|
||||
local container="${MYSQL_CONTAINER:-ac-mysql}"
|
||||
local mysql_pw="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
|
||||
|
||||
docker exec "$container" mysqladmin ping -uroot -p"$mysql_pw" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Wait for MySQL to be ready with timeout
|
||||
#
|
||||
# Usage:
|
||||
# mysql_wait_ready 60 # Wait up to 60 seconds
|
||||
#
|
||||
mysql_wait_ready() {
|
||||
local timeout="${1:-30}"
|
||||
local elapsed=0
|
||||
|
||||
info "Waiting for MySQL to be ready..."
|
||||
|
||||
while [ $elapsed -lt $timeout ]; do
|
||||
if mysql_is_ready; then
|
||||
ok "MySQL is ready"
|
||||
return 0
|
||||
fi
|
||||
sleep 2
|
||||
elapsed=$((elapsed + 2))
|
||||
done
|
||||
|
||||
err "MySQL did not become ready within ${timeout}s"
|
||||
return 1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# FILE & DIRECTORY HELPERS
|
||||
# =============================================================================
|
||||
|
||||
# Ensure directory exists and is writable
|
||||
# Creates directory if needed and sets permissions
|
||||
#
|
||||
# Usage:
|
||||
# ensure_writable_dir /path/to/directory
|
||||
#
|
||||
ensure_writable_dir() {
|
||||
local dir="$1"
|
||||
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir -p "$dir" 2>/dev/null || {
|
||||
err "Failed to create directory: $dir"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
if [ ! -w "$dir" ]; then
|
||||
chmod u+w "$dir" 2>/dev/null || {
|
||||
err "Directory not writable: $dir"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Create backup of file before modification
|
||||
#
|
||||
# Usage:
|
||||
# backup_file /path/to/important.conf
|
||||
# # Creates /path/to/important.conf.backup.TIMESTAMP
|
||||
#
|
||||
backup_file() {
|
||||
local file="$1"
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
warn "File does not exist, skipping backup: $file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local backup="${file}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$file" "$backup" || {
|
||||
err "Failed to create backup: $backup"
|
||||
return 1
|
||||
}
|
||||
|
||||
info "Created backup: $backup"
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# GIT HELPERS
|
||||
# =============================================================================
|
||||
|
||||
# Configure git identity if not already set
|
||||
#
|
||||
# Usage:
|
||||
# setup_git_config [USERNAME] [EMAIL]
|
||||
#
|
||||
setup_git_config() {
|
||||
local git_user="${1:-${GIT_USERNAME:-AzerothCore RealmMaster}}"
|
||||
local git_email="${2:-${GIT_EMAIL:-noreply@azerothcore.org}}"
|
||||
|
||||
if ! git config --global user.name >/dev/null 2>&1; then
|
||||
info "Configuring git identity: $git_user <$git_email>"
|
||||
git config --global user.name "$git_user" || true
|
||||
git config --global user.email "$git_email" || true
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# ERROR HANDLING UTILITIES
|
||||
# =============================================================================
|
||||
|
||||
# Retry command with exponential backoff
|
||||
#
|
||||
# Usage:
|
||||
# retry 5 docker pull myimage:latest
|
||||
# retry 3 2 mysql_query "acore_auth" "SELECT 1" # 3 retries with 2s initial delay
|
||||
#
|
||||
retry() {
|
||||
local max_attempts="$1"
|
||||
shift
|
||||
local delay="${1:-1}"
|
||||
|
||||
# Check if delay is a number, if not treat it as part of the command
|
||||
if ! [[ "$delay" =~ ^[0-9]+$ ]]; then
|
||||
delay=1
|
||||
else
|
||||
shift
|
||||
fi
|
||||
|
||||
local attempt=1
|
||||
local exit_code=0
|
||||
|
||||
while [ $attempt -le "$max_attempts" ]; do
|
||||
if "$@"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
exit_code=$?
|
||||
|
||||
if [ $attempt -lt "$max_attempts" ]; then
|
||||
warn "Command failed (attempt $attempt/$max_attempts), retrying in ${delay}s..."
|
||||
sleep "$delay"
|
||||
delay=$((delay * 2)) # Exponential backoff
|
||||
fi
|
||||
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
err "Command failed after $max_attempts attempts"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# INITIALIZATION
|
||||
# =============================================================================
|
||||
|
||||
# Library loaded successfully
|
||||
# Scripts can check for $_COMMON_LIB_LOADED to verify library is loaded
|
||||
@@ -7,52 +7,36 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Source common library for shared functions
|
||||
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
|
||||
source "$SCRIPT_DIR/lib/common.sh"
|
||||
else
|
||||
echo "ERROR: Common library not found at $SCRIPT_DIR/lib/common.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Source project name helper
|
||||
source "$PROJECT_ROOT/scripts/bash/project_name.sh"
|
||||
|
||||
# Module-specific configuration
|
||||
MODULE_HELPER="$PROJECT_ROOT/scripts/python/modules.py"
|
||||
DEFAULT_ENV_PATH="$PROJECT_ROOT/.env"
|
||||
ENV_PATH="${MODULES_ENV_PATH:-$DEFAULT_ENV_PATH}"
|
||||
TEMPLATE_FILE="$PROJECT_ROOT/.env.template"
|
||||
source "$PROJECT_ROOT/scripts/bash/project_name.sh"
|
||||
|
||||
# Default project name (read from .env or template)
|
||||
DEFAULT_PROJECT_NAME="$(project_name::resolve "$ENV_PATH" "$TEMPLATE_FILE")"
|
||||
|
||||
BLUE='\033[0;34m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
|
||||
# Module-specific state
|
||||
PLAYERBOTS_DB_UPDATE_LOGGED=0
|
||||
info(){ printf '%b\n' "${BLUE}ℹ️ $*${NC}"; }
|
||||
ok(){ printf '%b\n' "${GREEN}✅ $*${NC}"; }
|
||||
warn(){ printf '%b\n' "${YELLOW}⚠️ $*${NC}"; }
|
||||
err(){ printf '%b\n' "${RED}❌ $*${NC}"; exit 1; }
|
||||
|
||||
# Declare module metadata arrays globally at script level
|
||||
declare -A MODULE_NAME MODULE_REPO MODULE_REF MODULE_TYPE MODULE_ENABLED MODULE_NEEDS_BUILD MODULE_BLOCKED MODULE_POST_INSTALL MODULE_REQUIRES MODULE_CONFIG_CLEANUP MODULE_NOTES MODULE_STATUS MODULE_BLOCK_REASON
|
||||
declare -a MODULE_KEYS
|
||||
|
||||
read_env_value(){
|
||||
local key="$1" default="${2:-}" value="${!key:-}"
|
||||
if [ -n "$value" ]; then
|
||||
echo "$value"
|
||||
return
|
||||
fi
|
||||
if [ -f "$ENV_PATH" ]; then
|
||||
value="$(grep -E "^${key}=" "$ENV_PATH" 2>/dev/null | tail -n1 | cut -d'=' -f2- | tr -d '\r')"
|
||||
value="$(echo "$value" | sed 's/[[:space:]]*#.*//' | sed 's/[[:space:]]*$//')"
|
||||
if [[ "$value" == \"*\" && "$value" == *\" ]]; then
|
||||
value="${value:1:-1}"
|
||||
elif [[ "$value" == \'*\' && "$value" == *\' ]]; then
|
||||
value="${value:1:-1}"
|
||||
fi
|
||||
fi
|
||||
if [ -z "${value:-}" ]; then
|
||||
value="$default"
|
||||
fi
|
||||
printf '%s\n' "${value}"
|
||||
}
|
||||
|
||||
ensure_python(){
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
err "python3 is required but not installed in PATH"
|
||||
fi
|
||||
}
|
||||
# Ensure Python is available
|
||||
require_cmd python3
|
||||
|
||||
resolve_manifest_path(){
|
||||
if [ -n "${MODULES_MANIFEST_PATH:-}" ] && [ -f "${MODULES_MANIFEST_PATH}" ]; then
|
||||
@@ -567,10 +551,10 @@ track_module_state(){
|
||||
}
|
||||
|
||||
main(){
|
||||
ensure_python
|
||||
# Python is already checked at script start via require_cmd
|
||||
|
||||
if [ "${MODULES_LOCAL_RUN:-0}" != "1" ]; then
|
||||
cd /modules || err "Modules directory /modules not found"
|
||||
cd /modules || fatal "Modules directory /modules not found"
|
||||
fi
|
||||
MODULES_ROOT="$(pwd)"
|
||||
|
||||
|
||||
@@ -39,10 +39,6 @@ ensure_host_writable(){
|
||||
fi
|
||||
}
|
||||
|
||||
seed_sql_ledger_if_needed(){
|
||||
: # No-op; ledger removed
|
||||
}
|
||||
|
||||
sync_local_staging(){
|
||||
local src_root="$LOCAL_STORAGE_PATH"
|
||||
local dest_root="$STORAGE_PATH"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -31,54 +31,127 @@ def parse_bool(value: str) -> bool:
|
||||
|
||||
|
||||
def load_env_file(env_path: Path) -> Dict[str, str]:
|
||||
"""
|
||||
Load environment variables from .env file.
|
||||
|
||||
Args:
|
||||
env_path: Path to .env file
|
||||
|
||||
Returns:
|
||||
Dictionary of environment variable key-value pairs
|
||||
|
||||
Note:
|
||||
Returns empty dict if file doesn't exist (not an error).
|
||||
Handles quotes, comments, and export statements.
|
||||
"""
|
||||
if not env_path.exists():
|
||||
return {}
|
||||
|
||||
env: Dict[str, str] = {}
|
||||
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
||||
|
||||
try:
|
||||
content = env_path.read_text(encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to read environment file {env_path}: {e}", file=sys.stderr)
|
||||
return {}
|
||||
|
||||
for line_num, raw_line in enumerate(content.splitlines(), start=1):
|
||||
line = raw_line.strip()
|
||||
|
||||
# Skip empty lines and comments
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
# Remove 'export' prefix if present
|
||||
if line.startswith("export "):
|
||||
line = line[len("export ") :].strip()
|
||||
|
||||
# Skip lines without '='
|
||||
if "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if value.startswith('"') and value.endswith('"'):
|
||||
value = value[1:-1]
|
||||
elif value.startswith("'") and value.endswith("'"):
|
||||
value = value[1:-1]
|
||||
env[key] = value
|
||||
|
||||
try:
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
# Strip quotes
|
||||
if value.startswith('"') and value.endswith('"'):
|
||||
value = value[1:-1]
|
||||
elif value.startswith("'") and value.endswith("'"):
|
||||
value = value[1:-1]
|
||||
|
||||
env[key] = value
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Warning: Failed to parse line {line_num} in {env_path}: {raw_line}\n"
|
||||
f" Error: {e}",
|
||||
file=sys.stderr
|
||||
)
|
||||
continue
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def load_manifest(manifest_path: Path) -> List[Dict[str, object]]:
|
||||
"""
|
||||
Load and validate module manifest from JSON file.
|
||||
|
||||
Args:
|
||||
manifest_path: Path to module-manifest.json file
|
||||
|
||||
Returns:
|
||||
List of validated module dictionaries
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If manifest file doesn't exist
|
||||
json.JSONDecodeError: If manifest is not valid JSON
|
||||
ValueError: If manifest structure is invalid
|
||||
"""
|
||||
if not manifest_path.exists():
|
||||
raise FileNotFoundError(f"Manifest file not found: {manifest_path}")
|
||||
with manifest_path.open("r", encoding="utf-8") as fh:
|
||||
manifest = json.load(fh)
|
||||
|
||||
try:
|
||||
with manifest_path.open("r", encoding="utf-8") as fh:
|
||||
manifest = json.load(fh)
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(
|
||||
f"Invalid JSON in manifest file {manifest_path}:\n"
|
||||
f" Line {e.lineno}, Column {e.colno}: {e.msg}"
|
||||
) from e
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to read manifest file {manifest_path}: {e}") from e
|
||||
|
||||
modules = manifest.get("modules")
|
||||
if not isinstance(modules, list):
|
||||
raise ValueError("Manifest must define a top-level 'modules' array")
|
||||
|
||||
validated: List[Dict[str, object]] = []
|
||||
seen_keys: set[str] = set()
|
||||
for entry in modules:
|
||||
|
||||
for idx, entry in enumerate(modules):
|
||||
if not isinstance(entry, dict):
|
||||
raise ValueError("Each manifest entry must be an object")
|
||||
raise ValueError(f"Manifest entry at index {idx} must be an object")
|
||||
|
||||
key = entry.get("key")
|
||||
name = entry.get("name")
|
||||
repo = entry.get("repo")
|
||||
|
||||
if not key or not isinstance(key, str):
|
||||
raise ValueError("Manifest entry missing 'key'")
|
||||
raise ValueError(f"Manifest entry at index {idx} missing 'key'")
|
||||
|
||||
if key in seen_keys:
|
||||
raise ValueError(f"Duplicate manifest key detected: {key}")
|
||||
raise ValueError(f"Duplicate manifest key detected: '{key}' (at index {idx})")
|
||||
seen_keys.add(key)
|
||||
|
||||
if not name or not isinstance(name, str):
|
||||
raise ValueError(f"Manifest entry {key} missing 'name'")
|
||||
raise ValueError(f"Manifest entry '{key}' missing 'name' field")
|
||||
|
||||
if not repo or not isinstance(repo, str):
|
||||
raise ValueError(f"Manifest entry {key} missing 'repo'")
|
||||
raise ValueError(f"Manifest entry '{key}' missing 'repo' field")
|
||||
|
||||
validated.append(entry)
|
||||
|
||||
return validated
|
||||
|
||||
|
||||
|
||||
182
scripts/python/report_missing_modules.py
Normal file
182
scripts/python/report_missing_modules.py
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a categorized list of GitHub modules missing from the manifest.
|
||||
|
||||
The script reuses the discovery logic from ``update_module_manifest.py`` to
|
||||
fetch repositories by topic, filters out entries already tracked in
|
||||
``config/module-manifest.json`` and writes the remainder (including type,
|
||||
category, and inferred dependency hints) to a JSON file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List, Sequence, Tuple
|
||||
|
||||
from update_module_manifest import ( # type: ignore
|
||||
CATEGORY_BY_TYPE,
|
||||
DEFAULT_TOPICS,
|
||||
GitHubClient,
|
||||
collect_repositories,
|
||||
load_manifest,
|
||||
normalize_repo_url,
|
||||
repo_name_to_key,
|
||||
)
|
||||
|
||||
# heuristics used to surface potential dependency hints
|
||||
DEPENDENCY_KEYWORDS: Tuple[Tuple[str, str], ...] = (
|
||||
("playerbot", "MODULE_PLAYERBOTS"),
|
||||
("ah-bot", "MODULE_PLAYERBOTS"),
|
||||
("eluna", "MODULE_ELUNA"),
|
||||
)
|
||||
|
||||
# keywords that help categorize entries that should probably stay hidden by default
|
||||
SUPPRESSION_KEYWORDS: Tuple[Tuple[str, str], ...] = (
|
||||
("virtual machine", "vm"),
|
||||
(" vm ", "vm"),
|
||||
(" docker", "docker"),
|
||||
("container", "docker"),
|
||||
("vagrant", "vagrant"),
|
||||
("ansible", "automation"),
|
||||
("terraform", "automation"),
|
||||
("client", "client-distribution"),
|
||||
("launcher", "client-distribution"),
|
||||
)
|
||||
|
||||
|
||||
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--manifest",
|
||||
default="config/module-manifest.json",
|
||||
help="Path to module manifest JSON (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
default="missing-modules.json",
|
||||
help="Path to write the missing-module report JSON (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--topic",
|
||||
action="append",
|
||||
default=[],
|
||||
dest="topics",
|
||||
help="GitHub topic (or '+' expression) to scan (defaults to built-in list).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-pages",
|
||||
type=int,
|
||||
default=10,
|
||||
help="Maximum pages (x100 results) to fetch per topic (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--token",
|
||||
help="GitHub API token (defaults to $GITHUB_TOKEN or $GITHUB_API_TOKEN)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log",
|
||||
action="store_true",
|
||||
help="Print verbose progress information",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def implied_dependencies(module_type: str, text: str) -> List[str]:
|
||||
deps: List[str] = []
|
||||
if module_type == "lua":
|
||||
deps.append("MODULE_ELUNA")
|
||||
normalized = text.lower()
|
||||
for keyword, dep in DEPENDENCY_KEYWORDS:
|
||||
if keyword in normalized and dep not in deps:
|
||||
deps.append(dep)
|
||||
return deps
|
||||
|
||||
|
||||
def suppression_flags(category: str, text: str) -> List[str]:
|
||||
flags: List[str] = []
|
||||
if category == "tooling":
|
||||
flags.append("tooling")
|
||||
normalized = text.lower()
|
||||
for keyword, flag in SUPPRESSION_KEYWORDS:
|
||||
if keyword in normalized and flag not in flags:
|
||||
flags.append(flag)
|
||||
return flags
|
||||
|
||||
|
||||
def make_missing_entries(
|
||||
manifest_modules: List[dict],
|
||||
repos: Iterable,
|
||||
) -> List[dict]:
|
||||
by_key: Dict[str, dict] = {module.get("key"): module for module in manifest_modules if module.get("key")}
|
||||
by_repo: Dict[str, dict] = {
|
||||
normalize_repo_url(str(module.get("repo", ""))): module
|
||||
for module in manifest_modules
|
||||
if module.get("repo")
|
||||
}
|
||||
missing: List[dict] = []
|
||||
|
||||
for record in repos:
|
||||
repo = record.data
|
||||
repo_url = normalize_repo_url(repo.get("clone_url") or repo.get("html_url") or "")
|
||||
existing = by_repo.get(repo_url)
|
||||
key = repo_name_to_key(repo.get("name", ""))
|
||||
if not existing:
|
||||
existing = by_key.get(key)
|
||||
if existing:
|
||||
continue
|
||||
module_type = record.module_type
|
||||
category = CATEGORY_BY_TYPE.get(module_type, "uncategorized")
|
||||
description = repo.get("description") or ""
|
||||
combined_text = " ".join(
|
||||
filter(
|
||||
None,
|
||||
[
|
||||
repo.get("full_name"),
|
||||
description,
|
||||
" ".join(repo.get("topics") or []),
|
||||
],
|
||||
)
|
||||
)
|
||||
entry = {
|
||||
"key": key,
|
||||
"repo_name": repo.get("full_name"),
|
||||
"topic": record.topic_expr,
|
||||
"repo_url": repo.get("html_url") or repo.get("clone_url"),
|
||||
"description": description,
|
||||
"topics": repo.get("topics") or [],
|
||||
"type": module_type,
|
||||
"category": category,
|
||||
"implied_dependencies": implied_dependencies(module_type, combined_text),
|
||||
"flags": suppression_flags(category, combined_text),
|
||||
}
|
||||
missing.append(entry)
|
||||
missing.sort(key=lambda item: item["key"])
|
||||
return missing
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> int:
|
||||
args = parse_args(argv)
|
||||
topics = args.topics or DEFAULT_TOPICS
|
||||
token = args.token or os.environ.get("GITHUB_TOKEN") or os.environ.get("GITHUB_API_TOKEN")
|
||||
if not token:
|
||||
print(
|
||||
"Warning: no GitHub token provided, falling back to anonymous rate limit",
|
||||
file=sys.stderr,
|
||||
)
|
||||
client = GitHubClient(token, verbose=args.log)
|
||||
|
||||
manifest = load_manifest(args.manifest)
|
||||
repos = collect_repositories(client, topics, args.max_pages)
|
||||
missing = make_missing_entries(manifest.get("modules", []), repos)
|
||||
|
||||
output_path = Path(args.output)
|
||||
output_path.write_text(json.dumps(missing, indent=2))
|
||||
print(f"Wrote {len(missing)} entries to {output_path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
35
setup.sh
35
setup.sh
@@ -518,6 +518,39 @@ auto_enable_module_dependencies() {
|
||||
done
|
||||
}
|
||||
|
||||
ensure_module_platforms() {
|
||||
local needs_platform=0
|
||||
local key
|
||||
for key in "${MODULE_KEYS[@]}"; do
|
||||
case "$key" in
|
||||
MODULE_ELUNA|MODULE_AIO) continue ;;
|
||||
esac
|
||||
local value
|
||||
eval "value=\${$key:-0}"
|
||||
if [ "$value" = "1" ]; then
|
||||
needs_platform=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$needs_platform" != "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local platform
|
||||
for platform in MODULE_ELUNA MODULE_AIO; do
|
||||
[ -n "${KNOWN_MODULE_LOOKUP[$platform]:-}" ] || continue
|
||||
local platform_value
|
||||
eval "platform_value=\${$platform:-0}"
|
||||
if [ "$platform_value" != "1" ]; then
|
||||
local platform_name="${MODULE_NAME_MAP[$platform]:-${platform#MODULE_}}"
|
||||
say INFO "Automatically enabling ${platform_name} to support selected modules."
|
||||
printf -v "$platform" '%s' "1"
|
||||
MODULE_ENABLE_SET["$platform"]=1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
show_realm_configured(){
|
||||
echo -e "\n${GREEN}⚔️ Your realm configuration has been forged! ⚔️${NC}"
|
||||
@@ -1160,6 +1193,7 @@ fi
|
||||
done
|
||||
|
||||
auto_enable_module_dependencies
|
||||
ensure_module_platforms
|
||||
|
||||
if [ "${MODULE_OLLAMA_CHAT:-0}" = "1" ] && [ "${MODULE_PLAYERBOTS:-0}" != "1" ]; then
|
||||
say INFO "Automatically enabling MODULE_PLAYERBOTS for MODULE_OLLAMA_CHAT."
|
||||
@@ -1326,6 +1360,7 @@ fi
|
||||
fi
|
||||
|
||||
auto_enable_module_dependencies
|
||||
ensure_module_platforms
|
||||
|
||||
if [ -n "$CLI_PLAYERBOT_ENABLED" ]; then
|
||||
if [[ "$CLI_PLAYERBOT_ENABLED" != "0" && "$CLI_PLAYERBOT_ENABLED" != "1" ]]; then
|
||||
|
||||
350
updates-dry-run.json
Normal file
350
updates-dry-run.json
Normal file
@@ -0,0 +1,350 @@
|
||||
[
|
||||
{
|
||||
"key": "MODULE_INDIVIDUAL_PROGRESSION",
|
||||
"repo_name": "ZhengPeiRu21/mod-individual-progression",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/ZhengPeiRu21/mod-individual-progression"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_PLAYERBOTS",
|
||||
"repo_name": "mod-playerbots/mod-playerbots",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/mod-playerbots/mod-playerbots"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_OLLAMA_CHAT",
|
||||
"repo_name": "DustinHendrickson/mod-ollama-chat",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/DustinHendrickson/mod-ollama-chat"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_PLAYER_BOT_LEVEL_BRACKETS",
|
||||
"repo_name": "DustinHendrickson/mod-player-bot-level-brackets",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/DustinHendrickson/mod-player-bot-level-brackets"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_DUEL_RESET",
|
||||
"repo_name": "azerothcore/mod-duel-reset",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-duel-reset"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_AOE_LOOT",
|
||||
"repo_name": "azerothcore/mod-aoe-loot",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-aoe-loot"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TIC_TAC_TOE",
|
||||
"repo_name": "azerothcore/mod-tic-tac-toe",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-tic-tac-toe"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_NPC_BEASTMASTER",
|
||||
"repo_name": "azerothcore/mod-npc-beastmaster",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-npc-beastmaster"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_MORPHSUMMON",
|
||||
"repo_name": "azerothcore/mod-morphsummon",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-morphsummon"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_WORGOBLIN",
|
||||
"repo_name": "heyitsbench/mod-worgoblin",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/heyitsbench/mod-worgoblin"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_SKELETON_MODULE",
|
||||
"repo_name": "azerothcore/skeleton-module",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/skeleton-module"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_AUTOBALANCE",
|
||||
"repo_name": "azerothcore/mod-autobalance",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-autobalance"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TRANSMOG",
|
||||
"repo_name": "azerothcore/mod-transmog",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-transmog"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ARAC",
|
||||
"repo_name": "heyitsbench/mod-arac",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/heyitsbench/mod-arac"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_GLOBAL_CHAT",
|
||||
"repo_name": "azerothcore/mod-global-chat",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-global-chat"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_PRESTIGE_DRAFT_MODE",
|
||||
"repo_name": "Youpeoples/Prestige-and-Draft-Mode",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/Youpeoples/Prestige-and-Draft-Mode"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_BLACK_MARKET_AUCTION_HOUSE",
|
||||
"repo_name": "Youpeoples/Black-Market-Auction-House",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/Youpeoples/Black-Market-Auction-House"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ULTIMATE_FULL_LOOT_PVP",
|
||||
"repo_name": "Youpeoples/Ultimate-Full-Loot-Pvp",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/Youpeoples/Ultimate-Full-Loot-Pvp"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_SERVER_AUTO_SHUTDOWN",
|
||||
"repo_name": "azerothcore/mod-server-auto-shutdown",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-server-auto-shutdown"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TIME_IS_TIME",
|
||||
"repo_name": "dunjeon/mod-TimeIsTime",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/dunjeon/mod-TimeIsTime"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_WAR_EFFORT",
|
||||
"repo_name": "azerothcore/mod-war-effort",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-war-effort"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_FIREWORKS",
|
||||
"repo_name": "azerothcore/mod-fireworks-on-level",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-fireworks-on-level"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_NPC_ENCHANTER",
|
||||
"repo_name": "azerothcore/mod-npc-enchanter",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-npc-enchanter"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_NPC_BUFFER",
|
||||
"repo_name": "azerothcore/mod-npc-buffer",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-npc-buffer"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_PVP_TITLES",
|
||||
"repo_name": "azerothcore/mod-pvp-titles",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-pvp-titles"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_CHALLENGE_MODES",
|
||||
"repo_name": "ZhengPeiRu21/mod-challenge-modes",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/ZhengPeiRu21/mod-challenge-modes"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TREASURE_CHEST_SYSTEM",
|
||||
"repo_name": "zyggy123/Treasure-Chest-System",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/zyggy123/Treasure-Chest-System"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ASSISTANT",
|
||||
"repo_name": "noisiver/mod-assistant",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/noisiver/mod-assistant"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_STATBOOSTER",
|
||||
"repo_name": "AnchyDev/StatBooster",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/AnchyDev/StatBooster"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_BG_SLAVERYVALLEY",
|
||||
"repo_name": "Helias/mod-bg-slaveryvalley",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/Helias/mod-bg-slaveryvalley"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_REAGENT_BANK",
|
||||
"repo_name": "ZhengPeiRu21/mod-reagent-bank",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/ZhengPeiRu21/mod-reagent-bank"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ELUNA_TS",
|
||||
"repo_name": "azerothcore/eluna-ts",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/eluna-ts"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_AZEROTHSHARD",
|
||||
"repo_name": "azerothcore/mod-azerothshard",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/azerothcore/mod-azerothshard"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_LEVEL_GRANT",
|
||||
"repo_name": "michaeldelago/mod-quest-count-level",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/michaeldelago/mod-quest-count-level"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_DUNGEON_RESPAWN",
|
||||
"repo_name": "AnchyDev/DungeonRespawn",
|
||||
"topic": "azerothcore-module",
|
||||
"repo_url": "https://github.com/AnchyDev/DungeonRespawn"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_LUA_AH_BOT",
|
||||
"repo_name": "mostlynick3/azerothcore-lua-ah-bot",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/mostlynick3/azerothcore-lua-ah-bot"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ACCOUNTWIDE_SYSTEMS",
|
||||
"repo_name": "Aldori15/azerothcore-eluna-accountwide",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Aldori15/azerothcore-eluna-accountwide"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ELUNA_SCRIPTS",
|
||||
"repo_name": "Isidorsson/Eluna-scripts",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Isidorsson/Eluna-scripts"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TRANSMOG_AIO",
|
||||
"repo_name": "DanieltheDeveloper/azerothcore-transmog-3.3.5a",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/DanieltheDeveloper/azerothcore-transmog-3.3.5a"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_HARDCORE_MODE",
|
||||
"repo_name": "PrivateDonut/hardcore_mode",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/PrivateDonut/hardcore_mode"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_RECRUIT_A_FRIEND",
|
||||
"repo_name": "55Honey/Acore_RecruitAFriend",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_RecruitAFriend"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_EVENT_SCRIPTS",
|
||||
"repo_name": "55Honey/Acore_eventScripts",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_eventScripts"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_LOTTERY_LUA",
|
||||
"repo_name": "zyggy123/lottery-lua",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/zyggy123/lottery-lua"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_HORADRIC_CUBE",
|
||||
"repo_name": "TITIaio/Horadric-Cube-for-World-of-Warcraft",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/TITIaio/Horadric-Cube-for-World-of-Warcraft"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_GLOBAL_MAIL_BANKING_AUCTIONS",
|
||||
"repo_name": "Aldori15/azerothcore-global-mail_banking_auctions",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Aldori15/azerothcore-global-mail_banking_auctions"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_LEVEL_UP_REWARD",
|
||||
"repo_name": "55Honey/Acore_LevelUpReward",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_LevelUpReward"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_AIO_BLACKJACK",
|
||||
"repo_name": "Manmadedrummer/AIO-Blackjack",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Manmadedrummer/AIO-Blackjack"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_NPCBOT_EXTENDED_COMMANDS",
|
||||
"repo_name": "Day36512/Npcbot_Extended_Commands",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Day36512/Npcbot_Extended_Commands"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ACTIVE_CHAT",
|
||||
"repo_name": "Day36512/ActiveChat",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Day36512/ActiveChat"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_MULTIVENDOR",
|
||||
"repo_name": "Shadowveil-WotLK/AzerothCore-lua-MultiVendor",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Shadowveil-WotLK/AzerothCore-lua-MultiVendor"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_EXCHANGE_NPC",
|
||||
"repo_name": "55Honey/Acore_ExchangeNpc",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_ExchangeNpc"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_DYNAMIC_TRADER",
|
||||
"repo_name": "Day36512/Dynamic-Trader",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/Day36512/Dynamic-Trader"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_DISCORD_NOTIFIER",
|
||||
"repo_name": "0xCiBeR/Acore_DiscordNotifier",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/0xCiBeR/Acore_DiscordNotifier"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_ZONE_CHECK",
|
||||
"repo_name": "55Honey/Acore_Zonecheck",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_Zonecheck"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_HARDCORE_MODE",
|
||||
"repo_name": "HellionOP/Lua-HardcoreMode",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/HellionOP/Lua-HardcoreMode"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_SEND_AND_BIND",
|
||||
"repo_name": "55Honey/Acore_SendAndBind",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_SendAndBind"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_TEMP_ANNOUNCEMENTS",
|
||||
"repo_name": "55Honey/Acore_TempAnnouncements",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_TempAnnouncements"
|
||||
},
|
||||
{
|
||||
"key": "MODULE_CARBON_COPY",
|
||||
"repo_name": "55Honey/Acore_CarbonCopy",
|
||||
"topic": "azerothcore-lua",
|
||||
"repo_url": "https://github.com/55Honey/Acore_CarbonCopy"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user