new version commit

This commit is contained in:
mikx
2025-09-29 02:27:58 -04:00
commit 3e8d31e686
9244 changed files with 7357899 additions and 0 deletions

View File

@@ -0,0 +1,651 @@
# AzerothCore Startup Scripts
A comprehensive suite of scripts for managing AzerothCore server instances with advanced session management, automatic restart capabilities, and production-ready service management.
## 📋 Table of Contents
- [Overview](#overview)
- [Components](#components)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Detailed Usage](#detailed-usage)
- [Multiple Realms Setup](#multiple-realms-setup)
- [Service Management](#service-management)
- [Troubleshooting](#troubleshooting)
## 🎯 Overview
The AzerothCore startup scripts provide multiple approaches to running server instances:
1. **Development/Testing**: Simple execution for debugging and development
2. **Production with Restarts**: Automatic restart on crashes with crash detection
3. **Background Services**: Production-ready service management with PM2 or systemd
4. **Session Management**: Interactive console access via tmux/screen
All scripts are integrated into the `acore.sh` dashboard for easy access.
### 📦 Automatic Deployment
**Important**: When you compile AzerothCore using the acore dashboard (`./acore.sh compiler build`), all startup scripts are automatically copied from `apps/startup-scripts/src/` to your `bin/` folder. This means:
-**Portable Deployment**: You can copy the entire `bin/` folder to different servers
-**Self-Contained**: All restart and service management tools travel with your binaries
-**No Additional Setup**: Scripts work immediately after deployment
-**Production Ready**: Deploy to production servers without needing the full source code
This makes it easy to deploy your compiled binaries along with the management scripts to production environments where you may not have the full AzerothCore source code.
## 🔧 Components
### Core Scripts
- **`run-engine`**: Advanced script with session management and configuration priority
- **`simple-restarter`**: Wrapper around starter with restart functionality (legacy compatibility)
- **`starter`**: Basic binary execution with optional GDB support
- **`service-manager.sh`**: Production service management with PM2/systemd
### Configuration
- **`conf.sh.dist`**: Default configuration template
- **`conf.sh`**: User configuration (create from .dist)
- **`gdb.conf`**: GDB debugging configuration
### Examples
- **`restarter-auth.sh`**: Auth server restart example
- **`restarter-world.sh`**: World server restart example
- **`starter-auth.sh`**: Auth server basic start example
- **`starter-world.sh`**: World server basic start example
## 🚀 Quick Start
### 1. Basic Server Start (Development)
```bash
# Start authserver directly
./starter /path/to/bin authserver
# Start worldserver with config
./starter /path/to/bin worldserver "" /path/to/worldserver.conf
```
### 2. Start with Auto-Restart
```bash
# Using simple-restarter (legacy)
./simple-restarter /path/to/bin authserver
# Using run-engine (recommended)
./run-engine restart authserver --bin-path /path/to/bin
```
### 3. Production Service Management
```bash
# Create and start a service
./service-manager.sh create auth authserver --bin-path /path/to/bin
# List all services
./service-manager.sh list
# Stop a service
./service-manager.sh stop auth
```
### 4. Using acore.sh Dashboard
```bash
# Interactive dashboard
./acore.sh
# Direct commands
./acore.sh run-authserver # Start authserver with restart
./acore.sh run-worldserver # Start worldserver with restart
./acore.sh service-manager # Access service manager
```
## ⚙️ Configuration
### Configuration Priority (Highest to Lowest)
1. **`conf.sh`** - User configuration file
2. **Command line arguments** - Runtime parameters
3. **Environment variables** - `RUN_ENGINE_*` variables
4. **`conf.sh.dist`** - Default configuration
### Creating Configuration
```bash
# Copy default configuration
cp scripts/conf.sh.dist scripts/conf.sh
# Edit your configuration
nano scripts/conf.sh
```
### Key Configuration Options
```bash
# Binary settings
export BINPATH="/path/to/azerothcore/bin"
export SERVERBIN="worldserver" # or "authserver"
export CONFIG="/path/to/worldserver.conf"
# Session management
export SESSION_MANAGER="tmux" # none|auto|tmux|screen
export SESSION_NAME="ac-world"
# Interactive mode control
export AC_DISABLE_INTERACTIVE="0" # Set to 1 to disable interactive prompts (useful for non-interactive services)
# Debugging
export GDB_ENABLED="1" # 0 or 1
export GDB="/path/to/gdb.conf"
# Logging
export LOGS_PATH="/path/to/logs"
export CRASHES_PATH="/path/to/crashes"
export LOG_PREFIX_NAME="realm1"
```
## 📖 Detailed Usage
### 1. Run Engine
The `run-engine` is the most advanced script with multiple operation modes:
#### Basic Execution
```bash
# Start server once
./run-engine start worldserver --bin-path /path/to/bin
# Start with configuration file
./run-engine start worldserver --config ./conf-world.sh
# Start with specific server config
./run-engine start worldserver --server-config /path/to/worldserver.conf
```
#### Restart Mode
```bash
# Automatic restart on crash
./run-engine restart worldserver --bin-path /path/to/bin
# Restart with session management
./run-engine restart worldserver --session-manager tmux
```
#### Session Management
```bash
# Start in tmux session
./run-engine start worldserver --session-manager tmux
# Attach to existing session
tmux attach-session -t worldserver
# Start in screen session
./run-engine start worldserver --session-manager screen
# Attach to screen session
screen -r worldserver
```
#### Configuration Options
```bash
./run-engine restart worldserver \
--bin-path /path/to/bin \
--server-config /path/to/worldserver.conf \
--session-manager tmux \
--gdb-enabled 1 \
--logs-path /path/to/logs \
--crashes-path /path/to/crashes
```
### 2. Simple Restarter
Legacy-compatible wrapper with restart functionality:
```bash
# Basic restart
./simple-restarter /path/to/bin worldserver
# With full parameters
./simple-restarter \
/path/to/bin \
worldserver \
./gdb.conf \
/path/to/worldserver.conf \
/path/to/system.log \
/path/to/system.err \
1 \
/path/to/crashes
```
**Parameters:**
1. Binary path (required)
2. Binary name (required)
3. GDB configuration file (optional)
4. Server configuration file (optional)
5. System log file (optional)
6. System error file (optional)
7. GDB enabled flag (0/1, optional)
8. Crashes directory path (optional)
### 3. Starter
Basic execution script without restart functionality:
```bash
# Simple start
./starter /path/to/bin worldserver
# With GDB debugging
./starter /path/to/bin worldserver ./gdb.conf /path/to/worldserver.conf "" "" 1
```
### 4. Service Manager
Production-ready service management:
#### Creating Services
```bash
# Auto-detect provider (PM2 or systemd)
./service-manager.sh create auth authserver --bin-path /path/to/bin
# Force PM2
./service-manager.sh create world worldserver --provider pm2 --bin-path /path/to/bin
# Force systemd
./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/bin
# Create service with restart policy
./service-manager.sh create world worldserver --bin-path /path/to/bin --restart-policy always
```
#### Restart Policies
Services support two restart policies:
- **`on-failure`** (default): Restart only on crashes or errors (exit code != 0, only works with PM2 or systemd without tmux/screen)
- **`always`**: Restart on any exit, including clean shutdown (exit code 0)
**Important**: When using `--restart-policy always`, the in-game command `server shutdown X` will behave like `server restart X` - the service will automatically restart after shutdown. Only the shutdown message differs from a restart message.
```bash
# Service that restarts only on crashes (default behavior)
./service-manager.sh create auth authserver --bin-path /path/to/bin --restart-policy on-failure
# Service that always restarts (even on manual shutdown)
./service-manager.sh create world worldserver --bin-path /path/to/bin --restart-policy always
# Update existing service restart policy
./service-manager.sh update worldserver --restart-policy always
```
#### Service Operations
```bash
# Start/stop services
./service-manager.sh start auth
./service-manager.sh stop world
./service-manager.sh restart auth
# View logs
./service-manager.sh logs world
./service-manager.sh logs world --follow
# Attach to console (interactive)
./service-manager.sh attach world
# List services
./service-manager.sh list
./service-manager.sh list pm2
./service-manager.sh list systemd
# Delete service
./service-manager.sh delete auth
```
#### Health and Console Commands
Use these commands to programmatically check service health and interact with the console (used by CI workflows):
```bash
# Check if service is currently running (exit 0 if running)
./service-manager.sh is-running world
# Print current uptime in seconds (fails if not running)
./service-manager.sh uptime-seconds world
# Wait until uptime >= 10s (optional timeout 240s)
./service-manager.sh wait-uptime world 10 240
# Send a console command (uses pm2 send or tmux/screen)
./service-manager.sh send world "server info"
# Show provider, configs and run-engine settings
./service-manager.sh show-config world
```
Notes:
- For `send`, PM2 provider uses `pm2 send` with the process ID; systemd provider requires a session manager (tmux/screen). If no attachable session is configured, the command fails.
- `wait-uptime` fails with a non-zero exit code if the service does not reach the requested uptime within the timeout window.
#### Service Configuration
```bash
# Update service settings
./service-manager.sh update world --session-manager screen --gdb-enabled 1
# Edit configuration
./service-manager.sh edit world
# Restore missing services from registry
./service-manager.sh restore
```
## 🌍 Multiple Realms Setup
### Method 1: Using Service Manager (Recommended)
```bash
# Create multiple world server instances with different restart policies
./service-manager.sh create world1 worldserver \
--bin-path /path/to/bin \
--server-config /path/to/worldserver-realm1.conf \
--restart-policy on-failure
./service-manager.sh create world2 worldserver \
--bin-path /path/to/bin \
--server-config /path/to/worldserver-realm2.conf \
--restart-policy always
# Single auth server for all realms (always restart for stability)
./service-manager.sh create auth authserver \
--bin-path /path/to/bin \
--server-config /path/to/authserver.conf \
--restart-policy always
```
### Method 2: Using Run Engine with Different Configurations
Create separate configuration files for each realm:
**conf-realm1.sh:**
```bash
export BINPATH="/path/to/bin"
export SERVERBIN="worldserver"
export CONFIG="/path/to/worldserver-realm1.conf"
export SESSION_NAME="ac-realm1"
export LOG_PREFIX_NAME="realm1"
export LOGS_PATH="/path/to/logs/realm1"
```
**conf-realm2.sh:**
```bash
export BINPATH="/path/to/bin"
export SERVERBIN="worldserver"
export CONFIG="/path/to/worldserver-realm2.conf"
export SESSION_NAME="ac-realm2"
export LOG_PREFIX_NAME="realm2"
export LOGS_PATH="/path/to/logs/realm2"
```
Start each realm:
```bash
./run-engine restart worldserver --config ./conf-realm1.sh
./run-engine restart worldserver --config ./conf-realm2.sh
```
### Method 3: Using Examples with Custom Configurations
Copy and modify the example scripts:
```bash
# Copy examples
cp examples/restarter-world.sh restarter-realm1.sh
cp examples/restarter-world.sh restarter-realm2.sh
# Edit each script to point to different configuration files
# Then run:
./restarter-realm1.sh
./restarter-realm2.sh
```
## 🛠️ Service Management
### Service Registry and Persistence
The service manager includes a comprehensive registry system that tracks all created services and enables automatic restoration:
#### Service Registry Features
- **Automatic Tracking**: All services are automatically registered when created
- **Cross-Reboot Persistence**: PM2 services are configured with startup persistence
- **Service Restoration**: Missing services can be detected and restored from registry
- **Migration Support**: Legacy service configurations can be migrated to the new format
#### Using the Registry
```bash
# Check for missing services and restore them
./service-manager.sh restore
# List all registered services (includes status)
./service-manager.sh list
# Services are automatically added to registry on creation
./service-manager.sh create auth authserver --bin-path /path/to/bin
```
#### Custom Configuration Directories
You can customize where service configurations and PM2/systemd files are stored:
```bash
# Set custom directories
export AC_SERVICE_CONFIG_DIR="/path/to/your/project/services"
# Now all service operations will use these custom directories
./service-manager.sh create auth authserver --bin-path /path/to/bin
```
This is particularly useful for:
- **Version Control**: Keep service configurations in your project repository
- **Multiple Projects**: Separate service configurations per project
- **Team Collaboration**: Share service setups across development teams
#### Migration from Legacy Format
If you have existing services in the old format, use the migration script:
```bash
# Migrate existing registry to new format
./migrate-registry.sh
# The script will:
# - Detect old format automatically
# - Create a backup of the old registry
# - Convert to new format with proper tracking
# - Preserve all existing service information
```
### PM2 Services
When using PM2 as the service provider:
* [PM2 CLI Documentation](https://pm2.io/docs/runtime/reference/pm2-cli/)
**Automatic PM2 Persistence**: The service manager automatically configures PM2 for persistence across reboots by:
- Running `pm2 startup` to set up the startup script
- Running `pm2 save` after each service creation/modification
- This ensures your services automatically start when the system reboots
NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` command to connect to the service console because pm2 supports interactive mode.
### Environment Variables
The startup scripts recognize several environment variables for configuration and runtime behavior:
#### Configuration Directory Variables
- **`AC_SERVICE_CONFIG_DIR`**: Override the default configuration directory for services registry and configurations
- Default: `${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services`
- Used for storing service registry and run-engine configurations
#### Service Detection Variables
- **`AC_LAUNCHED_BY_PM2`**: Set to `1` when launched by PM2 (automatically set by service-manager)
- Disables the use of the `unbuffer` command for output capture
- Enables non-interactive mode to prevent prompts
- More robust than relying on PM2's internal variables
- **`AC_DISABLE_INTERACTIVE`**: Controls interactive mode (0=enabled, 1=disabled)
- Automatically set based on execution context
- Prevents AzerothCore from showing interactive prompts in service environments
#### Configuration Variables
- **`RUN_ENGINE_*`**: See [Configuration](#configuration) section for complete list
- **`SERVICE_MODE`**: Set to `true` to enable service-specific behavior
- **`SESSION_MANAGER`**: Override session manager choice (tmux, screen, none, auto)
### Systemd Services
When using systemd as the service provider:
```bash
# Systemd commands
systemctl --user status acore-auth # Check status
systemctl --user logs acore-auth # View logs
systemctl --user restart acore-auth # Restart
systemctl --user enable acore-auth # Enable auto-start
# For system services (requires sudo)
sudo systemctl status acore-auth
sudo systemctl enable acore-auth
```
**Enhanced systemd Integration:**
- **Automatic Service Type**: When using session managers (tmux/screen), services are automatically configured with `Type=forking` for proper daemon behavior
- **Smart ExecStop**: Services with session managers get automatic `ExecStop` commands to properly terminate tmux/screen sessions when stopping the service
- **Non-Interactive Mode**: Services without session managers automatically set `AC_DISABLE_INTERACTIVE=1` to prevent hanging on prompts
### Session Management in Services
Services can be configured with session managers for interactive access:
```bash
# Create service with tmux
./service-manager.sh create world worldserver \
--bin-path /path/to/bin \
--session-manager tmux
# Attach to the session
./service-manager.sh attach world
# or directly:
tmux attach-session -t worldserver
```
## 🎮 Integration with acore.sh Dashboard
The startup scripts are fully integrated into the AzerothCore dashboard:
### Direct Commands
```bash
# Run servers with simple restart (development/testing)
./acore.sh run-worldserver # Option 11 or 'rw'
./acore.sh run-authserver # Option 12 or 'ra'
# Access service manager (production)
./acore.sh service-manager # Option 15 or 'sm'
# Examples:
./acore.sh rw # Quick worldserver start
./acore.sh ra # Quick authserver start
./acore.sh sm create auth authserver --bin-path /path/to/bin
```
### What Happens Behind the Scenes
- **run-worldserver/run-authserver**: Calls `simple-restarter` with appropriate binary
- **service-manager**: Provides full access to the service management interface
- Scripts automatically use the correct binary path from your build configuration
## 🐛 Troubleshooting
### Common Issues
#### 1. Binary Not Found
```bash
Error: Binary '/path/to/bin/worldserver' not found
```
**Solution**: Check binary path and ensure servers are compiled
```bash
# Check if binary exists
ls -la /path/to/bin/worldserver
# Compile if needed
./acore.sh compiler build
```
#### 2. Configuration File Issues
```bash
Error: Configuration file not found
```
**Solution**: Create configuration from template
```bash
cp scripts/conf.sh.dist scripts/conf.sh
# Edit conf.sh with correct paths
```
#### 3. Session Manager Not Available
```bash
Warning: tmux not found, falling back to direct execution
```
**Solution**: Install required session manager
```bash
# Ubuntu/Debian
sudo apt install tmux screen
# CentOS/RHEL
sudo yum install tmux screen
```
#### 4. Permission Issues (systemd)
```bash
Failed to create systemd service
```
**Solution**: Check user permissions or use --system flag
```bash
# For user services (no sudo required)
./service-manager.sh create auth authserver --bin-path /path/to/bin
# For system services (requires sudo)
./service-manager.sh create auth authserver --bin-path /path/to/bin --system
```
#### 5. PM2 Not Found
```bash
Error: PM2 is not installed
```
**Solution**: Install PM2
```bash
npm install -g pm2
# or
sudo npm install -g pm2
```
#### 7. Registry Out of Sync
```bash
# If the service registry shows services that don't actually exist
```
**Solution**: Use registry sync or restore
```bash
# Check and restore missing services (also cleans up orphaned entries)
./service-manager.sh restore
# If you have a very old registry format, migrate it
./migrate-registry.sh
```

1
apps/startup-scripts/src/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
logs

View File

@@ -0,0 +1,57 @@
# AzerothCore Run Engine Default Configuration
# This file contains default values that can be overridden by environment variables
# Priority order: conf.sh > environment variables > conf.sh.dist (this file)
# Enable/disable GDB execution
export GDB_ENABLED="${RUN_ENGINE_GDB_ENABLED:-0}"
# [optional] GDB configuration file
# default: gdb.conf
export GDB="${RUN_ENGINE_GDB:-}"
# Directory where binaries are stored
export BINPATH="${RUN_ENGINE_BINPATH:-}"
# Server binary name (e.g., worldserver, authserver)
export SERVERBIN="${RUN_ENGINE_SERVERBIN:-}"
# Path to server configuration file (including the file name)
# ex: /home/user/azerothcore/etc/worldserver.conf
export CONFIG="${RUN_ENGINE_CONFIG:-}"
# Session manager to use: none|auto|tmux|screen
# auto will detect the best available option
export SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-none}"
# Default session manager (fallback when SESSION_MANAGER is not set)
export DEFAULT_SESSION_MANAGER="${RUN_ENGINE_DEFAULT_SESSION_MANAGER:-none}"
# Path of the crashes directory
# If not specified, it will be created in the same directory as logs named "crashes"
export CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-}"
# Path of log files directory
export LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-}"
# Prefix name for log files to avoid collision with other instances
export LOG_PREFIX_NAME="${RUN_ENGINE_LOG_PREFIX_NAME:-}"
# [optional] Name of session (tmux session or screen session)
# If not specified, a default name will be generated based on server binary
export SESSION_NAME="${RUN_ENGINE_SESSION_NAME:-}"
# [optional] Screen-specific options: -A -m -d -S
# WARNING: if you are running it under a systemd service
# please do not remove -m -d arguments from screen if you are using it,
# or keep WITH_CONSOLE=0. Otherwise the journald-logging system will take
# 100% of CPU slowing down the whole machine.
export SCREEN_OPTIONS="${RUN_ENGINE_SCREEN_OPTIONS:-}"
# Enable/disable console output
# If disabled, output will be redirected to logging files
export WITH_CONSOLE="${RUN_ENGINE_WITH_CONSOLE:-0}"
# Restart policy (on-failure|always)
export RESTART_POLICY="always"

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# AzerothCore Auth Server Restarter Example
# This example shows how to use the run-engine with restart functionality for authserver
PATH_RUNENGINE="./"
CONFIG_FILE="./conf-auth.sh"
# Method 1: Using configuration file (recommended)
if [ -f "$CONFIG_FILE" ]; then
echo "Starting authserver with restart loop using config file: $CONFIG_FILE"
source "$CONFIG_FILE"
"$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE"
else
echo "Error: Configuration file not found: $CONFIG_FILE"
echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
echo "Make sure to set: export SERVERBIN=\"authserver\""
echo ""
echo "Alternative: Start with binary path directly"
echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/authserver"
echo "Example: $PATH_RUNENGINE/run-engine restart authserver # if in PATH"
exit 1
fi
# Method 2: Direct binary path (full path)
# Uncomment the line below to start with full binary path
#
# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf
# Method 3: Binary name only (system PATH)
# Uncomment the line below if authserver is in your system PATH
#
# "$PATH_RUNENGINE/run-engine" restart authserver --server-config /path/to/authserver.conf
# Method 4: With session manager (tmux/screen)
# Uncomment the line below to use tmux session
#
# "$PATH_RUNENGINE/run-engine" restart authserver --session-manager tmux --server-config /path/to/authserver.conf
# Method 5: Environment variables only
# Uncomment the lines below for environment variable configuration
#
# export RUN_ENGINE_BINPATH="/path/to/your/bin"
# export RUN_ENGINE_SERVERBIN="authserver"
# export RUN_ENGINE_CONFIG="/path/to/authserver.conf"
# "$PATH_RUNENGINE/run-engine" restart authserver

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# AzerothCore World Server Restarter Example
# This example shows how to use the run-engine with restart functionality for worldserver
PATH_RUNENGINE="./"
CONFIG_FILE="./conf-world.sh"
# Method 1: Using configuration file (recommended)
if [ -f "$CONFIG_FILE" ]; then
echo "Starting worldserver with restart loop using config file: $CONFIG_FILE"
"$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE"
else
echo "Error: Configuration file not found: $CONFIG_FILE"
echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
echo "Make sure to set: export SERVERBIN=\"worldserver\""
echo ""
echo "Alternative: Start with binary path directly"
echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/worldserver"
echo "Example: $PATH_RUNENGINE/run-engine restart worldserver # if in PATH"
exit 1
fi
# Method 2: Direct binary path (full path)
# Uncomment the line below to start with full binary path
#
# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf
# Method 3: Binary name only (system PATH)
# Uncomment the line below if worldserver is in your system PATH
#
# "$PATH_RUNENGINE/run-engine" restart worldserver --server-config /path/to/worldserver.conf
# Method 4: With session manager (tmux/screen)
# Uncomment the line below to use tmux session
#
# "$PATH_RUNENGINE/run-engine" restart worldserver --session-manager tmux --server-config /path/to/worldserver.conf
# Method 5: Environment variables only
# Uncomment the lines below for environment variable configuration
#
# export RUN_ENGINE_BINPATH="/path/to/your/bin"
# export RUN_ENGINE_SERVERBIN="worldserver"
# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf"
# "$PATH_RUNENGINE/run-engine" restart worldserver

View File

@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# AzerothCore Auth Server Starter Example
# This example shows how to use the run-engine to start authserver without restart loop
PATH_RUNENGINE="./"
CONFIG_FILE="./conf-auth.sh"
# Method 1: Using configuration file (recommended)
if [ -f "$CONFIG_FILE" ]; then
echo "Starting authserver (single run) with config file: $CONFIG_FILE"
"$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE"
else
echo "Error: Configuration file not found: $CONFIG_FILE"
echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
echo "Make sure to set: export SERVERBIN=\"authserver\""
echo ""
echo "Alternative: Start with binary path directly"
echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/authserver"
echo "Example: $PATH_RUNENGINE/run-engine start authserver # if in PATH"
exit 1
fi
# Method 2: Direct binary path (full path)
# Uncomment the line below to start with full binary path
#
# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf
# Method 3: Binary name only (system PATH)
# Uncomment the line below if authserver is in your system PATH
#
# "$PATH_RUNENGINE/run-engine" start authserver --server-config /path/to/authserver.conf
# Method 4: With session manager (tmux/screen)
# Uncomment the line below to use tmux session
#
# "$PATH_RUNENGINE/run-engine" start authserver --session-manager tmux --server-config /path/to/authserver.conf
# Method 5: Environment variables only
# Uncomment the lines below for environment variable configuration
#
# export RUN_ENGINE_BINPATH="/path/to/your/bin"
# export RUN_ENGINE_SERVERBIN="authserver"
# export RUN_ENGINE_CONFIG="/path/to/authserver.conf"
# "$PATH_RUNENGINE/run-engine" start authserver

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# AzerothCore World Server Starter Example
# This example shows how to use the run-engine to start worldserver without restart loop
PATH_RUNENGINE="./"
CONFIG_FILE="./conf-world.sh"
# Method 1: Using configuration file (recommended)
if [ -f "$CONFIG_FILE" ]; then
echo "Starting worldserver (single run) with config file: $CONFIG_FILE"
"$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE"
else
echo "Error: Configuration file not found: $CONFIG_FILE"
echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
echo "Make sure to set: export SERVERBIN=\"worldserver\""
echo ""
echo "Alternative: Start with binary path directly"
echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/worldserver"
echo "Example: $PATH_RUNENGINE/run-engine start worldserver # if in PATH"
exit 1
fi
# Method 2: Direct binary path (full path)
# Uncomment the line below to start with full binary path
#
# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf
# Method 3: Binary name only (system PATH)
# Uncomment the line below if worldserver is in your system PATH
#
# "$PATH_RUNENGINE/run-engine" start worldserver --server-config /path/to/worldserver.conf
# Method 4: With session manager (tmux/screen)
# Uncomment the line below to use tmux session
#
# "$PATH_RUNENGINE/run-engine" start worldserver --session-manager tmux --server-config /path/to/worldserver.conf
# Method 5: Environment variables only
# Uncomment the lines below for environment variable configuration
#
# export RUN_ENGINE_BINPATH="/path/to/your/bin"
# export RUN_ENGINE_SERVERBIN="worldserver"
# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf"
# "$PATH_RUNENGINE/run-engine" start worldserver

View File

@@ -0,0 +1,144 @@
#!/usr/bin/env bash
# One-time migration script for service registry
# Converts old format to new format
set -euo pipefail # Strict error handling
CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}"
REGISTRY_FILE="$CONFIG_DIR/service_registry.json"
BACKUP_FILE="$CONFIG_DIR/service_registry.json.backup"
# Colors
readonly YELLOW='\033[1;33m'
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'
echo -e "${BLUE}AzerothCore Service Registry Migration Tool${NC}"
echo "=============================================="
# Check dependencies
if ! command -v jq >/dev/null 2>&1; then
echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}"
exit 1
fi
# Create config directory if it doesn't exist
mkdir -p "$CONFIG_DIR"
# Check if registry exists
if [ ! -f "$REGISTRY_FILE" ]; then
echo -e "${YELLOW}No registry file found. Nothing to migrate.${NC}"
exit 0
fi
# Validate JSON format
if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then
echo -e "${RED}Error: Registry file contains invalid JSON.${NC}"
echo "Please check the file: $REGISTRY_FILE"
exit 1
fi
# Check if it's already new format
if jq -e 'type == "array" and (length == 0 or .[0] | has("bin_path"))' "$REGISTRY_FILE" >/dev/null 2>&1; then
echo -e "${GREEN}Registry is already in new format. No migration needed.${NC}"
exit 0
fi
# Check if it's old format
if ! jq -e 'type == "array" and (length == 0 or .[0] | has("config"))' "$REGISTRY_FILE" >/dev/null 2>&1; then
echo -e "${YELLOW}Registry format not recognized. Manual review needed.${NC}"
echo "Current registry content:"
cat "$REGISTRY_FILE"
exit 1
fi
echo -e "${YELLOW}Old format detected. Starting migration...${NC}"
# Create backup
if ! cp "$REGISTRY_FILE" "$BACKUP_FILE"; then
echo -e "${RED}Error: Failed to create backup file.${NC}"
exit 1
fi
echo -e "${BLUE}Backup created: $BACKUP_FILE${NC}"
# Convert to new format
echo "[]" > "$REGISTRY_FILE.new"
services_migrated=0
while IFS= read -r service; do
if [ -n "$service" ] && [ "$service" != "null" ]; then
name=$(echo "$service" | jq -r '.name // ""')
provider=$(echo "$service" | jq -r '.provider // ""')
type=$(echo "$service" | jq -r '.type // ""')
config=$(echo "$service" | jq -r '.config // ""')
# Validate required fields
if [ -z "$name" ] || [ -z "$provider" ] || [ -z "$type" ]; then
echo -e "${YELLOW}Skipping invalid service entry: $service${NC}"
continue
fi
echo -e "${YELLOW}Migrating service: $name${NC}"
# Create new format entry with all required fields
new_entry=$(jq -n \
--arg name "$name" \
--arg provider "$provider" \
--arg type "$type" \
--arg bin_path "unknown" \
--arg args "" \
--arg created "$(date -Iseconds)" \
--arg status "migrated" \
--arg systemd_type "--user" \
--arg restart_policy "always" \
--arg session_manager "none" \
--arg gdb_enabled "0" \
--arg pm2_opts "" \
--arg server_config "" \
--arg legacy_config "$config" \
'{
name: $name,
provider: $provider,
type: $type,
bin_path: $bin_path,
args: $args,
created: $created,
status: $status,
systemd_type: $systemd_type,
restart_policy: $restart_policy,
session_manager: $session_manager,
gdb_enabled: $gdb_enabled,
pm2_opts: $pm2_opts,
server_config: $server_config,
legacy_config: $legacy_config
}')
# Add to new registry with error checking
if ! jq --argjson entry "$new_entry" '. += [$entry]' "$REGISTRY_FILE.new" > "$REGISTRY_FILE.new.tmp"; then
echo -e "${RED}Error: Failed to add service $name to new registry${NC}"
rm -f "$REGISTRY_FILE.new" "$REGISTRY_FILE.new.tmp"
exit 1
fi
mv "$REGISTRY_FILE.new.tmp" "$REGISTRY_FILE.new"
services_migrated=$((services_migrated + 1))
fi
done < <(jq -c '.[]?' "$BACKUP_FILE" 2>/dev/null || echo "")
# Replace old registry with new one
if ! mv "$REGISTRY_FILE.new" "$REGISTRY_FILE"; then
echo -e "${RED}Error: Failed to replace old registry with new one${NC}"
exit 1
fi
echo -e "${GREEN}Migration completed successfully!${NC}"
echo -e "${BLUE}Services migrated: $services_migrated${NC}"
echo -e "${BLUE}Use 'service-manager.sh restore' to review and update services.${NC}"
echo -e "${YELLOW}Note: Migrated services have bin_path='unknown' and need manual recreation.${NC}"
echo ""
echo -e "${BLUE}To recreate services, use commands like:${NC}"
echo " ./service-manager.sh create auth authserver --provider pm2 --bin-path /path/to/your/bin"
echo " ./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/your/bin"

View File

@@ -0,0 +1,483 @@
#!/usr/bin/env bash
# AzerothCore Run Engine
# Advanced script for running AzerothCore services with session management and restart capabilities
#
# This script can be sourced to provide functions or executed directly with parameters
#
# Configuration Priority Order (highest to lowest):
# 1. conf.sh - User configuration file (highest priority)
# 2. Command line arguments (--config, --server-config, etc.)
# 3. Environment variables (RUN_ENGINE_*)
# 4. conf.sh.dist - Default configuration (lowest priority)
#
# Environment Variables:
# RUN_ENGINE_CONFIG_FILE - Path to temporary configuration file (optional)
# RUN_ENGINE_SESSION_MANAGER - Session manager (none|auto|tmux|screen, default: auto)
# RUN_ENGINE_BINPATH - Binary directory path
# RUN_ENGINE_SERVERBIN - Server binary name (worldserver|authserver)
# RUN_ENGINE_CONFIG - Server configuration file path
# RUN_ENGINE_LOGS_PATH - Directory for log files
# RUN_ENGINE_CRASHES_PATH - Directory for crash dumps
# RUN_ENGINE_SESSION_NAME - Session name for tmux/screen
export RUN_ENGINE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Configuration priority order:
# 1. conf.sh (highest priority - user overrides)
# 2. Environment variables (RUN_ENGINE_*)
# 3. conf.sh.dist (lowest priority - defaults)
# Load default configuration first (sets defaults from environment variables)
if [ -e "$RUN_ENGINE_PATH/conf.sh.dist" ]; then
source "$RUN_ENGINE_PATH/conf.sh.dist"
fi
# Load user configuration if exists (this takes priority over everything)
if [ -e "$RUN_ENGINE_PATH/conf.sh" ]; then
source "$RUN_ENGINE_PATH/conf.sh"
fi
# Load configuration
function load_config() {
local config_file="$1"
# If a specific config file is provided via command line, load it
# This allows temporary overrides for specific runs
if [ -n "$config_file" ] && [ -e "$config_file" ]; then
echo "Loading configuration from: $config_file"
source "$config_file"
elif [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -e "$RUN_ENGINE_CONFIG_FILE" ]; then
echo "Loading configuration from environment: $RUN_ENGINE_CONFIG_FILE"
source "$RUN_ENGINE_CONFIG_FILE"
fi
# Final override with any remaining environment variables
# This ensures that even after loading config files, environment variables take precedence
BINPATH="${RUN_ENGINE_BINPATH:-$BINPATH}"
SERVERBIN="${RUN_ENGINE_SERVERBIN:-$SERVERBIN}"
CONFIG="${RUN_ENGINE_CONFIG:-$CONFIG}"
SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-$SESSION_MANAGER}"
LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-$LOGS_PATH}"
CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-$CRASHES_PATH}"
}
# Detect available session manager
function detect_session_manager() {
if command -v tmux >/dev/null 2>&1; then
echo "tmux"
elif command -v screen >/dev/null 2>&1; then
echo "screen"
else
echo "none"
fi
}
# Determine which session manager to use
function get_session_manager() {
local requested="$1"
case "$requested" in
"none")
echo "none"
;;
"auto")
detect_session_manager
;;
"tmux")
if command -v tmux >/dev/null 2>&1; then
echo "tmux"
else
echo "error"
fi
;;
"screen")
if command -v screen >/dev/null 2>&1; then
echo "screen"
else
echo "error"
fi
;;
*)
echo "none"
;;
esac
}
# Configure log files
function configure_files() {
TRACE_BEGIN_STRING="SIGSEGV"
TRACE_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_trace.log"
ERR_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_error.log"
SYSLOG="$LOGS_PATH/${LOG_PREFIX_NAME}_system.log"
SYSERR="$LOGS_PATH/${LOG_PREFIX_NAME}_system.err"
LINKS_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_crash_links.link"
}
# Check if service is running
function check_status() {
local session_name="$1"
local ret=1
# Check for GDB process
local gdbres=$(pgrep -f "gdb.*--batch.*$SERVERBIN")
if [[ "$GDB_ENABLED" -eq 1 && -n "$gdbres" ]]; then
return 1
fi
# Check for binary process
local binres=$(pgrep -f "$SERVERBIN -c $CONFIG")
if [ -n "$binres" ]; then
return 1
fi
# Check session manager
if [ -n "$session_name" ]; then
case "$(get_session_manager "${SESSION_MANAGER:-auto}")" in
"tmux")
tmux has-session -t "$session_name" 2>/dev/null && return 1
;;
"screen")
screen -ls "$session_name" 2>/dev/null | grep -q "$session_name" && return 1
;;
esac
fi
return 0
}
# Run with session manager
function run_with_session() {
local session_manager="$1"
local session_name="$2"
local wrapper="$3"
shift 3
local args=("$@")
if [ "$wrapper" = "simple-restarter" ]; then
script_path="$RUN_ENGINE_PATH/simple-restarter"
else
script_path="$RUN_ENGINE_PATH/starter"
fi
case "$session_manager" in
"tmux")
echo "> Starting with tmux session: $session_name - attach with 'tmux attach -t $session_name'"
tmux new-session -d -s "$session_name" -- "$script_path" "${args[@]}"
;;
"screen")
local OPTIONS="-A -m -d -S"
if [ -n "$SCREEN_OPTIONS" ]; then
OPTIONS="$SCREEN_OPTIONS"
fi
echo "> Starting with screen session: $session_name (options: $OPTIONS) - attach with 'screen -r $session_name'"
echo "screen $OPTIONS \"$session_name\" -- \"$script_path\" ${args[*]}"
screen $OPTIONS "$session_name" -- "$script_path" "${args[@]}"
;;
"none"|*)
echo "> Starting without session manager"
"$script_path" "${args[@]}"
;;
esac
}
# Parse command line arguments
function parse_arguments() {
local mode="$1"
local serverbin="$2"
shift 2
local config_file=""
local serverconfig=""
local session_manager=""
# Parse named arguments
while [[ $# -gt 0 ]]; do
case $1 in
--config)
config_file="$2"
shift 2
;;
--server-config)
serverconfig="$2"
shift 2
;;
--session-manager)
session_manager="$2"
shift 2
;;
*)
echo "Unknown argument: $1"
return 1
;;
esac
done
# Export parsed values for use by start_service
export PARSED_MODE="$mode"
export PARSED_SERVERBIN="$serverbin"
export PARSED_CONFIG_FILE="$config_file"
export PARSED_SERVERCONFIG="$serverconfig"
export PARSED_SESSION_MANAGER="$session_manager"
}
# Start service (single run or with simple-restarter)
function start_service() {
local config_file="$1"
local serverbin_path="$2"
local serverconfig="$3"
local use_restarter="${4:-false}"
local session_manager_choice="$5"
# Load configuration first
load_config "$config_file"
# if no session manager is specified, get it from config
if [ -z "$session_manager_choice" ]; then
session_manager_choice="$SESSION_MANAGER"
fi
# Parse serverbin_path to extract BINPATH and SERVERBIN
if [ -n "$serverbin_path" ]; then
# If it's a full path, extract directory and binary name
if [[ "$serverbin_path" == */* ]]; then
BINPATH="$(dirname "$serverbin_path")"
SERVERBIN="$(basename "$serverbin_path")"
else
# If it's just a binary name, use it as-is (system PATH)
SERVERBIN="$serverbin_path"
BINPATH="${BINPATH:-""}" # Empty means use current directory or system PATH
fi
fi
# Use environment/config values if not set from command line
BINPATH="${BINPATH:-$RUN_ENGINE_BINPATH}"
SERVERBIN="${SERVERBIN:-$RUN_ENGINE_SERVERBIN}"
CONFIG="${serverconfig:-$CONFIG}"
echo "SERVERBIN: $SERVERBIN"
# Validate required parameters
if [ -z "$SERVERBIN" ]; then
echo "Error: SERVERBIN is required"
echo "Could not determine server binary from: $serverbin_path"
echo "Provide it as:"
echo " - Full path: $0 <mode> /path/to/bin/worldserver"
echo " - Binary name: $0 <mode> worldserver"
echo " - Environment variables: RUN_ENGINE_SERVERBIN"
echo " - Configuration file with SERVERBIN variable"
return 1
fi
# If BINPATH is set, validate binary exists and create log paths
if [ -n "$BINPATH" ]; then
if [ ! -d "$BINPATH" ]; then
echo "Error: BINPATH not found: $BINPATH"
return 1
fi
# Set up directories and logging relative to BINPATH
LOGS_PATH="${LOGS_PATH:-"$BINPATH/logs"}"
CRASHES_PATH="${CRASHES_PATH:-"$BINPATH/crashes"}"
mkdir -p "$LOGS_PATH"
mkdir -p "$CRASHES_PATH"
else
# For system binaries, try to detect binary location and create logs accordingly
local detected_binpath=""
# Try to find binary in system PATH
local binary_location=$(which "$SERVERBIN" 2>/dev/null)
if [ -n "$binary_location" ]; then
detected_binpath="$(dirname "$binary_location")"
echo "Binary found in system PATH: $binary_location"
# Set BINPATH to the detected location so starter script can find the binary
BINPATH="$detected_binpath"
fi
# Set up log paths based on detected or fallback location
if [ -n "$detected_binpath" ]; then
LOGS_PATH="${LOGS_PATH:-"$detected_binpath/logs"}"
CRASHES_PATH="${CRASHES_PATH:-"$detected_binpath/crashes"}"
else
# Fallback to current directory for logs
LOGS_PATH="${LOGS_PATH:-./logs}"
CRASHES_PATH="${CRASHES_PATH:-"$./crashes"}"
fi
mkdir -p "$LOGS_PATH"
mkdir -p "$CRASHES_PATH"
fi
# Set up logging names
LOG_PREFIX_NAME="${LOG_PREFIX_NAME:-${SERVERBIN%server}}"
# Set up session name (with backward compatibility for SCREEN_NAME)
SESSION_NAME="${SESSION_NAME:-$SCREEN_NAME}"
SESSION_NAME="${SESSION_NAME:-AC-${SERVERBIN%server}}"
configure_files
local session_manager=$(get_session_manager "$session_manager_choice")
if [ "$session_manager" = "error" ]; then
echo "Error: Invalid session manager specified: $session_manager_choice, is it installed?"
exit 1
fi
echo "Using session manager: $session_manager"
echo "Starting server: $SERVERBIN"
if [ -n "$CONFIG" ]; then
echo "Server config: $CONFIG"
else
echo "Server config: default (not specified)"
fi
# Set AC_DISABLE_INTERACTIVE when running as a service without interactive session manager
# This prevents AzerothCore from showing interactive prompts when running under systemd/pm2
if [[ "${SERVICE_MODE:-false}" == "true" && "$session_manager" == "none" ]]; then
export AC_DISABLE_INTERACTIVE=1
echo "Service mode: Non-interactive mode enabled (AC_DISABLE_INTERACTIVE=1)"
else
export AC_DISABLE_INTERACTIVE=0
if [[ "${SERVICE_MODE:-false}" == "true" ]]; then
echo "Service mode: Interactive mode enabled (session manager: $session_manager)"
else
echo "Direct execution: Interactive mode enabled"
fi
fi
if [ "$use_restarter" = "true" ]; then
# Use simple-restarter for restart functionality
local gdb_enabled="${GDB_ENABLED:-0}"
run_with_session "$session_manager" "$SESSION_NAME" "simple-restarter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH"
else
# Single run using starter
local gdb_enabled="${GDB_ENABLED:-0}"
run_with_session "$session_manager" "$SESSION_NAME" "starter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH"
fi
}
# Cleanup function
function finish() {
local session_manager=$(get_session_manager "${SESSION_MANAGER:-auto}")
if [ -n "$SESSION_NAME" ]; then
case "$session_manager" in
"tmux")
tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
;;
"screen")
screen -X -S "$SESSION_NAME" quit 2>/dev/null || true
;;
esac
fi
}
# Legacy compatibility functions for old examples
function restarter() {
echo "Legacy function 'restarter' called - redirecting to new API"
start_service "" "" "" "true" "${SESSION_MANAGER:-auto}"
}
function starter() {
echo "Legacy function 'starter' called - redirecting to new API"
start_service "" "" "" "false" "${SESSION_MANAGER:-auto}"
}
# Set trap for cleanup (currently disabled to avoid interfering with systemd)
# trap finish EXIT
# Main execution when script is run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
case "${1:-help}" in
"start"|"restart")
if [ $# -lt 2 ]; then
echo "Error: Missing required arguments"
echo "Usage: $0 <mode> <serverbin> [options]"
echo "Example: $0 start worldserver --config ./conf-world.sh --server-config worldserver.conf"
exit 1
fi
# Parse arguments
if ! parse_arguments "$@"; then
exit 1
fi
# Determine restart mode
use_restarter="false"
if [ "$PARSED_MODE" = "restart" ]; then
use_restarter="true"
fi
# Start service with parsed arguments
start_service "$PARSED_CONFIG_FILE" "$PARSED_SERVERBIN" "$PARSED_SERVERCONFIG" "$use_restarter" "$PARSED_SESSION_MANAGER"
;;
"help"|*)
echo "AzerothCore Run Engine"
echo ""
echo "Usage: $0 <mode> <serverbin> [options]"
echo ""
echo "Modes:"
echo " start - Start service once (no restart on crash)"
echo " restart - Start service with restart on crash (uses simple-restarter)"
echo ""
echo "Required Parameters:"
echo " serverbin - Server binary (full path or binary name)"
echo " Full path: /path/to/bin/worldserver"
echo " Binary name: worldserver (uses system PATH)"
echo ""
echo "Options:"
echo " --config <file> - Path to configuration file"
echo " --server-config <file> - Server configuration file (sets -c parameter)"
echo " --session-manager <type> - Session manager: none|auto|tmux|screen (default: auto)"
echo ""
echo "Configuration Priority (highest to lowest):"
echo " 1. conf.sh - User configuration file"
echo " 2. Command line arguments (--config, --server-config, etc.)"
echo " 3. Environment variables (RUN_ENGINE_*)"
echo " 4. conf.sh.dist - Default configuration"
echo ""
echo "Environment Variables:"
echo " RUN_ENGINE_CONFIG_FILE - Config file path"
echo " RUN_ENGINE_SESSION_MANAGER - Session manager (default: auto)"
echo " RUN_ENGINE_BINPATH - Binary directory path"
echo " RUN_ENGINE_SERVERBIN - Server binary name"
echo " RUN_ENGINE_CONFIG - Server configuration file"
echo " RUN_ENGINE_LOGS_PATH - Directory for log files"
echo " RUN_ENGINE_CRASHES_PATH - Directory for crash dumps"
echo " RUN_ENGINE_SESSION_NAME - Session name for tmux/screen"
echo ""
echo "Examples:"
echo ""
echo " # Using full path to binary"
echo " $0 start /home/user/ac/bin/worldserver"
echo ""
echo " # Using binary name (system PATH)"
echo " $0 start worldserver"
echo ""
echo " # With configuration file"
echo " $0 start worldserver --config ./conf-world.sh"
echo ""
echo " # With server configuration (sets -c parameter)"
echo " $0 start /path/to/bin/worldserver --server-config /etc/worldserver.conf"
echo ""
echo " # With session manager"
echo " $0 restart worldserver --session-manager tmux"
echo ""
echo " # Complete example"
echo " $0 restart /home/user/ac/bin/worldserver --config ./conf-world.sh --server-config worldserver.conf --session-manager screen"
echo ""
echo "Binary Resolution:"
echo " - Full path (contains /): Extracts directory and binary name"
echo " - Binary name only: Uses system PATH to find executable"
echo " Auto-detection will check current directory first, then system PATH"
echo ""
echo "Server Config:"
echo " If --server-config is specified, it's passed as -c parameter to the server."
echo " If not specified, the server will use its default configuration."
;;
esac
fi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# AzerothCore Simple Restarter
# This script is a wrapper around the starter script that provides restart functionality
# and maintains compatibility with the acore dashboard
#
# Usage: simple-restarter <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
#
# Parameters (same as starter):
# $1 - Binary to execute (required)
# $2 - GDB configuration file (optional)
# $3 - Configuration file path (optional)
# $4 - System log file (optional)
# $5 - System error file (optional)
# $6 - GDB enabled flag (0/1, optional)
# $7 - Crashes directory path (optional)
# Get script directory
CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Parameters (same as starter)
BINPATH="$1"
BINFILE="$2"
GDB_FILE="$3"
CONFIG="$4"
SYSLOG="$5"
SYSERR="$6"
GDB_ENABLED="${7:-0}"
CRASHES_PATH="$8"
BINARY="$BINPATH/$BINFILE"
# Default values (same as starter)
DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf"
# Set defaults if not provided
GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}"
# Counters for crash detection
_instant_crash_count=0
_restart_count=0
# Check if starter script exists
STARTER_SCRIPT="$CURRENT_PATH/starter"
if [ ! -f "$STARTER_SCRIPT" ]; then
echo "Error: starter script not found at $STARTER_SCRIPT"
exit 1
fi
# Main restart loop
while true; do
STARTING_TIME=$(date +%s)
# Use starter script to launch the binary with all parameters
"$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH"
_exit_code=$?
echo "$(basename "$BINARY") terminated with exit code: $_exit_code"
# Calculate runtime
ENDING_TIME=$(date +%s)
DIFFERENCE=$((ENDING_TIME - STARTING_TIME))
((_restart_count++))
echo "$(basename "$BINARY") terminated after $DIFFERENCE seconds, restart count: $_restart_count"
# Crash loop detection
if [ "$DIFFERENCE" -lt 10 ]; then
# Increment instant crash count if runtime is lower than 10 seconds
((_instant_crash_count++))
echo "Warning: Quick restart detected ($DIFFERENCE seconds) - instant crash count: $_instant_crash_count"
else
# Reset count on successful longer run
_instant_crash_count=0
fi
# Prevent infinite crash loops
if [ "$_instant_crash_count" -gt 5 ]; then
echo "Error: $(basename "$BINARY") restarter exited. Infinite crash loop prevented (6 crashes in under 10 seconds each)"
echo "Please check your system configuration and logs"
exit 1
fi
# Exit cleanly if shutdown was requested by command or SIGINT (exit code 0)
if [ "$_exit_code" -eq 0 ]; then
echo "$(basename "$BINARY") shutdown safely"
exit 0
fi
echo "$(basename "$BINARY") will restart in 3 seconds..."
sleep 3
done

View File

@@ -0,0 +1,151 @@
#!/usr/bin/env bash
# AzerothCore Starter Script
# This script handles the execution of AzerothCore binaries with optional GDB support
#
# Usage: starter <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
#
# Parameters:
# $1 - Binary path (required)
# $2 - Binary file name (required)
# $3 - GDB configuration file (optional)
# $4 - Configuration file path (optional)
# $5 - System log file (optional)
# $6 - System error file (optional)
# $7 - GDB enabled flag (0/1, optional)
# $8 - Crashes directory path (optional)
BINPATH="$1"
BINFILE="$2"
GDB_FILE="$3"
CONFIG="$4"
SYSLOG="$5"
SYSERR="$6"
GDB_ENABLED="${7:-0}"
CRASHES_PATH="$8"
# Default values
CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEFAULT_CRASHES_PATH=$(realpath "$BINPATH/crashes")
[ -n "$CONFIG" ] && CONFIG_ABS=$(realpath "$CONFIG")
# Set defaults if not provided
CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
# Validate binary
if [ -z "$BINPATH" ] || [ -z "$BINFILE" ]; then
echo "Error: Binary path and file are required"
echo "Usage: $0 <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]"
exit 1
fi
BINARY="$BINPATH/$BINFILE"
if [ ! -f "$BINARY" ]; then
echo "Error: Binary '$BINARY' not found"
exit 1
fi
# Create crashes directory if it doesn't exist
mkdir -p "$CRASHES_PATH"
cd "$BINPATH" || {
echo "Error: Could not change to binary path '$BINPATH'"
exit 1
}
EXECPATH=$(realpath "$BINFILE")
if [ "$GDB_ENABLED" -eq 1 ]; then
echo "Starting $EXECPATH with GDB enabled"
# Generate GDB configuration on the fly
TIMESTAMP=$(date +%Y-%m-%d-%H-%M-%S)
GDB_TEMP_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.conf"
GDB_OUTPUT_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.txt"
# Create GDB configuration file if it is not defined
if [ -z "$GDB_FILE" ]; then
# Create GDB configuration
cat > "$GDB_TEMP_FILE" << EOF
set logging file $GDB_OUTPUT_FILE
set logging enabled on
set debug timestamp
EOF
# Add run command with config if specified
if [ -n "$CONFIG_ABS" ]; then
echo "run -c $CONFIG_ABS" >> "$GDB_TEMP_FILE"
else
echo "run" >> "$GDB_TEMP_FILE"
fi
cat >> "$GDB_TEMP_FILE" << EOF
bt
bt full
info thread
thread apply all backtrace full
EOF
GDB_FILE="$GDB_TEMP_FILE"
fi
# Create log files if specified
if [ -n "$SYSLOG" ]; then
[ ! -f "$SYSLOG" ] && touch "$SYSLOG"
fi
if [ -n "$SYSERR" ]; then
[ ! -f "$SYSERR" ] && touch "$SYSERR"
fi
# Execute with GDB
if [ "${WITH_CONSOLE:-0}" -eq 0 ] && [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
gdb -x "$GDB_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR"
else
echo "> Console enabled"
if [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
gdb -x "$GDB_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2)
else
gdb -x "$GDB_FILE" --batch "$EXECPATH"
fi
fi
# clean up temporary GDB file if it exists
if [ -n "$GDB_TEMP_FILE" ]; then
# Clean up temporary GDB file
rm -f "$GDB_TEMP_FILE"
fi
else
echo "Starting $BINFILE without GDB"
# Determine if PM2 is active
is_pm2_active="0"
[ "$AC_LAUNCHED_BY_PM2" == "1" ] && is_pm2_active="1"
# Determine if interactive mode is enabled
is_interactive_enabled="1"
[ "$AC_DISABLE_INTERACTIVE" == "1" ] && is_interactive_enabled="0"
# use normal execution if we are running the binary under PM2
# or when interactive mode is enabled
if [[ "$is_pm2_active" == "1" || "$is_interactive_enabled" == "1" ]]; then
echo "Running AC"
"$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
else
# When AC_DISABLE_INTERACTIVE is set to 1 and we are not in PM2
# This means we are using systemd without interactive mode and no session managers
# in this case we need to run AC with unbuffer for line-buffered output
# NOTE unbuffer doesn't fully support interactive mode
if command -v unbuffer >/dev/null 2>&1; then
echo "Running AC with unbuffer for line-buffered output"
unbuffer "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
else
echo "⚠️ unbuffer not found, the output may not be line-buffered. Try installing expect."
exec "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
fi
fi
fi

View File

@@ -0,0 +1,14 @@
# BATS Test Configuration
# Set test timeout (in seconds)
export BATS_TEST_TIMEOUT=30
# Enable verbose output for debugging
export BATS_VERBOSE_RUN=1
# Test output format
export BATS_FORMATTER=pretty
# Enable colored output
export BATS_NO_PARALLELIZE_ACROSS_FILES=1
export BATS_NO_PARALLELIZE_WITHIN_FILE=1

View File

@@ -0,0 +1,305 @@
#!/usr/bin/env bats
# AzerothCore Startup Scripts Test Suite
# This script tests the basic functionality of the startup scripts using the unified test framework
# Load the AzerothCore test framework
load '../../test-framework/bats_libs/acore-support'
load '../../test-framework/bats_libs/acore-assert'
# Setup that runs before each test
setup() {
startup_scripts_setup
export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../src" && pwd)"
}
# Cleanup that runs after each test
teardown() {
acore_test_teardown
}
# ===== STARTER SCRIPT TESTS =====
@test "starter: should fail with missing parameters" {
run timeout 3s "$SCRIPT_DIR/starter" '' ''
[ "$status" -ne 0 ]
[[ "$output" =~ "Error: Binary path and file are required" ]]
}
@test "starter: should start with valid binary" {
cd "$TEST_DIR"
run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "test-server" "" "$TEST_DIR/test-server.conf" "" "" 0
debug_on_failure
# The starter might have issues with the script command, so we check for specific behavior
# Either it should succeed or show a specific error we can work with
[[ "$output" =~ "Test server starting" ]] || [[ "$output" =~ "script:" ]] || [[ "$status" -eq 124 ]]
}
@test "starter: should validate binary path exists" {
run "$SCRIPT_DIR/starter" "/nonexistent/path" "test-server"
[ "$status" -ne 0 ]
[[ "$output" =~ "Binary '/nonexistent/path/test-server' not found" ]]
}
@test "starter: should detect PM2 environment properly" {
cd "$TEST_DIR"
# Test with AC_LAUNCHED_BY_PM2=1 (should not use script command)
AC_LAUNCHED_BY_PM2=1 run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "test-server" "" "$TEST_DIR/test-server.conf" "" "" 0
debug_on_failure
# Should start without using script command
[[ "$output" =~ "Test server starting" ]]
}
# ===== SIMPLE RESTARTER TESTS =====
@test "simple-restarter: should fail with missing parameters" {
run timeout 3s "$SCRIPT_DIR/simple-restarter" '' ''
[ "$status" -ne 0 ]
[[ "$output" =~ "Error: Binary path and file are required" ]]
}
@test "simple-restarter: should fail with missing binary" {
run timeout 3s "$SCRIPT_DIR/simple-restarter" "$TEST_DIR/bin" 'nonexistent'
[ "$status" -ne 0 ]
[[ "$output" =~ "not found" ]] || [[ "$output" =~ "terminated with exit code" ]]
}
@test "simple-restarter: should detect starter script" {
# Test that it finds the starter script
run timeout 1s "$SCRIPT_DIR/simple-restarter" '' ''
# Should not fail because starter script is missing
[[ ! "$output" =~ "starter script not found" ]]
}
# ===== RUN-ENGINE TESTS =====
@test "run-engine: should show help" {
run "$SCRIPT_DIR/run-engine" help
[ "$status" -eq 0 ]
[[ "$output" =~ "AzerothCore Run Engine" ]]
}
@test "run-engine: should validate parameters for start command" {
run "$SCRIPT_DIR/run-engine" start
[ "$status" -ne 0 ]
[[ "$output" =~ "Missing required arguments" ]]
}
@test "run-engine: should detect binary with full path" {
run timeout 5s "$SCRIPT_DIR/run-engine" start "$TEST_DIR/bin/test-server" --server-config "$TEST_DIR/test-server.conf"
debug_on_failure
[[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]]
}
@test "run-engine: should detect binary in current directory" {
cd "$TEST_DIR/bin"
run timeout 5s "$SCRIPT_DIR/run-engine" start test-server --server-config "$TEST_DIR/test-server.conf"
debug_on_failure
[[ "$output" =~ "Binary found in current directory" ]] || [[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]]
}
@test "run-engine: should support restart mode" {
run timeout 5s "$SCRIPT_DIR/run-engine" restart "$TEST_DIR/bin/test-server" --server-config "$TEST_DIR/test-server.conf"
debug_on_failure
[[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]]
}
# ===== SERVICE MANAGER TESTS =====
@test "service-manager: should show help" {
run "$SCRIPT_DIR/service-manager.sh" help
[ "$status" -eq 0 ]
[[ "$output" =~ "AzerothCore Service Setup" ]]
}
@test "service-manager: should validate create command parameters" {
run "$SCRIPT_DIR/service-manager.sh" create
[ "$status" -ne 0 ]
[[ "$output" =~ "Missing required arguments" ]] || [[ "$output" =~ "Error:" ]]
}
@test "service-manager: should validate restart policy values" {
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth --bin-path /nonexistent --restart-policy invalid
[ "$status" -ne 0 ]
[[ "$output" =~ "Invalid restart policy" ]]
}
@test "service-manager: should accept valid restart policy values" {
# Test on-failure (should be accepted)
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth --bin-path /nonexistent --restart-policy on-failure
# Should fail due to missing binary, not restart policy validation
[[ ! "$output" =~ "Invalid restart policy" ]]
# Test always (should be accepted)
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth2 --bin-path /nonexistent --restart-policy always
# Should fail due to missing binary, not restart policy validation
[[ ! "$output" =~ "Invalid restart policy" ]]
}
@test "service-manager: should include restart policy in help output" {
run "$SCRIPT_DIR/service-manager.sh" help
[ "$status" -eq 0 ]
[[ "$output" =~ "--restart-policy" ]]
[[ "$output" =~ "on-failure|always" ]]
}
@test "service-manager: help lists health and console commands" {
run "$SCRIPT_DIR/service-manager.sh" help
[ "$status" -eq 0 ]
[[ "$output" =~ "is-running <service-name>" ]]
[[ "$output" =~ "uptime-seconds <service-name>" ]]
[[ "$output" =~ "wait-uptime <service> <sec>" ]]
[[ "$output" =~ "send <service-name>" ]]
[[ "$output" =~ "show-config <service-name>" ]]
}
@test "service-manager: pm2 uptime and wait-uptime work with mocked pm2" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
# Create registry with pm2 provider service
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
{"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
]
EOF
# Create minimal service config and run-engine config files required by 'send'
echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf"
cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF'
export SESSION_MANAGER="none"
export SESSION_NAME="test-world"
EOF
# Mock pm2
cat > "$TEST_DIR/bin/pm2" << 'EOF'
#!/usr/bin/env bash
case "$1" in
jlist)
# Produce a JSON with uptime ~20 seconds
if date +%s%N >/dev/null 2>&1; then
nowms=$(( $(date +%s%N) / 1000000 ))
else
nowms=$(( $(date +%s) * 1000 ))
fi
up=$(( nowms - 20000 ))
echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]"
;;
id)
echo "[1]"
;;
attach|send|list|describe|logs)
exit 0
;;
*)
exit 0
;;
esac
EOF
chmod +x "$TEST_DIR/bin/pm2"
run "$SCRIPT_DIR/service-manager.sh" uptime-seconds test-world
debug_on_failure
[ "$status" -eq 0 ]
# Output should be a number >= 10
[[ "$output" =~ ^[0-9]+$ ]]
[ "$output" -ge 10 ]
run "$SCRIPT_DIR/service-manager.sh" wait-uptime test-world 10 5
debug_on_failure
[ "$status" -eq 0 ]
}
@test "service-manager: send works under pm2 with mocked pm2" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
# Create registry and config as in previous test
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
{"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
]
EOF
echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf"
cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF'
export SESSION_MANAGER="none"
export SESSION_NAME="test-world"
EOF
# pm2 mock
cat > "$TEST_DIR/bin/pm2" << 'EOF'
#!/usr/bin/env bash
case "$1" in
jlist)
if date +%s%N >/dev/null 2>&1; then
nowms=$(( $(date +%s%N) / 1000000 ))
else
nowms=$(( $(date +%s) * 1000 ))
fi
up=$(( nowms - 15000 ))
echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]"
;;
id)
echo "[1]"
;;
send)
# simulate success
exit 0
;;
attach|list|describe|logs)
exit 0
;;
*)
exit 0
;;
esac
EOF
chmod +x "$TEST_DIR/bin/pm2"
run "$SCRIPT_DIR/service-manager.sh" send test-world "server info"
debug_on_failure
[ "$status" -eq 0 ]
}
@test "service-manager: wait-uptime times out for unknown service" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
echo "[]" > "$AC_SERVICE_CONFIG_DIR/service_registry.json"
run "$SCRIPT_DIR/service-manager.sh" wait-uptime unknown 2 1
[ "$status" -ne 0 ]
}
# ===== EXAMPLE SCRIPTS TESTS =====
@test "examples: restarter-world should show configuration error" {
run "$SCRIPT_DIR/examples/restarter-world.sh"
[[ "$output" =~ "Configuration file not found" ]]
}
@test "examples: starter-auth should show configuration error" {
run "$SCRIPT_DIR/examples/starter-auth.sh"
[[ "$output" =~ "Configuration file not found" ]]
}
@test "examples: restarter-auth should show configuration error" {
run "$SCRIPT_DIR/examples/restarter-auth.sh"
[[ "$output" =~ "Configuration file not found" ]]
}
@test "examples: restarter-world should show alternative suggestions" {
run "$SCRIPT_DIR/examples/restarter-world.sh"
[[ "$output" =~ "Alternative: Start with binary path directly" ]]
}
# ===== INTEGRATION TESTS =====
@test "integration: starter and simple-restarter work together" {
# Test that simple-restarter can use starter
run timeout 5s "$SCRIPT_DIR/simple-restarter" "$TEST_DIR/bin" "test-server"
# Should start and then restart at least once
[[ "$output" =~ "terminated with exit code" ]] || [[ "$status" -eq 124 ]]
}
@test "integration: run-engine can handle missing config gracefully" {
run timeout 3s "$SCRIPT_DIR/run-engine" start "$TEST_DIR/bin/test-server"
# Should either work or give a meaningful error
[[ "$status" -eq 124 ]] || [[ "$status" -eq 0 ]] || [[ "$output" =~ "config" ]]
}