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
|
KEIRA3_EXTERNAL_PORT=4201
|
||||||
KEIRA_DATABASE_HOST=ac-mysql
|
KEIRA_DATABASE_HOST=ac-mysql
|
||||||
KEIRA_DATABASE_PORT=3306
|
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
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,10 +11,12 @@ local-storage/
|
|||||||
images/
|
images/
|
||||||
node_modules/
|
node_modules/
|
||||||
.mcp*/
|
.mcp*/
|
||||||
scripts/__pycache__/
|
scripts/__pycache__/*
|
||||||
|
scripts/python/__pycache__/*
|
||||||
.env
|
.env
|
||||||
package-lock.json
|
package-lock.json
|
||||||
package.json
|
package.json
|
||||||
todo.md
|
todo.md
|
||||||
.gocache/
|
.gocache/
|
||||||
.module-ledger/
|
.module-ledger/
|
||||||
|
deploy.log
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,95 +1,345 @@
|
|||||||
{
|
{
|
||||||
"modules": [
|
"modules": [
|
||||||
"MODULE_1V1_ARENA",
|
"MODULE_PLAYERBOTS",
|
||||||
"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_AOE_LOOT",
|
"MODULE_AOE_LOOT",
|
||||||
"MODULE_ARAC",
|
"MODULE_LEARN_SPELLS",
|
||||||
"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_FIREWORKS",
|
"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_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_GLOBAL_MAIL_BANKING_AUCTIONS",
|
||||||
"MODULE_GUILDHOUSE",
|
"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_PROGRESSION_SYSTEM",
|
||||||
"MODULE_PROMOTION_AZEROTHCORE",
|
"MODULE_NPC_FREE_PROFESSIONS",
|
||||||
"MODULE_PVP_TITLES",
|
"MODULE_DUEL_RESET",
|
||||||
"MODULE_RANDOM_ENCHANTS",
|
"MODULE_ZONE_DIFFICULTY",
|
||||||
"MODULE_REAGENT_BANK",
|
"MODULE_MORPHSUMMON",
|
||||||
"MODULE_RECRUIT_A_FRIEND",
|
"MODULE_SPELL_REGULATOR",
|
||||||
"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_WEEKEND_XP",
|
"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_WHO_LOGGED",
|
||||||
"MODULE_WORGOBLIN",
|
"MODULE_ACCOUNT_MOUNTS",
|
||||||
"MODULE_ZONE_CHECK",
|
"MODULE_ANTIFARMING",
|
||||||
"MODULE_ZONE_DIFFICULTY"
|
"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",
|
"label": "\ud83e\udde9 All Modules",
|
||||||
"description": "Enable every optional module in the repository",
|
"description": "Enable every optional module in the repository",
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ services:
|
|||||||
- --innodb-log-file-size=${MYSQL_INNODB_LOG_FILE_SIZE}
|
- --innodb-log-file-size=${MYSQL_INNODB_LOG_FILE_SIZE}
|
||||||
- --innodb-redo-log-capacity=${MYSQL_INNODB_REDO_LOG_CAPACITY}
|
- --innodb-redo-log-capacity=${MYSQL_INNODB_REDO_LOG_CAPACITY}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "sh", "-c", "mysqladmin ping -h localhost -u ${MYSQL_USER} -p${MYSQL_ROOT_PASSWORD} --silent || exit 1"]
|
test: ["CMD", "sh", "-c", "mysqladmin ping -h localhost -u ${MYSQL_USER} -p${MYSQL_ROOT_PASSWORD} --silent || exit 1"]
|
||||||
interval: ${MYSQL_HEALTHCHECK_INTERVAL}
|
interval: ${MYSQL_HEALTHCHECK_INTERVAL}
|
||||||
@@ -477,6 +478,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
networks:
|
networks:
|
||||||
- azerothcore
|
- azerothcore
|
||||||
volumes:
|
volumes:
|
||||||
@@ -531,6 +533,7 @@ services:
|
|||||||
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
||||||
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
networks:
|
networks:
|
||||||
- azerothcore
|
- azerothcore
|
||||||
cap_add: ["SYS_NICE"]
|
cap_add: ["SYS_NICE"]
|
||||||
@@ -568,6 +571,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
networks:
|
networks:
|
||||||
- azerothcore
|
- azerothcore
|
||||||
volumes:
|
volumes:
|
||||||
@@ -603,6 +611,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
- "${AUTH_EXTERNAL_PORT}:${AUTH_PORT}"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
networks:
|
networks:
|
||||||
- azerothcore
|
- azerothcore
|
||||||
volumes:
|
volumes:
|
||||||
@@ -660,6 +669,7 @@ services:
|
|||||||
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
- ${STORAGE_PATH}/modules:/azerothcore/modules
|
||||||
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
- ${STORAGE_PATH}/lua_scripts:/azerothcore/lua_scripts
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
networks:
|
networks:
|
||||||
- azerothcore
|
- azerothcore
|
||||||
cap_add: ["SYS_NICE"]
|
cap_add: ["SYS_NICE"]
|
||||||
@@ -716,6 +726,11 @@ services:
|
|||||||
- "${WORLD_EXTERNAL_PORT}:${WORLD_PORT}"
|
- "${WORLD_EXTERNAL_PORT}:${WORLD_PORT}"
|
||||||
- "${SOAP_EXTERNAL_PORT}:${SOAP_PORT}"
|
- "${SOAP_EXTERNAL_PORT}:${SOAP_PORT}"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
cap_add: ["SYS_NICE"]
|
cap_add: ["SYS_NICE"]
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "sh", "-c", "ps aux | grep '[w]orldserver' | grep -v grep || exit 1"]
|
test: ["CMD", "sh", "-c", "ps aux | grep '[w]orldserver' | grep -v grep || exit 1"]
|
||||||
@@ -863,10 +878,6 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 40s
|
start_period: 40s
|
||||||
logging:
|
logging:
|
||||||
driver: json-file
|
|
||||||
options:
|
|
||||||
max-size: "10m"
|
|
||||||
max-file: "3"
|
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
networks:
|
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
|
#!/bin/bash
|
||||||
# Fix item import for backup-merged characters
|
# 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
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$SCRIPT_DIR"
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
COLOR_RED='\033[0;31m'
|
# Source common library
|
||||||
COLOR_GREEN='\033[0;32m'
|
if [ -f "$SCRIPT_DIR/lib/common.sh" ]; then
|
||||||
COLOR_YELLOW='\033[1;33m'
|
source "$SCRIPT_DIR/lib/common.sh"
|
||||||
COLOR_BLUE='\033[0;34m'
|
else
|
||||||
COLOR_CYAN='\033[0;36m'
|
echo "ERROR: Common library not found at $SCRIPT_DIR/lib/common.sh" >&2
|
||||||
COLOR_RESET='\033[0m'
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
log(){ printf '%b\n' "${COLOR_GREEN}$*${COLOR_RESET}"; }
|
# Default values (can be overridden by environment or command line)
|
||||||
info(){ printf '%b\n' "${COLOR_CYAN}$*${COLOR_RESET}"; }
|
BACKUP_DIR="${BACKUP_DIR:-}"
|
||||||
warn(){ printf '%b\n' "${COLOR_YELLOW}$*${COLOR_RESET}"; }
|
ACCOUNT_IDS="${ACCOUNT_IDS:-}"
|
||||||
err(){ printf '%b\n' "${COLOR_RED}$*${COLOR_RESET}"; }
|
CHAR_GUIDS="${CHAR_GUIDS:-}"
|
||||||
fatal(){ err "$*"; exit 1; }
|
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"
|
# Show help message
|
||||||
BACKUP_DIR="/nfs/containers/ac-backup"
|
show_help() {
|
||||||
AUTH_DB="acore_auth"
|
cat << EOF
|
||||||
CHARACTERS_DB="acore_characters"
|
Fix item import for backup-merged characters
|
||||||
|
|
||||||
# Verify parameters
|
Usage:
|
||||||
[[ -d "$BACKUP_DIR" ]] || fatal "Backup directory not found: $BACKUP_DIR"
|
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
|
# Setup temp directory
|
||||||
TEMP_DIR="$(mktemp -d)"
|
TEMP_DIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||||
|
|
||||||
# MySQL connection helper
|
# MySQL connection helpers (override common.sh defaults with script-specific values)
|
||||||
mysql_exec(){
|
mysql_exec_local(){
|
||||||
local db="$1"
|
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 db="$1"
|
||||||
local query="$2"
|
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 "═══════════════════════════════════════════════════════════"
|
||||||
log " FIXING ITEM IMPORT FOR BACKUP-MERGED CHARACTERS"
|
log " FIXING ITEM IMPORT FOR BACKUP-MERGED CHARACTERS"
|
||||||
log "═══════════════════════════════════════════════════════════"
|
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..."
|
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
|
if [[ -z "$IMPORTED_CHARS" ]]; then
|
||||||
fatal "No imported characters found (accounts 451, 452)"
|
fatal "No imported characters found with account IDs: $ACCOUNT_IDS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Found imported characters:"
|
info "Found imported characters:"
|
||||||
@@ -60,7 +170,8 @@ echo "$IMPORTED_CHARS" | while read -r char_name char_guid; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Check current item count for these characters
|
# 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"
|
info "Current items for imported characters: $CURRENT_ITEM_COUNT"
|
||||||
|
|
||||||
if [[ "$CURRENT_ITEM_COUNT" != "0" ]]; then
|
if [[ "$CURRENT_ITEM_COUNT" != "0" ]]; then
|
||||||
@@ -94,26 +205,26 @@ log "Creating staging database..."
|
|||||||
STAGE_CHARS_DB="fix_stage_chars_$$"
|
STAGE_CHARS_DB="fix_stage_chars_$$"
|
||||||
|
|
||||||
# Drop any existing staging database
|
# 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
|
# 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 database on exit
|
||||||
cleanup_staging(){
|
cleanup_staging(){
|
||||||
if [[ -n "${STAGE_CHARS_DB:-}" ]]; then
|
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
|
fi
|
||||||
}
|
}
|
||||||
trap 'cleanup_staging; rm -rf "$TEMP_DIR"' EXIT
|
trap 'cleanup_staging; rm -rf "$TEMP_DIR"' EXIT
|
||||||
|
|
||||||
# Load backup into staging database
|
# Load backup into staging database
|
||||||
info "Loading 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" | \
|
sed "s/\`$CHARACTERS_DB\`/\`$STAGE_CHARS_DB\`/g; s/USE \`$CHARACTERS_DB\`;/USE \`$STAGE_CHARS_DB\`;/g" "$TEMP_DIR/characters.sql" | \
|
||||||
docker exec -i ac-mysql mysql -uroot -p"$MYSQL_PW" 2>/dev/null
|
docker exec -i "$MYSQL_CONTAINER" mysql -uroot -p"$MYSQL_PW" 2>/dev/null
|
||||||
|
|
||||||
# Get current database state
|
# 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))
|
ITEM_OFFSET=$((CURRENT_MAX_ITEM_GUID + 10000))
|
||||||
|
|
||||||
info "Current max item GUID: $CURRENT_MAX_ITEM_GUID"
|
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
|
# Create character mapping for the imported characters
|
||||||
log "Creating character mapping..."
|
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 (
|
CREATE TABLE character_guid_map (
|
||||||
old_guid INT UNSIGNED PRIMARY KEY,
|
old_guid INT UNSIGNED PRIMARY KEY,
|
||||||
new_guid INT UNSIGNED,
|
new_guid INT UNSIGNED,
|
||||||
name VARCHAR(12)
|
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)
|
INSERT INTO character_guid_map (old_guid, new_guid, name)
|
||||||
VALUES
|
SELECT
|
||||||
(1, 4501, 'Artimage'),
|
s.guid as old_guid,
|
||||||
(2, 4502, 'Flombey'),
|
c.guid as new_guid,
|
||||||
(3, 4503, 'Hammertime');
|
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
|
EOF
|
||||||
|
|
||||||
# Create item GUID mapping
|
# Create item GUID mapping
|
||||||
mysql_exec "$STAGE_CHARS_DB" <<EOF
|
mysql_exec_local "$STAGE_CHARS_DB" <<EOF
|
||||||
CREATE TABLE item_guid_map (
|
CREATE TABLE item_guid_map (
|
||||||
old_guid INT UNSIGNED PRIMARY KEY,
|
old_guid INT UNSIGNED PRIMARY KEY,
|
||||||
new_guid INT UNSIGNED,
|
new_guid INT UNSIGNED,
|
||||||
@@ -153,7 +274,7 @@ INNER JOIN character_guid_map cm ON i.owner_guid = cm.old_guid;
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Check how many items will be imported
|
# 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"
|
info "Items to import: $ITEMS_TO_IMPORT"
|
||||||
|
|
||||||
if [[ "$ITEMS_TO_IMPORT" == "0" ]]; then
|
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_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
|
if echo "$ITEM_RESULT" | grep -q "ERROR"; then
|
||||||
err "Item import failed:"
|
err "Item import failed:"
|
||||||
echo "$ITEM_RESULT" | grep "ERROR" >&2
|
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_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
|
if echo "$INV_RESULT" | grep -q "ERROR"; then
|
||||||
err "Inventory import failed:"
|
err "Inventory import failed:"
|
||||||
echo "$INV_RESULT" | grep "ERROR" >&2
|
echo "$INV_RESULT" | grep "ERROR" >&2
|
||||||
@@ -225,8 +346,8 @@ if echo "$INV_RESULT" | grep -q "ERROR"; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Report counts
|
# Report counts
|
||||||
ITEMS_IMPORTED=$(mysql_query "$CHARACTERS_DB" "SELECT COUNT(*) FROM item_instance WHERE owner_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 "$CHARACTERS_DB" "SELECT COUNT(*) FROM character_inventory WHERE guid IN (4501, 4502, 4503);")
|
INV_IMPORTED=$(mysql_query_local "$CHARACTERS_DB" "SELECT COUNT(*) FROM character_inventory WHERE guid IN ($CHAR_GUIDS);")
|
||||||
|
|
||||||
info "Items imported: $ITEMS_IMPORTED"
|
info "Items imported: $ITEMS_IMPORTED"
|
||||||
info "Inventory slots imported: $INV_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)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && 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"
|
MODULE_HELPER="$PROJECT_ROOT/scripts/python/modules.py"
|
||||||
DEFAULT_ENV_PATH="$PROJECT_ROOT/.env"
|
DEFAULT_ENV_PATH="$PROJECT_ROOT/.env"
|
||||||
ENV_PATH="${MODULES_ENV_PATH:-$DEFAULT_ENV_PATH}"
|
ENV_PATH="${MODULES_ENV_PATH:-$DEFAULT_ENV_PATH}"
|
||||||
TEMPLATE_FILE="$PROJECT_ROOT/.env.template"
|
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 (read from .env or template)
|
||||||
DEFAULT_PROJECT_NAME="$(project_name::resolve "$ENV_PATH" "$TEMPLATE_FILE")"
|
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
|
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 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_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
|
declare -a MODULE_KEYS
|
||||||
|
|
||||||
read_env_value(){
|
# Ensure Python is available
|
||||||
local key="$1" default="${2:-}" value="${!key:-}"
|
require_cmd python3
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_manifest_path(){
|
resolve_manifest_path(){
|
||||||
if [ -n "${MODULES_MANIFEST_PATH:-}" ] && [ -f "${MODULES_MANIFEST_PATH}" ]; then
|
if [ -n "${MODULES_MANIFEST_PATH:-}" ] && [ -f "${MODULES_MANIFEST_PATH}" ]; then
|
||||||
@@ -567,10 +551,10 @@ track_module_state(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
main(){
|
main(){
|
||||||
ensure_python
|
# Python is already checked at script start via require_cmd
|
||||||
|
|
||||||
if [ "${MODULES_LOCAL_RUN:-0}" != "1" ]; then
|
if [ "${MODULES_LOCAL_RUN:-0}" != "1" ]; then
|
||||||
cd /modules || err "Modules directory /modules not found"
|
cd /modules || fatal "Modules directory /modules not found"
|
||||||
fi
|
fi
|
||||||
MODULES_ROOT="$(pwd)"
|
MODULES_ROOT="$(pwd)"
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,6 @@ ensure_host_writable(){
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
seed_sql_ledger_if_needed(){
|
|
||||||
: # No-op; ledger removed
|
|
||||||
}
|
|
||||||
|
|
||||||
sync_local_staging(){
|
sync_local_staging(){
|
||||||
local src_root="$LOCAL_STORAGE_PATH"
|
local src_root="$LOCAL_STORAGE_PATH"
|
||||||
local dest_root="$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]:
|
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():
|
if not env_path.exists():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
env: Dict[str, str] = {}
|
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()
|
line = raw_line.strip()
|
||||||
|
|
||||||
|
# Skip empty lines and comments
|
||||||
if not line or line.startswith("#"):
|
if not line or line.startswith("#"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Remove 'export' prefix if present
|
||||||
if line.startswith("export "):
|
if line.startswith("export "):
|
||||||
line = line[len("export ") :].strip()
|
line = line[len("export ") :].strip()
|
||||||
|
|
||||||
|
# Skip lines without '='
|
||||||
if "=" not in line:
|
if "=" not in line:
|
||||||
continue
|
continue
|
||||||
key, value = line.split("=", 1)
|
|
||||||
key = key.strip()
|
try:
|
||||||
value = value.strip()
|
key, value = line.split("=", 1)
|
||||||
if value.startswith('"') and value.endswith('"'):
|
key = key.strip()
|
||||||
value = value[1:-1]
|
value = value.strip()
|
||||||
elif value.startswith("'") and value.endswith("'"):
|
|
||||||
value = value[1:-1]
|
# Strip quotes
|
||||||
env[key] = value
|
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
|
return env
|
||||||
|
|
||||||
|
|
||||||
def load_manifest(manifest_path: Path) -> List[Dict[str, object]]:
|
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():
|
if not manifest_path.exists():
|
||||||
raise FileNotFoundError(f"Manifest file not found: {manifest_path}")
|
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")
|
modules = manifest.get("modules")
|
||||||
if not isinstance(modules, list):
|
if not isinstance(modules, list):
|
||||||
raise ValueError("Manifest must define a top-level 'modules' array")
|
raise ValueError("Manifest must define a top-level 'modules' array")
|
||||||
|
|
||||||
validated: List[Dict[str, object]] = []
|
validated: List[Dict[str, object]] = []
|
||||||
seen_keys: set[str] = set()
|
seen_keys: set[str] = set()
|
||||||
for entry in modules:
|
|
||||||
|
for idx, entry in enumerate(modules):
|
||||||
if not isinstance(entry, dict):
|
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")
|
key = entry.get("key")
|
||||||
name = entry.get("name")
|
name = entry.get("name")
|
||||||
repo = entry.get("repo")
|
repo = entry.get("repo")
|
||||||
|
|
||||||
if not key or not isinstance(key, str):
|
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:
|
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)
|
seen_keys.add(key)
|
||||||
|
|
||||||
if not name or not isinstance(name, str):
|
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):
|
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)
|
validated.append(entry)
|
||||||
|
|
||||||
return validated
|
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
|
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(){
|
show_realm_configured(){
|
||||||
echo -e "\n${GREEN}⚔️ Your realm configuration has been forged! ⚔️${NC}"
|
echo -e "\n${GREEN}⚔️ Your realm configuration has been forged! ⚔️${NC}"
|
||||||
@@ -1160,6 +1193,7 @@ fi
|
|||||||
done
|
done
|
||||||
|
|
||||||
auto_enable_module_dependencies
|
auto_enable_module_dependencies
|
||||||
|
ensure_module_platforms
|
||||||
|
|
||||||
if [ "${MODULE_OLLAMA_CHAT:-0}" = "1" ] && [ "${MODULE_PLAYERBOTS:-0}" != "1" ]; then
|
if [ "${MODULE_OLLAMA_CHAT:-0}" = "1" ] && [ "${MODULE_PLAYERBOTS:-0}" != "1" ]; then
|
||||||
say INFO "Automatically enabling MODULE_PLAYERBOTS for MODULE_OLLAMA_CHAT."
|
say INFO "Automatically enabling MODULE_PLAYERBOTS for MODULE_OLLAMA_CHAT."
|
||||||
@@ -1326,6 +1360,7 @@ fi
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
auto_enable_module_dependencies
|
auto_enable_module_dependencies
|
||||||
|
ensure_module_platforms
|
||||||
|
|
||||||
if [ -n "$CLI_PLAYERBOT_ENABLED" ]; then
|
if [ -n "$CLI_PLAYERBOT_ENABLED" ]; then
|
||||||
if [[ "$CLI_PLAYERBOT_ENABLED" != "0" && "$CLI_PLAYERBOT_ENABLED" != "1" ]]; 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