Delete scripts/vps-paragon-diagnostics.sh
This commit is contained in:
@@ -1,336 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Collect VPS evidence for Paragon / DBUpdater / binary staleness triage.
|
||||
# Run ON the VPS (Linux). Safe: read-only; does not restart services.
|
||||
#
|
||||
# Usage (from clone):
|
||||
# bash scripts/vps-paragon-diagnostics.sh
|
||||
#
|
||||
# Optional environment:
|
||||
# FRACTURED_REPO — absolute path to Fractured git root (default: parent of scripts/)
|
||||
# FRACTURED_WS_BIN — path to worldserver binary (default: auto-detect)
|
||||
# FRACTURED_WORLDSERVER_CONF — path to worldserver.conf (default: guess from BIN + common layouts)
|
||||
# FRACTURED_SYSTEMD_UNITS — space-separated units to try (default: "fractured-world worldserver ac-worldserver")
|
||||
# FRACTURED_MYSQL — prefix to invoke mysql, e.g. 'mysql -uacore -pacore -h127.0.0.1'
|
||||
# (default Fractured local DB user/password are often both "acore"; use ~/.my.cnf if you prefer not to pass -p on the command line)
|
||||
# If unset, SQL blocks are printed for manual copy-paste only.
|
||||
# FRACTURED_SPELL_IDS — space-separated spell IDs for spell_dbc spot-check (defaults to common DK rune spenders)
|
||||
# FRACTURED_DIAG_OUTPUT — full log file path (default: <repo>/var/vps-paragon-diagnostics-last.txt)
|
||||
#
|
||||
# All output is mirrored to the log file (tee) while still printing to the terminal.
|
||||
# Default path lives under var/ (gitignored in this repo). Open that file in Cursor,
|
||||
# scp it down, or: git add -f var/vps-paragon-diagnostics-last.txt if you intend to commit it.
|
||||
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO="${FRACTURED_REPO:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||
|
||||
DIAG_OUT="${FRACTURED_DIAG_OUTPUT:-$REPO/var/vps-paragon-diagnostics-last.txt}"
|
||||
mkdir -p "$(dirname "$DIAG_OUT")"
|
||||
exec > >(tee "$DIAG_OUT") 2>&1
|
||||
echo "Logging to: $DIAG_OUT"
|
||||
|
||||
hr() { printf '\n%s\n' "================================================================================"; }
|
||||
sub() { printf '\n-- %s\n' "$1"; }
|
||||
|
||||
detect_worldserver_bin() {
|
||||
local bin="" es path u units
|
||||
if [[ -n "${FRACTURED_WS_BIN:-}" ]]; then
|
||||
readlink -f "$FRACTURED_WS_BIN" 2>/dev/null && return
|
||||
echo "$FRACTURED_WS_BIN"
|
||||
return
|
||||
fi
|
||||
|
||||
units="${FRACTURED_SYSTEMD_UNITS:-fractured-world worldserver ac-worldserver}"
|
||||
for u in $units; do
|
||||
if systemctl is-active --quiet "$u" 2>/dev/null || systemctl is-enabled --quiet "$u" 2>/dev/null; then
|
||||
es=$(systemctl show "$u" -p ExecStart --value 2>/dev/null || true)
|
||||
if [[ -n "$es" ]]; then
|
||||
if [[ "$es" == \{*path=* ]]; then
|
||||
path=$(printf '%s' "$es" | sed -n 's/.*path=\([^;]*\).*/\1/p')
|
||||
else
|
||||
path=$(printf '%s' "$es" | awk '{print $1}' | sed 's/^path=//')
|
||||
fi
|
||||
if [[ -n "$path" && -x "$path" ]]; then
|
||||
readlink -f "$path" 2>/dev/null && return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
local pid
|
||||
pid=$(pgrep -xo worldserver 2>/dev/null || true)
|
||||
if [[ -n "$pid" ]]; then
|
||||
readlink -f "/proc/$pid/exe" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
if command -v worldserver >/dev/null 2>&1; then
|
||||
readlink -f "$(command -v worldserver)" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
guess_worldserver_conf() {
|
||||
local bin="$1"
|
||||
local d cands=()
|
||||
[[ -z "$bin" ]] && return
|
||||
d=$(dirname "$bin")
|
||||
cands+=("$d/../etc/worldserver.conf")
|
||||
cands+=("$d/../../etc/worldserver.conf")
|
||||
cands+=("$HOME/azeroth-server/etc/worldserver.conf")
|
||||
cands+=("$HOME/env/dist/etc/worldserver.conf")
|
||||
for f in "${cands[@]}"; do
|
||||
f=$(readlink -f "$f" 2>/dev/null || true)
|
||||
if [[ -n "$f" && -f "$f" ]]; then
|
||||
echo "$f"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
binary_strings_paths() {
|
||||
local ws="$1"
|
||||
[[ -z "$ws" || ! -f "$ws" ]] && return
|
||||
strings "$ws" 2>/dev/null | grep -iE '/(home|root|opt|srv|var)[^[:space:]]*/(Fractured|fractured|azeroth|AzerothCore|acore)' | sort -u | head -40
|
||||
}
|
||||
|
||||
hr
|
||||
echo "Fractured Paragon / native VPS diagnostics"
|
||||
echo "Date (UTC): $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "Repo (expected): $REPO"
|
||||
|
||||
sub "1A — worldserver binary"
|
||||
WS=$(detect_worldserver_bin || true)
|
||||
if [[ -z "$WS" ]]; then
|
||||
echo "ERROR: Could not find worldserver. Set FRACTURED_WS_BIN=/full/path/to/worldserver and re-run."
|
||||
else
|
||||
echo "Binary: $WS"
|
||||
if stat -c 'binary mtime: %y' "$WS" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
stat -f 'binary mtime: %Sm' -t '%Y-%m-%d %H:%M:%S %z' "$WS" 2>/dev/null || stat "$WS"
|
||||
fi
|
||||
fi
|
||||
|
||||
sub "1B — repo HEAD + Paragon_Essence.cpp mtime"
|
||||
if [[ -d "$REPO/.git" ]]; then
|
||||
(cd "$REPO" && git log -1 --format='HEAD commit: %h %ci %s')
|
||||
else
|
||||
echo "WARN: not a git repo: $REPO (set FRACTURED_REPO)"
|
||||
fi
|
||||
PE="$REPO/modules/mod-paragon/src/Paragon_Essence.cpp"
|
||||
if [[ -f "$PE" ]]; then
|
||||
if stat -c 'Paragon_Essence.cpp mtime: %y' "$PE" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
stat -f 'Paragon_Essence.cpp mtime: %Sm' -t '%Y-%m-%d %H:%M:%S %z' "$PE" 2>/dev/null || stat "$PE"
|
||||
fi
|
||||
else
|
||||
echo "WARN: missing $PE"
|
||||
fi
|
||||
|
||||
sub "1C — strings heuristics (0 can mean stripped binary — use 1A+1B)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
c1=$(strings "$WS" 2>/dev/null | grep -c 'CLASS_PARAGON' || true)
|
||||
c2=$(strings "$WS" 2>/dev/null | grep -c 'C BUILD SAVE_CURRENT' || true)
|
||||
c3=$(strings "$WS" 2>/dev/null | grep -c 'character_paragon_build_share_archive' || true)
|
||||
echo "CLASS_PARAGON count: $c1"
|
||||
echo "C BUILD SAVE_CURRENT count: $c2"
|
||||
echo "character_paragon_build_share_archive count: $c3"
|
||||
else
|
||||
echo "(skipped — no binary)"
|
||||
fi
|
||||
|
||||
sub "1D — binary fingerprint (compare sha256 across dev vs VPS)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$WS"
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$WS"
|
||||
else
|
||||
echo "(no sha256sum — install coreutils)"
|
||||
fi
|
||||
echo "Embedded revision / version strings (first matches):"
|
||||
strings "$WS" 2>/dev/null | grep -iE 'azerothcore|revision|git|commit|build.*20[0-9]{2}' | head -25 || echo "(none matched)"
|
||||
else
|
||||
echo "(skipped — no binary)"
|
||||
fi
|
||||
|
||||
CONF="${FRACTURED_WORLDSERVER_CONF:-}"
|
||||
if [[ -z "$CONF" && -n "$WS" ]]; then
|
||||
CONF=$(guess_worldserver_conf "$WS")
|
||||
fi
|
||||
|
||||
sub "2B — worldserver.conf (updater / source / rates / paragon)"
|
||||
if [[ -n "$CONF" && -f "$CONF" ]]; then
|
||||
echo "Using conf: $CONF"
|
||||
grep -E '^SourceDirectory|^Updates\.EnableDatabases|^Updates\.AutoSetup|^[[:space:]]*SourceDirectory|^[[:space:]]*Updates\.EnableDatabases|^[[:space:]]*Updates\.AutoSetup' "$CONF" 2>/dev/null || echo "(no matching lines or unreadable)"
|
||||
echo "--- Rate.RunicPower (if set) ---"
|
||||
grep -iE '^Rate\.RunicPower|^[[:space:]]*Rate\.RunicPower' "$CONF" 2>/dev/null || echo "(not set — server uses default)"
|
||||
echo "--- Paragon.* module options (if any) ---"
|
||||
grep -iE '^Paragon\.|^[[:space:]]*Paragon\.' "$CONF" 2>/dev/null || echo "(no Paragon.* keys in worldserver.conf — check etc/modules/mod_paragon.conf)"
|
||||
else
|
||||
echo "WARN: worldserver.conf not found. Set FRACTURED_WORLDSERVER_CONF=/path/to/worldserver.conf"
|
||||
fi
|
||||
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
ETCGuess=$(readlink -f "$(dirname "$WS")/../etc" 2>/dev/null || true)
|
||||
MPC="$ETCGuess/modules/mod_paragon.conf"
|
||||
if [[ -f "$MPC" ]]; then
|
||||
sub "2B2 — mod_paragon.conf Paragon.* toggles (non-comment)"
|
||||
grep -E '^Paragon\.' "$MPC" 2>/dev/null | head -40 || echo "(no uncommented Paragon.* lines)"
|
||||
fi
|
||||
fi
|
||||
|
||||
sub "2A — path-like strings from binary (candidate source roots)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
binary_strings_paths "$WS" || true
|
||||
else
|
||||
echo "(skipped)"
|
||||
fi
|
||||
|
||||
sub "Resolved source root for 2D"
|
||||
RESOLVED=""
|
||||
if [[ -n "$CONF" && -f "$CONF" ]]; then
|
||||
sd=$(awk -F= '/^[[:space:]]*SourceDirectory[[:space:]]*=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2);
|
||||
gsub(/^["'\'']|["'\'']$/, "", $2);
|
||||
print $2; exit }' "$CONF" 2>/dev/null || true)
|
||||
if [[ -n "${sd:-}" ]]; then
|
||||
RESOLVED="$sd"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$RESOLVED" ]]; then
|
||||
RESOLVED="$REPO"
|
||||
fi
|
||||
echo "Using RESOLVED=$RESOLVED (from SourceDirectory if set in conf, else FRACTURED_REPO)"
|
||||
|
||||
sub "2D — Paragon SQL dirs under RESOLVED"
|
||||
for subdir in \
|
||||
"$RESOLVED/modules/mod-paragon/data/sql/db-world/updates/" \
|
||||
"$RESOLVED/modules/mod-paragon/data/sql/db-characters/updates/"; do
|
||||
if [[ -d "$subdir" ]]; then
|
||||
echo "Listing: $subdir"
|
||||
ls -la "$subdir" 2>/dev/null | tail -15
|
||||
else
|
||||
echo "MISSING: $subdir"
|
||||
fi
|
||||
done
|
||||
|
||||
sub "CMake build dir hints (common Fractured layouts)"
|
||||
for cand in "$REPO/var/build/obj" "$REPO/build" "$REPO/../build"; do
|
||||
if [[ -f "$cand/CMakeCache.txt" ]]; then
|
||||
echo "Found CMakeCache: $cand/CMakeCache.txt"
|
||||
grep -E '^CMAKE_HOME_DIRECTORY:|^MODULES:|^CMAKE_INSTALL_PREFIX:' "$cand/CMakeCache.txt" 2>/dev/null | head -5
|
||||
fi
|
||||
done
|
||||
|
||||
sub "DATABASE — updates rows (2026_05_10 / paragon)"
|
||||
SQL_WORLD=$(cat <<'EOS'
|
||||
SELECT name, hash, speed FROM updates
|
||||
WHERE name LIKE '2026_05_10%' OR name LIKE '%paragon%'
|
||||
ORDER BY name DESC LIMIT 30;
|
||||
EOS
|
||||
)
|
||||
SQL_CHAR="$SQL_WORLD"
|
||||
|
||||
if [[ -n "${FRACTURED_MYSQL:-}" ]]; then
|
||||
echo "--- acore_world ---"
|
||||
$FRACTURED_MYSQL acore_world -e "$SQL_WORLD" || echo "(mysql failed for acore_world)"
|
||||
echo "--- acore_characters ---"
|
||||
$FRACTURED_MYSQL acore_characters -e "$SQL_CHAR" || echo "(mysql failed for acore_characters)"
|
||||
|
||||
sub "DATABASE — DBC parity for runes / Paragon (acore_world)"
|
||||
# Common DK rune spenders (WotLK). Override: export FRACTURED_SPELL_IDS='45477 45462'
|
||||
SPELL_IDS="${FRACTURED_SPELL_IDS:-45477 45462 49923 55050 56815}"
|
||||
IDS_CSV=$(echo "$SPELL_IDS" | tr ' ' ',')
|
||||
echo "--- spell_dbc table size (world DB overrides; 0 rows = all spells from disk DBC only) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "SELECT COUNT(*) AS spell_dbc_rows FROM spell_dbc;" 2>/dev/null || echo "(spell_dbc missing or no access)"
|
||||
echo "--- acore_world.version (last core revision written by worldserver) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "SELECT * FROM version LIMIT 5;" 2>/dev/null || echo "(version table missing?)"
|
||||
|
||||
echo "--- chrclasses_dbc class 6 + 12 (DisplayPower: 0=mana, 5=POWER_RUNE in AC) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, DisplayPower, Name_Lang_enUS FROM chrclasses_dbc WHERE ID IN (6,12);
|
||||
" 2>/dev/null || echo "(query failed — chrclasses_dbc missing?)"
|
||||
echo "Note: If only ID=12 appears, class 6 (DK) is not overridden in DB — loaded from disk DBC (normal)."
|
||||
|
||||
echo "--- spell_dbc: are sample DK spells overridden in DB? ---"
|
||||
spell_sample_n=$($FRACTURED_MYSQL acore_world -N -B -e \
|
||||
"SELECT COUNT(*) FROM spell_dbc WHERE ID IN ($IDS_CSV);" 2>/dev/null || echo 0)
|
||||
echo "Row count in spell_dbc for sample IDs ($SPELL_IDS): ${spell_sample_n:-0}"
|
||||
if [[ "${spell_sample_n:-0}" == "0" ]]; then
|
||||
echo "=> 0 means those spells use on-disk Spell.dbc only; the sample block below will be empty (not an error)."
|
||||
fi
|
||||
echo "--- spell_dbc sample (PowerType 5 = POWER_RUNE in AC) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc WHERE ID IN ($IDS_CSV);
|
||||
" 2>/dev/null || echo "(query failed — spell_dbc missing or wrong schema)"
|
||||
echo "--- spellrunecost join for sample IDs (empty if no spell_dbc rows above) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT s.ID AS spell_id, s.PowerType, s.RuneCostID, r.Blood, r.Unholy, r.Frost, r.RunicPower
|
||||
FROM spell_dbc s
|
||||
LEFT JOIN spellrunecost_dbc r ON r.ID = s.RuneCostID
|
||||
WHERE s.ID IN ($IDS_CSV);
|
||||
" 2>/dev/null || echo "(join failed — check spellrunecost_dbc)"
|
||||
|
||||
echo "--- spell_dbc suspicious overrides: RuneCostID>0 but PowerType!=5 (can break rune checks) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc
|
||||
WHERE RuneCostID > 0 AND PowerType <> 5
|
||||
ORDER BY ID LIMIT 40;
|
||||
" 2>/dev/null || echo "(query failed)"
|
||||
echo "Compare counts/IDs to dev: unexpected rows here warrant a DB diff."
|
||||
|
||||
echo "--- spell_dbc POWER_RUNE (5) spells with RuneCostID (sample) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, RuneCostID FROM spell_dbc
|
||||
WHERE PowerType = 5 AND RuneCostID > 0
|
||||
ORDER BY ID LIMIT 15;
|
||||
" 2>/dev/null || echo "(query failed)"
|
||||
else
|
||||
echo "FRACTURED_MYSQL not set — run manually (example: export FRACTURED_MYSQL='mysql -uUSER -hHOST')"
|
||||
echo "acore_world:"
|
||||
echo "$SQL_WORLD"
|
||||
echo "acore_characters:"
|
||||
echo "$SQL_CHAR"
|
||||
echo ""
|
||||
echo "Optional DBC parity (acore_world) — run after connecting:"
|
||||
echo " SELECT ID, DisplayPower, Name_Lang_enUS FROM chrclasses_dbc WHERE ID IN (6,12);"
|
||||
echo " SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc WHERE ID IN (45477,45462,49923,55050,56815);"
|
||||
echo " SELECT s.ID, s.RuneCostID, r.Blood, r.Unholy, r.Frost, r.RunicPower FROM spell_dbc s"
|
||||
echo " LEFT JOIN spellrunecost_dbc r ON r.ID = s.RuneCostID WHERE s.ID IN (45477,45462,49923,55050,56815);"
|
||||
fi
|
||||
|
||||
sub "mod_paragon.conf vs .dist (install etc)"
|
||||
ETC=""
|
||||
if [[ -n "$WS" ]]; then
|
||||
ETC=$(readlink -f "$(dirname "$WS")/../etc" 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -z "$ETC" || ! -d "$ETC" ]]; then
|
||||
ETC=$(readlink -f "$HOME/azeroth-server/etc" 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -n "$ETC" && -d "$ETC/modules" ]]; then
|
||||
MP="$ETC/modules/mod_paragon.conf"
|
||||
MPD="$ETC/modules/mod_paragon.conf.dist"
|
||||
if [[ -f "$MP" && -f "$MPD" ]]; then
|
||||
diff -u "$MP" "$MPD" 2>/dev/null | head -80 || true
|
||||
else
|
||||
echo "ETC=$ETC — mod_paragon.conf or .dist missing (MP=$MP MPD=$MPD)"
|
||||
fi
|
||||
else
|
||||
echo "Could not find install etc/modules (set paths manually for diff)."
|
||||
fi
|
||||
|
||||
hr
|
||||
echo "DELIVERABLE for maintainer:"
|
||||
echo "1) Paste 1A–1D (binary mtime, git HEAD, strings, sha256 + revision strings)."
|
||||
echo "2) Paste DATABASE blocks: updates + DBC parity (chrclasses 12, spell_dbc, spellrunecost join)."
|
||||
echo "3) Paste 2A path strings + 2D listings (or MISSING lines)."
|
||||
echo "4) From dev: same 1D sha256 of worldserver OR same SQL block — proves binary/data parity."
|
||||
echo "5) ONE sentence: exact in-game symptom."
|
||||
echo "Done."
|
||||
echo ""
|
||||
echo "Full transcript: $DIAG_OUT"
|
||||
Reference in New Issue
Block a user