Files
moonwell-core/import-custom-sql.sh
T
2026-05-11 18:35:47 +04:00

289 lines
8.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
IMPORT_MYTHICPLUS_SQL="${ACORE_IMPORT_MYTHICPLUS_SQL:-1}"
IMPORT_STORE_SQL="${ACORE_IMPORT_STORE_SQL:-1}"
usage() {
cat <<EOF
Usage: $(basename "$0")
Imports required custom SQL that lives outside the normal AzerothCore module paths.
Currently this bootstraps the MythicPlus and Store Lua packages, plus module strings
that are not covered by the normal module SQL import path in this deployment.
EOF
}
log() {
printf '[import-custom-sql] %s\n' "$*"
}
mysql_query() {
local database="$1"
local sql="$2"
docker compose exec -T ac-database bash -lc \
"mysql -N -s -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \"$database\"" <<<"$sql"
}
mysql_exec() {
local database="${1:-}"
local sql="$2"
if [[ -n "$database" ]]; then
docker compose exec -T ac-database bash -lc \
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \"$database\"" <<<"$sql"
else
docker compose exec -T ac-database bash -lc \
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\"" <<<"$sql"
fi
}
table_exists() {
local database="$1"
local table_name="$2"
local result
result="$(mysql_query "information_schema" \
"SELECT COUNT(*) FROM tables WHERE table_schema = '${database}' AND table_name = '${table_name}';")"
[[ "$result" == "1" ]]
}
database_exists() {
local database="$1"
local result
result="$(mysql_query "information_schema" \
"SELECT COUNT(*) FROM schemata WHERE schema_name = '${database}';")"
[[ "$result" == "1" ]]
}
import_sql_file() {
local database="$1"
local file_path="$2"
if [[ ! -f "$file_path" ]]; then
printf 'SQL file not found: %s\n' "$file_path" >&2
exit 1
fi
log "importing $(basename "$file_path") into ${database}"
docker compose exec -T ac-database bash -lc \
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \"$database\"" < "$file_path"
}
require_integer_flag() {
local name="$1"
local value="$2"
if ! [[ "$value" =~ ^[01]$ ]]; then
printf '%s must be 0 or 1, got: %s\n' "$name" "$value" >&2
exit 1
fi
}
maybe_import_table() {
local database="$1"
local table_name="$2"
local file_path="$3"
if table_exists "$database" "$table_name"; then
log "table ${database}.${table_name} already exists, skipping"
return 0
fi
import_sql_file "$database" "$file_path"
}
import_mythicplus_sql() {
if [[ "$IMPORT_MYTHICPLUS_SQL" == "0" ]]; then
log "MythicPlus SQL import disabled, skipped"
return 0
fi
if [[ ! -d "$ROOT_DIR/sql/Mythic+" ]]; then
log "sql/Mythic+ directory not found, skipped"
return 0
fi
maybe_import_table "acore_characters" "character_mythic_history" \
"$ROOT_DIR/sql/Mythic+/characters/character_mythic_history.sql"
maybe_import_table "acore_characters" "character_mythic_keys" \
"$ROOT_DIR/sql/Mythic+/characters/character_mythic_keys.sql"
maybe_import_table "acore_characters" "character_mythic_rating" \
"$ROOT_DIR/sql/Mythic+/characters/character_mythic_rating.sql"
maybe_import_table "acore_characters" "character_mythic_vault" \
"$ROOT_DIR/sql/Mythic+/characters/character_mythic_vault.sql"
maybe_import_table "acore_characters" "character_mythic_weekly_affixes" \
"$ROOT_DIR/sql/Mythic+/characters/character_mythic_weekly_affixes.sql"
maybe_import_table "acore_world" "world_mythic_loot" \
"$ROOT_DIR/sql/Mythic+/world/world_mythic_loot.sql"
maybe_import_table "acore_world" "world_vault_loot" \
"$ROOT_DIR/sql/Mythic+/world/world_vault_loot.sql"
if [[ "$(mysql_query "acore_world" "
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM creature_template WHERE entry = 900001)
AND EXISTS (SELECT 1 FROM item_template WHERE entry = 900100)
AND EXISTS (SELECT 1 FROM gameobject_template WHERE entry = 900000)
AND EXISTS (SELECT 1 FROM creature WHERE id1 = 900001 OR id2 = 900001 OR id3 = 900001)
AND EXISTS (SELECT 1 FROM gameobject WHERE id = 900000)
THEN 1
ELSE 0
END;
")" == "0" ]]; then
log "repairing MythicPlus world bootstrap entries"
mysql_exec "acore_world" "
DELETE FROM creature WHERE id1 = 900001 OR id2 = 900001 OR id3 = 900001;
DELETE FROM gameobject WHERE id = 900000;
DELETE FROM creature_template_locale WHERE entry = 900001;
DELETE FROM item_template_locale WHERE ID = 900100;
DELETE FROM creature_template_model WHERE CreatureID = 900001;
DELETE FROM creature_template WHERE entry = 900001;
DELETE FROM item_template WHERE entry = 900100;
DELETE FROM gameobject_template WHERE entry = 900000;
"
import_sql_file "acore_world" "$ROOT_DIR/sql/Mythic+/world/creature_and_keystones.sql"
else
log "MythicPlus creature bootstrap already exists, skipping"
fi
}
import_store_sql() {
local store_sql_file="$ROOT_DIR/sql/Store.sql"
local temp_sql=""
if [[ "$IMPORT_STORE_SQL" == "0" ]]; then
log "Store SQL import disabled, skipped"
return 0
fi
if [[ ! -d "$ROOT_DIR/lua_scripts/Store_System" ]]; then
log "lua_scripts/Store_System directory not found, skipped"
return 0
fi
if [[ ! -f "$store_sql_file" ]]; then
log "sql/Store.sql not found, skipped"
return 0
fi
if ! database_exists "store"; then
log "creating database store"
mysql_exec "" "CREATE DATABASE IF NOT EXISTS \`store\` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
fi
temp_sql="$(mktemp)"
{
printf 'CREATE DATABASE IF NOT EXISTS `store` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;\n'
printf 'USE `store`;\n'
awk '
BEGIN { skip = 0 }
skip {
if ($0 ~ /;[[:space:]]*$/) {
skip = 0
}
next
}
$0 ~ /^INSERT INTO `store_logs` / {
skip = 1
next
}
{
sub(/^INSERT INTO /, "INSERT IGNORE INTO ")
print
}
' "$store_sql_file"
} > "$temp_sql"
log "bootstrapping store database"
mysql_exec "" "$(cat "$temp_sql")"
rm -f "$temp_sql"
}
import_aoe_loot_sql() {
local aoe_loot_sql_file="$ROOT_DIR/modules/mod-aoe-loot/data/sql/db-world/base/aoe_loot_module_string.sql"
if [[ ! -f "$aoe_loot_sql_file" ]]; then
log "mod-aoe-loot module string SQL not found, skipped"
return 0
fi
if [[ "$(mysql_query "acore_world" "SELECT COUNT(*) FROM module_string WHERE module = 'mod-aoe-loot';")" != "0" ]] && \
[[ "$(mysql_query "acore_world" "SELECT COUNT(*) FROM module_string_locale WHERE module = 'mod-aoe-loot';")" != "0" ]]; then
log "mod-aoe-loot module strings already exist, skipping"
return 0
fi
import_sql_file "acore_world" "$aoe_loot_sql_file"
}
import_taxi_builder_sql() {
# Source-of-truth tables for the Taxi Route Builder. The migration also
# lives in data/sql/updates/pending_db_world/ for AzerothCore DBUpdater,
# but we apply it eagerly here so deploy flows that don't rely on the
# worldserver-side updater still bootstrap correctly.
local taxi_builder_sql_file="$ROOT_DIR/data/sql/updates/pending_db_world/rev_1778279831177904414.sql"
if [[ ! -f "$taxi_builder_sql_file" ]]; then
log "taxi-builder migration not found, skipped"
return 0
fi
if table_exists "acore_world" "custom_taxi_nodes" \
&& table_exists "acore_world" "custom_taxi_paths" \
&& table_exists "acore_world" "custom_taxi_path_nodes"; then
log "taxi-builder tables already exist, skipping"
return 0
fi
import_sql_file "acore_world" "$taxi_builder_sql_file"
}
if (($# > 0)); then
case "$1" in
-h|--help)
usage
exit 0
;;
*)
printf 'Unknown option: %s\n\n' "$1" >&2
usage >&2
exit 1
;;
esac
fi
require_integer_flag "ACORE_IMPORT_MYTHICPLUS_SQL" "$IMPORT_MYTHICPLUS_SQL"
require_integer_flag "ACORE_IMPORT_STORE_SQL" "$IMPORT_STORE_SQL"
cd "$ROOT_DIR"
if ! docker compose ps --status running --services | grep -qx 'ac-database'; then
printf 'ac-database is not running. Start the database first.\n' >&2
exit 1
fi
import_mythicplus_sql
import_aoe_loot_sql
import_taxi_builder_sql
import_store_sql
log "custom SQL bootstrap completed"