Skip to content

Instantly share code, notes, and snippets.

@Daribon
Last active December 31, 2025 04:58
Show Gist options
  • Select an option

  • Save Daribon/03a696faf50c70ef692ca9803843d7ac to your computer and use it in GitHub Desktop.

Select an option

Save Daribon/03a696faf50c70ef692ca9803843d7ac to your computer and use it in GitHub Desktop.
VMaNGOS Linux install script
#!/bin/bash
set -euo pipefail
# -------------------------------------------------------------
# VMaNGOS Linux Installer
# -------------------------------------------------------------
[[ $EUID -eq 0 ]] || { echo "Error: Run as root (or with sudo)"; exit 1; }
# Get real user home directory
if [[ -n "${SUDO_USER:-}" ]]; then
USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
else
USER_HOME="$HOME"
fi
INSTALL="$USER_HOME/vmangos"
STATE_FILE="$INSTALL/installed-patches.ini"
# ---------- Supported client patches ----------
PATCHES=("1.12.1" "1.11.2" "1.10.2" "1.9.4" "1.8.4" "1.7.1" "1.6.1")
declare -A BUILD_MAP=(
["1.12.1"]=5875 ["1.11.2"]=5464 ["1.10.2"]=5302
["1.9.4"]=5086 ["1.8.4"]=4878 ["1.7.1"]=4695
["1.6.1"]=4544
)
# ---------- Detect package manager ----------
if command -v pacman >/dev/null 2>&1; then
PKG_MANAGER="pacman"
elif command -v dnf >/dev/null 2>&1; then
PKG_MANAGER="dnf"
elif command -v apt >/dev/null 2>&1; then
PKG_MANAGER="apt"
else
echo "Error: Unsupported system. Only pacman, dnf, and apt are supported."
exit 1
fi
echo "Detected system: $PKG_MANAGER"
# ---------- Helpers ----------
pick_patch() {
echo "Available client patches (newest first):"
select PATCH in "${PATCHES[@]}"; do
[[ -n "$PATCH" ]] && break
echo "Invalid selection. Try again."
done
}
ini_get() {
local file="$1" section="$2" key="$3"
awk -F '=' '
$0 ~ "^\\[" section "\\]" { in_section = 1; next }
in_section && $0 ~ "^\\[" { in_section = 0 }
in_section && tolower($1) ~ "^" tolower(key) "[ \t]*$" {
gsub(/^[ \t]+|[ \t]+$/, "", $2)
print $2
exit
}
' section="$section" key="$key" "$file"
}
# ---------- 1. Choose patch ----------
if [[ ! -f "$STATE_FILE" ]] || [[ $(grep -c '^\[.*\]$' "$STATE_FILE" 2>/dev/null || echo 0) -eq 0 ]]; then
pick_patch
BUILD="${BUILD_MAP[$PATCH]}"
else
echo "What would you like to do?"
select ACTION in "Install new patch" "Update existing patch" "Quit"; do
case "$REPLY" in
1) pick_patch; BUILD="${BUILD_MAP[$PATCH]}"; break ;;
2)
mapfile -t builds < <(grep -oP '^\[\K[^]]+' "$STATE_FILE")
declare -a opts=()
for b in "${builds[@]}"; do
p=$(ini_get "$STATE_FILE" "$b" "patch")
h=$(ini_get "$STATE_FILE" "$b" "git_hash")
[[ -f "$INSTALL/bin/mangosd_$b" ]] && opts+=("$p ($b — $h)")
done
[[ ${#opts[@]} -eq 0 ]] && { echo "No installed patches found."; exit 0; }
echo "Select patch to update:"
select opt in "${opts[@]}"; do [[ -n "$opt" ]] && break; done
PATCH=$(echo "$opt" | awk '{print $1}')
BUILD="${BUILD_MAP[$PATCH]}"
break
;;
3) echo "Goodbye!"; exit 0 ;;
*) echo "Invalid choice." ;;
esac
done
fi
# Resolve patch name from build number if needed
[[ -z "${PATCH:-}" ]] && PATCH=$(for p in "${!BUILD_MAP[@]}"; do [[ "${BUILD_MAP[$p]}" -eq "$BUILD" ]] && echo "$p" && break; done)
[[ -z "$PATCH" ]] && { echo "Error: Cannot map build $BUILD to any known patch."; exit 1; }
# ---------- 2. Install system dependencies ----------
echo "Updating system and installing required packages..."
case "$PKG_MANAGER" in
pacman)
pacman -Syu --noconfirm
pacman -S --needed --noconfirm base-devel cmake git mariadb mariadb-libs onetbb openssl zlib-ng-compat wget unzip tmux curl
;;
dnf)
dnf check-update || true
dnf install -y epel-release
dnf group install -y "Development Tools"
dnf install -y cmake git mariadb-server mariadb-devel openssl-devel tbb-devel zlib-devel wget unzip tmux curl
;;
apt)
apt update && apt upgrade -y
DEBIAN_FRONTEND=noninteractive apt install -y \
build-essential cmake git mariadb-server libmariadb-dev \
libssl-dev libtbb-dev zlib1g-dev wget unzip tmux curl ca-certificates
;;
esac
# ---------- 3. ACE 6.5.16 ----------
ACE_VER="6.5.16"
ACE_ROOT="/usr/local/src/ACE_wrappers"
export ACE_ROOT
export LD_LIBRARY_PATH="/usr/local/lib:$ACE_ROOT/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
if [[ ! -f /usr/local/lib/libACE.so ]]; then
echo "Building ACE $ACE_VER..."
wget -q "https://github.com/DOCGroup/ACE_TAO/releases/download/ACE%2BTAO-6_5_16/ACE-$ACE_VER.tar.gz" -O /tmp/ace.tar.gz
tar -xf /tmp/ace.tar.gz -C /usr/local/src
rm /tmp/ace.tar.gz
echo '#include "ace/config-linux.h"' > "$ACE_ROOT/ace/config.h"
printf 'include $(ACE_ROOT)/include/makeinclude/platform_linux.GNU\nINSTALL_PREFIX = /usr/local\n' > "$ACE_ROOT/include/makeinclude/platform_macros.GNU"
make -C "$ACE_ROOT" -j$(nproc)
make -C "$ACE_ROOT" install
fi
# ---------- 4. Clone / update source ----------
SRC="$INSTALL/core"
mkdir -p "$INSTALL"
chown -R "$SUDO_USER:$SUDO_USER" "$INSTALL"
if [[ ! -d "$SRC" ]]; then
sudo -u "$SUDO_USER" git clone https://github.com/vmangos/core.git "$SRC"
fi
cd "$SRC"
sudo -u "$SUDO_USER" git fetch --tags --prune --prune-tags --force
sudo -u "$SUDO_USER" git pull --ff-only
# ---------- 4.5 Build extractors? ----------
BUILD_EXTRACTORS=false
if [[ ! -d "$INSTALL/extractors" ]]; then
read -p "Build map/vmap/mmap extractors? (y/N): " -n 1 -r; echo
[[ $REPLY =~ ^[Yy]$ ]] && BUILD_EXTRACTORS=true
fi
[[ "$BUILD_EXTRACTORS" == true ]] && mkdir -p "$INSTALL/extractors" && chown -R "$SUDO_USER:$SUDO_USER" "$INSTALL/extractors"
# ---------- 5. Compile VMaNGOS ----------
BUILD_DIR="$SRC/build"
sudo -u "$SUDO_USER" mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
CMAKE_OPTS=(
-DCMAKE_INSTALL_PREFIX="$INSTALL"
-DSUPPORTED_CLIENT_BUILD="$BUILD"
-DUSE_STD_MALLOC=ON
-DUSE_ANTICHEAT=ON
-DCMAKE_BUILD_TYPE=Release
-DACE_ROOT="$ACE_ROOT"
)
[[ "$BUILD_EXTRACTORS" == true ]] && CMAKE_OPTS+=(-DUSE_EXTRACTORS=1 -DEXTRACTORS_INSTALL_DIR="$INSTALL/extractors")
sudo -u "$SUDO_USER" cmake .. "${CMAKE_OPTS[@]}"
sudo -u "$SUDO_USER" make -j$(nproc)
sudo -u "$SUDO_USER" make install
# Versioned binaries
sudo -u "$SUDO_USER" mkdir -p "$INSTALL/bin"
[[ -f "$INSTALL/bin/mangosd" ]] && sudo -u "$SUDO_USER" mv "$INSTALL/bin/mangosd" "$INSTALL/bin/mangosd_$BUILD"
[[ -f "$INSTALL/bin/realmd" ]] && sudo -u "$SUDO_USER" mv "$INSTALL/bin/realmd" "$INSTALL/bin/realmd_$BUILD"
# ---------- 6. Create DB ----------
systemctl enable --now mariadb.service
# DB user/password
read -rp "Database user [mangos]: " DB_USER; DB_USER=${DB_USER:-mangos}
read -rsp "Database password [mangos]: " DB_PASS; echo; DB_PASS=${DB_PASS:-mangos}
echo "Setting up fresh database..."
mariadb -u root <<-EOF
DROP DATABASE IF EXISTS realmd;
DROP DATABASE IF EXISTS mangos;
DROP DATABASE IF EXISTS characters;
DROP DATABASE IF EXISTS logs;
CREATE DATABASE realmd DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE DATABASE mangos DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE DATABASE characters DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE DATABASE logs DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
DROP USER IF EXISTS '$DB_USER'@'localhost';
CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
GRANT ALL PRIVILEGES ON realmd.* TO '$DB_USER'@'localhost';
GRANT ALL PRIVILEGES ON mangos.* TO '$DB_USER'@'localhost';
GRANT ALL PRIVILEGES ON characters.* TO '$DB_USER'@'localhost';
GRANT ALL PRIVILEGES ON logs.* TO '$DB_USER'@'localhost';
FLUSH PRIVILEGES;
EOF
# ---------- 7. Download & import latest database ----------
echo "Downloading latest full database..."
ZIP_URL=$(curl -s https://api.github.com/repos/vmangos/core/releases/tags/db_latest \
| grep -o '"name": "db-[0-9a-f]*\.zip"' \
| sed 's/"name": "\([^"]*\)"/\1/' \
| head -n1 \
| sed 's/^/https:\/\/github.com\/vmangos\/core\/releases\/download\/db_latest\//' )
wget -qO /tmp/db-latest.zip "$ZIP_URL"
unzip -o /tmp/db-latest.zip -d /tmp
mariadb -u"$DB_USER" -p"$DB_PASS" --max-allowed-packet=256M realmd < /tmp/db_dump/logon.sql
mariadb -u"$DB_USER" -p"$DB_PASS" --max-allowed-packet=256M characters < /tmp/db_dump/characters.sql
mariadb -u"$DB_USER" -p"$DB_PASS" --max-allowed-packet=256M mangos < /tmp/db_dump/mangos.sql
mariadb -u"$DB_USER" -p"$DB_PASS" --max-allowed-packet=256M logs < /tmp/db_dump/logs.sql
rm -rf /tmp/db-latest.zip /tmp/db_dump
echo "Database imported successfully."
# ---------- 8. Configuration files ----------
sudo -u "$SUDO_USER" mkdir -p "$INSTALL/etc" "$INSTALL/data" "$INSTALL/logs"
[[ ! -f "$INSTALL/etc/mangosd.conf" ]] && sudo -u "$SUDO_USER" cp "$SRC/src/mangosd/mangosd.conf.dist.in" "$INSTALL/etc/mangosd.conf"
[[ ! -f "$INSTALL/etc/realmd.conf" ]] && sudo -u "$SUDO_USER" cp "$SRC/src/realmd/realmd.conf.dist.in" "$INSTALL/etc/realmd.conf"
sudo -u "$SUDO_USER" sed -i "s|^DataDir.*|DataDir = \"$INSTALL/data\"|" "$INSTALL/etc/mangosd.conf"
sudo -u "$SUDO_USER" sed -i "s|^LogsDir.*|LogsDir = \"$INSTALL/logs\"|" "$INSTALL/etc/mangosd.conf"
sudo -u "$SUDO_USER" sed -i "s|^LogsDir.*|LogsDir = \"$INSTALL/logs\"|" "$INSTALL/etc/realmd.conf"
# ---------- 9. Realm list + admin account ----------
mariadb -u"$DB_USER" -p"$DB_PASS" realmd <<-EOF
DELETE FROM realmlist WHERE id = 1;
INSERT INTO realmlist (id, name, address, port, icon, realmflags, timezone, allowedSecurityLevel, population)
VALUES (1, 'VMaNGOS $PATCH', '127.0.0.1', 8085, 1, 0, 1, 0, 0)
ON DUPLICATE KEY UPDATE name=VALUES(name), address=VALUES(address), port=VALUES(port);
EOF
mariadb -u"$DB_USER" -p"$DB_PASS" realmd <<-EOF
INSERT INTO account (username, gmlevel, v, s, joindate, last_ip)
VALUES ('ADMIN', 6,
UPPER(HEX(UNHEX('6142D50FAEE50222E3DB6F6B8E035B94C630002A6BCA32A0B051BBC3C1B17434'))),
UPPER(HEX(UNHEX('A1775C51EA9C621BA2715A1D9E5366F58DA050D466D30323F41B3A6020DCD3C9'))),
NOW(), '127.0.0.1')
ON DUPLICATE KEY UPDATE v=VALUES(v), s=VALUES(s);
INSERT IGNORE INTO account_access (id, gmlevel, RealmID)
SELECT id, 6, -1 FROM account WHERE username = 'ADMIN';
EOF
# ---------- 10. Save installation state ----------
GIT_HASH=$(cd "$SRC" && git rev-parse --short HEAD)
mkdir -p "$(dirname "$STATE_FILE")"
sed -i "/^\[$BUILD\]/,/^$/d" "$STATE_FILE" 2>/dev/null || true
cat >> "$STATE_FILE" <<EOF
[$BUILD]
patch=$PATCH
git_hash=$GIT_HASH
EOF
chown "$SUDO_USER:$SUDO_USER" "$STATE_FILE"
# ---------- 11. Create handy start/stop scripts in $INSTALL ----------
cat > "$INSTALL/start-realmd.sh" <<'EOF'
#!/bin/bash
INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD=$(ls "$INSTALL_DIR/bin"/realmd_* 2>/dev/null | head -1 | sed 's/.*realmd_//')
[[ -z "$BUILD" ]] && { echo "Error: No realmd binary found!"; exit 1; }
if tmux has-session -t realmd 2>/dev/null; then
echo "realmd already running → attaching..."
tmux attach-session -t realmd
exit 0
fi
echo "Starting realmd (build $BUILD)..."
tmux new-session -s realmd "$INSTALL_DIR/bin/realmd_$BUILD -c $INSTALL_DIR/etc/realmd.conf"
EOF
cat > "$INSTALL/start-mangosd.sh" <<'EOF'
#!/bin/bash
INSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD=$(ls "$INSTALL_DIR/bin"/mangosd_* 2>/dev/null | head -1 | sed 's/.*mangosd_//')
[[ -z "$BUILD" ]] && { echo "Error: No mangosd binary found!"; exit 1; }
if tmux has-session -t mangosd 2>/dev/null; then
echo "mangosd already running → attaching..."
tmux attach-session -t mangosd
exit 0
fi
echo "Starting mangosd (build $BUILD)..."
tmux new-session -s mangosd "$INSTALL_DIR/bin/mangosd_$BUILD -c $INSTALL_DIR/etc/mangosd.conf"
EOF
cat > "$INSTALL/stop-realmd.sh" <<'EOF'
#!/bin/bash
if tmux has-session -t realmd 2>/dev/null; then
echo "Stopping realmd..."
tmux send-keys -t realmd "server exit" C-m
sleep 3
tmux kill-session -t realmd 2>/dev/null
echo "realmd stopped."
else
echo "realmd is not running."
fi
EOF
cat > "$INSTALL/stop-mangosd.sh" <<'EOF'
#!/bin/bash
if tmux has-session -t mangosd 2>/dev/null; then
echo "Stopping mangosd..."
tmux send-keys -t mangosd "server shutdown 5" C-m
sleep 8
tmux kill-session -t mangosd 2>/dev/null
echo "mangosd stopped."
else
echo "mangosd is not running."
fi
EOF
chmod +x "$INSTALL"/start-*.sh "$INSTALL"/stop-*.sh
chown "$SUDO_USER:$SUDO_USER" "$INSTALL"/*.sh
# ---------- 11.5 Create extractor script ----------
if [[ "$BUILD_EXTRACTORS" == true ]]; then
cat > "$INSTALL/bin/Extractors/run_extractors.sh" << SCRIPT_EOF
#!/bin/bash
set -e
trap 'cleanup_and_exit' INT TERM HUP
BIN_DIR="\$(cd "\$(dirname "\$0")" && pwd)"
DATA_DIR="$INSTALL/data"
BUILD_NUMBER="$BUILD"
main () {
get_wow_client_path
run_extractors
cleanup_and_exit
}
get_wow_client_path () {
echo "=== VMaNGOS Map Extractor ==="
echo "Please provide the path to your WoW client directory (containing Data/ folder)"
read -e -p "WoW client path: " WOW_CLIENT_PATH
# Remove surrounding quotes if present (but keep the path for later use)
CLEAN_PATH=\$(echo "\$WOW_CLIENT_PATH" | sed -e 's/^"//' -e 's/"\$//' -e "s/^'//" -e "s/'\$//")
# Expand tilde to home directory
CLEAN_PATH="\${CLEAN_PATH/#\~/\$HOME}"
# Validate the path contains Data folder
if [[ ! -d "\$CLEAN_PATH/Data" ]]; then
echo "Error: Data/ folder not found in \$CLEAN_PATH"
echo "Please make sure you provide the correct WoW client directory."
echo "The path should contain a 'Data' folder with game data files."
exit 1
fi
# Use the clean path for the rest of the script
WOW_CLIENT_PATH="\$CLEAN_PATH"
echo "✓ Using WoW client data from: \$WOW_CLIENT_PATH/Data"
echo "✓ Build number: \$BUILD_NUMBER"
echo
}
echoerr () {
echo "\$@" 1>&2
}
run_extractors () {
echo "Starting map extraction..."
echo
# Create data directory
mkdir -p "\$DATA_DIR"
echo "Step 1: Extracting maps and DBC files..."
"\$BIN_DIR/MapExtractor" -i "\$WOW_CLIENT_PATH" -o "\$DATA_DIR" -e 7 && echo "✓ Maps & DBC extracted"
cd "\$DATA_DIR" || { echoerr "Failed to change directory to \$DATA_DIR"; exit 1; }
# Move DBC files to build-specific directory
mkdir -p "\$BUILD_NUMBER"
if [[ -d "dbc" ]]; then
mv "dbc" "\$BUILD_NUMBER/dbc"
echo "✓ DBC files moved to \$BUILD_NUMBER/dbc/"
else
echo "✗ DBC directory not found in \$DATA_DIR/dbc/"
exit 1
fi
echo "Step 2: Extracting VMaps..."
"\$BIN_DIR/VMapExtractor" -d "\$WOW_CLIENT_PATH/Data" && echo "✓ VMaps extracted"
echo "Step 3: Assembling VMaps..."
"\$BIN_DIR/VMapAssembler" && echo "✓ VMaps assembled"
echo "Step 4: Generating movement maps..."
"\$BIN_DIR/MoveMapGenerator" \\
--threads "\$(nproc)" \\
--silent \\
--configInputPath "\$BIN_DIR/config.json" \\
--offMeshInput "\$BIN_DIR/offmesh.txt" && echo "✓ Movement maps generated"
cd - > /dev/null || { echoerr "Failed to navigate to original directory"; exit 1; }
echo
echo "✓ All map extraction completed successfully!"
echo "Extracted data structure:"
echo " \$DATA_DIR/maps/ - Game maps"
echo " \$DATA_DIR/\$BUILD_NUMBER/dbc/ - DBC files"
echo " \$DATA_DIR/vmaps/ - Visual maps"
echo " \$DATA_DIR/mmaps/ - Movement maps"
}
cleanup_and_exit () {
# Cleanup temporary directories
[[ -d "\$DATA_DIR/Buildings" ]] && rm -rf "\$DATA_DIR/Buildings" 2>/dev/null || true
[[ -d "\$DATA_DIR/Cameras" ]] && rm -rf "\$DATA_DIR/Cameras" 2>/dev/null || true
exit 0
}
main "\$@"
SCRIPT_EOF
# Set proper ownership and permissions
chown $SUDO_USER:$SUDO_USER "$INSTALL/bin/Extractors/run_extractors.sh"
chmod +x "$INSTALL/bin/Extractors/run_extractors.sh"
echo "✓ Extractors installed in $INSTALL/bin/Extractors/"
fi
# ---------- Final message ----------
clear
echo "============================================================"
echo "SUCCESS! VMaNGOS $PATCH (build $BUILD) installed successfully!"
echo "Installation directory: $INSTALL"
echo "Binaries: $INSTALL/bin/mangosd_$BUILD"
echo " $INSTALL/bin/realmd_$BUILD"
echo
[[ "$BUILD_EXTRACTORS" == true ]] && echo "Extract data:" && echo " cd $INSTALL/bin/Extractors && ./run_extractors.sh" && echo
echo "Start server:"
echo " cd $INSTALL"
echo " ./start-realmd.sh # opens realmd console"
echo " ./start-mangosd.sh # opens mangosd console"
echo
echo "Or run in background (detached):"
echo " tmux new -d -s realmd './bin/realmd_$BUILD -c etc/realmd.conf'"
echo " tmux new -d -s mangosd './bin/mangosd_$BUILD -c etc/mangosd.conf'"
echo
echo "Login: ADMIN / admin"
echo "Realmlist: 127.0.0.1:8085"
echo "Database: $DB_USER / $DB_PASS"
echo "============================================================"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment