mirror of
https://github.com/uprightbass360/AzerothCore-RealmMaster.git
synced 2026-01-13 00:58:34 +00:00
feat: adds config/db import functionality
This commit is contained in:
@@ -158,6 +158,13 @@ MODULES_REQUIRES_PLAYERBOT_SOURCE=0
|
||||
# Client Data Settings
|
||||
# =====================
|
||||
CLIENT_DATA_VERSION=v18
|
||||
|
||||
# =====================
|
||||
# Server Configuration
|
||||
# =====================
|
||||
# Configuration preset to apply during deployment
|
||||
# Available: none, blizzlike, fast-leveling, hardcore-pvp, casual-pve
|
||||
SERVER_CONFIG_PRESET=none
|
||||
CLIENT_DATA_CACHE_PATH=${STORAGE_PATH_LOCAL}/client-data-cache
|
||||
CLIENT_DATA_PATH=${STORAGE_PATH}/client-data
|
||||
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,7 +2,7 @@ data/
|
||||
backups/
|
||||
manual-backups/
|
||||
V1/
|
||||
ImportBackup/
|
||||
database-import
|
||||
database-import/*.sql
|
||||
database-import/*.sql.gz
|
||||
source/*
|
||||
@@ -20,4 +20,4 @@ package-lock.json
|
||||
package.json
|
||||
.modules_state
|
||||
.modules-meta
|
||||
todo.md
|
||||
todo.md
|
||||
|
||||
279
config/CONFIG_MANAGEMENT.md
Normal file
279
config/CONFIG_MANAGEMENT.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# Configuration Management System
|
||||
|
||||
This system allows easy management of AzerothCore server settings using familiar INI/conf file syntax.
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
config/
|
||||
├── server-overrides.conf # Main configuration overrides
|
||||
└── presets/ # Pre-configured server types
|
||||
├── blizzlike.conf # Authentic WotLK experience
|
||||
├── fast-leveling.conf # 3x XP rates
|
||||
├── hardcore-pvp.conf # Competitive PvP server
|
||||
└── casual-pve.conf # Relaxed PvE experience
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Edit Configuration Settings
|
||||
|
||||
Edit `config/server-overrides.conf` with your desired settings:
|
||||
|
||||
```ini
|
||||
[worldserver.conf]
|
||||
Rate.XP.Kill = 2.0
|
||||
Rate.XP.Quest = 2.0
|
||||
GM.InGMList.Level = 3
|
||||
|
||||
[playerbots.conf]
|
||||
AiPlayerbot.MinRandomBots = 100
|
||||
AiPlayerbot.MaxRandomBots = 300
|
||||
```
|
||||
|
||||
### 2. Apply Configuration
|
||||
|
||||
```bash
|
||||
# Apply your custom overrides
|
||||
./scripts/apply-config.py
|
||||
|
||||
# Or apply a preset
|
||||
./scripts/apply-config.py --preset fast-leveling
|
||||
|
||||
# Preview changes without applying
|
||||
./scripts/apply-config.py --dry-run
|
||||
```
|
||||
|
||||
### 3. Restart Server
|
||||
|
||||
Restart your AzerothCore containers to apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose down && docker compose up -d
|
||||
```
|
||||
|
||||
## 🤖 Automated Integration
|
||||
|
||||
The configuration system is fully integrated into the setup and deployment workflow for seamless automation.
|
||||
|
||||
### **Setup Integration**
|
||||
During `./setup.sh`, you can choose a server configuration preset:
|
||||
```
|
||||
SERVER CONFIGURATION PRESET
|
||||
Choose a server configuration preset:
|
||||
1) Default (No Preset)
|
||||
Use default AzerothCore settings without any modifications
|
||||
2) Blizzlike Server
|
||||
Authentic WotLK experience with 1x rates and original mechanics
|
||||
3) Fast Leveling Server
|
||||
3x XP rates with quality of life improvements and cross-faction features
|
||||
4) Hardcore PvP Server
|
||||
Competitive PvP environment with 1.5x leveling and minimal bots
|
||||
5) Casual PvE Server
|
||||
Relaxed PvE experience with 2x rates and social features
|
||||
```
|
||||
|
||||
The chosen preset is stored in `.env`:
|
||||
```bash
|
||||
SERVER_CONFIG_PRESET=fast-leveling
|
||||
```
|
||||
|
||||
### **Deploy Integration**
|
||||
During `./deploy.sh`, the configuration is automatically applied:
|
||||
```
|
||||
Step 5/6: Applying server configuration
|
||||
✅ Applying server configuration preset: fast-leveling
|
||||
✅ Server configuration preset 'fast-leveling' applied successfully
|
||||
ℹ️ Restarting worldserver to apply configuration changes
|
||||
```
|
||||
|
||||
### **Automated Usage Examples**
|
||||
|
||||
**Interactive Setup:**
|
||||
```bash
|
||||
./setup.sh
|
||||
# Choose preset during interactive configuration
|
||||
./deploy.sh
|
||||
# Configuration automatically applied
|
||||
```
|
||||
|
||||
**Non-Interactive Setup:**
|
||||
```bash
|
||||
./setup.sh --server-config fast-leveling --non-interactive
|
||||
./deploy.sh
|
||||
# Configuration automatically applied
|
||||
```
|
||||
|
||||
**Skip Configuration:**
|
||||
```bash
|
||||
./deploy.sh --skip-config
|
||||
# Deploys without applying any configuration changes
|
||||
```
|
||||
|
||||
## 📋 Available Commands
|
||||
|
||||
### Apply Custom Overrides
|
||||
```bash
|
||||
./scripts/apply-config.py
|
||||
```
|
||||
|
||||
### Apply a Preset
|
||||
```bash
|
||||
# List available presets
|
||||
./scripts/apply-config.py --list-presets
|
||||
|
||||
# Apply specific preset
|
||||
./scripts/apply-config.py --preset blizzlike
|
||||
./scripts/apply-config.py --preset fast-leveling
|
||||
./scripts/apply-config.py --preset hardcore-pvp
|
||||
./scripts/apply-config.py --preset casual-pve
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
```bash
|
||||
# Apply only specific conf files
|
||||
./scripts/apply-config.py --files "worldserver.conf,playerbots.conf"
|
||||
|
||||
# Preview changes without applying
|
||||
./scripts/apply-config.py --dry-run
|
||||
|
||||
# Use different storage path
|
||||
./scripts/apply-config.py --storage-path /custom/storage
|
||||
|
||||
# Use different overrides file
|
||||
./scripts/apply-config.py --overrides-file /path/to/custom.conf
|
||||
```
|
||||
|
||||
## ⚙️ Configuration Format
|
||||
|
||||
### Section Headers
|
||||
Each section corresponds to a `.conf` file:
|
||||
```ini
|
||||
[worldserver.conf] # Settings for worldserver.conf
|
||||
[authserver.conf] # Settings for authserver.conf
|
||||
[playerbots.conf] # Settings for playerbots.conf
|
||||
[mod_transmog.conf] # Settings for mod_transmog.conf
|
||||
```
|
||||
|
||||
### Data Types
|
||||
```ini
|
||||
# Boolean values (0 = disabled, 1 = enabled)
|
||||
SomeFeature.Enable = 1
|
||||
|
||||
# Numeric values
|
||||
Rate.XP.Kill = 2.5
|
||||
MaxPlayerLevel = 80
|
||||
|
||||
# String values (can be quoted or unquoted)
|
||||
ServerMessage = "Welcome to our server!"
|
||||
DatabaseInfo = "127.0.0.1;3306;user;pass;db"
|
||||
```
|
||||
|
||||
### Comments
|
||||
Lines starting with `#` are comments and are ignored:
|
||||
```ini
|
||||
# This is a comment
|
||||
# Rate.XP.Kill = 1.0 # This setting is disabled
|
||||
Rate.XP.Quest = 2.0 # Active setting with comment
|
||||
```
|
||||
|
||||
## 🎯 Available Presets
|
||||
|
||||
### blizzlike.conf
|
||||
- **Description**: Authentic WotLK experience
|
||||
- **XP Rates**: 1x (Blizzlike)
|
||||
- **Features**: No cross-faction interaction, standard death penalties
|
||||
|
||||
### fast-leveling.conf
|
||||
- **Description**: 3x XP with quality of life improvements
|
||||
- **XP Rates**: 3x Kill/Quest, 2.5x Money
|
||||
- **Features**: Cross-faction interaction, faster corpse decay, autobalance
|
||||
|
||||
### hardcore-pvp.conf
|
||||
- **Description**: Competitive PvP environment
|
||||
- **XP Rates**: 1.5x (to reach endgame faster)
|
||||
- **Features**: No cross-faction interaction, minimal bots, expensive transmog
|
||||
|
||||
### casual-pve.conf
|
||||
- **Description**: Relaxed PvE with social features
|
||||
- **XP Rates**: 2x XP, 2.5x Rest bonus
|
||||
- **Features**: Full cross-faction interaction, high bot population, solo LFG
|
||||
|
||||
## 🔧 How It Works
|
||||
|
||||
1. **Preservation**: The system reads your existing `.conf` files and preserves all comments and structure
|
||||
2. **Override**: Only the settings you specify are updated
|
||||
3. **Fallback**: If a `.conf` file doesn't exist, it's created from the corresponding `.dist` file
|
||||
4. **Safety**: Use `--dry-run` to preview changes before applying
|
||||
|
||||
## 📝 Common Settings Reference
|
||||
|
||||
### XP and Progression
|
||||
```ini
|
||||
[worldserver.conf]
|
||||
Rate.XP.Kill = 2.0 # XP from killing monsters
|
||||
Rate.XP.Quest = 2.0 # XP from completing quests
|
||||
Rate.XP.Explore = 1.5 # XP from exploring new areas
|
||||
Rate.Rest.InGame = 2.0 # Rest bonus while logged in
|
||||
Rate.Rest.Offline.InTavernOrCity = 2.0 # Rest bonus while offline in safe zones
|
||||
```
|
||||
|
||||
### Drop Rates
|
||||
```ini
|
||||
[worldserver.conf]
|
||||
Rate.Drop.Money = 1.5 # Money drop rate
|
||||
Rate.Drop.Items = 1.2 # Item drop rate
|
||||
```
|
||||
|
||||
### Cross-Faction Settings
|
||||
```ini
|
||||
[worldserver.conf]
|
||||
AllowTwoSide.Interaction.Chat = 1 # Cross-faction chat
|
||||
AllowTwoSide.Interaction.Group = 1 # Cross-faction groups
|
||||
AllowTwoSide.Interaction.Guild = 1 # Cross-faction guilds
|
||||
AllowTwoSide.Interaction.Auction = 1 # Shared auction house
|
||||
AllowTwoSide.Interaction.Mail = 1 # Cross-faction mail
|
||||
```
|
||||
|
||||
### Playerbot Settings
|
||||
```ini
|
||||
[playerbots.conf]
|
||||
AiPlayerbot.RandomBotMinLevel = 15 # Minimum bot level
|
||||
AiPlayerbot.RandomBotMaxLevel = 80 # Maximum bot level
|
||||
AiPlayerbot.MinRandomBots = 50 # Minimum number of bots
|
||||
AiPlayerbot.MaxRandomBots = 200 # Maximum number of bots
|
||||
AiPlayerbot.RandomBotJoinLfg = 1 # Bots join LFG
|
||||
AiPlayerbot.RandomBotJoinBG = 1 # Bots join battlegrounds
|
||||
```
|
||||
|
||||
### Module Settings
|
||||
```ini
|
||||
[mod_transmog.conf]
|
||||
Transmogrification.Enable = 1 # Enable transmogrification
|
||||
Transmogrification.Cost = 100000 # Cost in copper
|
||||
|
||||
[mod_autobalance.conf]
|
||||
AutoBalance.enable = 1 # Enable dungeon scaling
|
||||
AutoBalance.MinPlayerReward = 1 # Scale rewards for solo play
|
||||
```
|
||||
|
||||
## 🆘 Troubleshooting
|
||||
|
||||
### Configuration Not Applied
|
||||
- Ensure you restart the server after applying changes
|
||||
- Check that the `.conf` files exist in your `storage/config/` directory
|
||||
- Use `--dry-run` to verify what changes would be made
|
||||
|
||||
### Permission Errors
|
||||
```bash
|
||||
# Make sure the script is executable
|
||||
chmod +x scripts/apply-config.py
|
||||
|
||||
# Check file permissions in storage/config/
|
||||
ls -la storage/config/
|
||||
```
|
||||
|
||||
### Finding Available Settings
|
||||
- Look in your `storage/config/` directory for `.conf` files
|
||||
- Each module's available settings are documented in their `.conf` files
|
||||
- Use `--dry-run` to see which files would be affected
|
||||
36
config/presets/blizzlike.conf
Normal file
36
config/presets/blizzlike.conf
Normal file
@@ -0,0 +1,36 @@
|
||||
# CONFIG_NAME: Blizzlike Server
|
||||
# CONFIG_DESCRIPTION: Authentic WotLK experience with 1x rates and original mechanics
|
||||
# Blizzlike Server Configuration - Authentic WotLK Experience
|
||||
|
||||
[worldserver.conf]
|
||||
# Blizzlike experience rates (1x)
|
||||
Rate.XP.Kill = 1.0
|
||||
Rate.XP.Quest = 1.0
|
||||
Rate.XP.Explore = 1.0
|
||||
Rate.Rest.InGame = 1.0
|
||||
Rate.Rest.Offline.InTavernOrCity = 1.0
|
||||
|
||||
# Blizzlike drop rates (1x)
|
||||
Rate.Drop.Money = 1.0
|
||||
Rate.Drop.Items = 1.0
|
||||
|
||||
# Standard player settings
|
||||
MaxPlayerLevel = 80
|
||||
GM.InGMList.Level = 3
|
||||
|
||||
# Faction restrictions (authentic)
|
||||
AllowTwoSide.Interaction.Chat = 0
|
||||
AllowTwoSide.Interaction.Channel = 0
|
||||
AllowTwoSide.Interaction.Group = 0
|
||||
AllowTwoSide.Interaction.Guild = 0
|
||||
AllowTwoSide.Interaction.Auction = 0
|
||||
AllowTwoSide.Interaction.Mail = 0
|
||||
|
||||
# Standard death penalties
|
||||
Death.CorpseDecayType = 0
|
||||
Death.Bones.BattlegroundOrArena = 1
|
||||
Corpse.Decay.NORMAL = 300
|
||||
Corpse.Decay.RARE = 900
|
||||
Corpse.Decay.ELITE = 1800
|
||||
Corpse.Decay.RAREELITE = 3600
|
||||
Corpse.Decay.WORLDBOSS = 7200
|
||||
63
config/presets/casual-pve.conf
Normal file
63
config/presets/casual-pve.conf
Normal file
@@ -0,0 +1,63 @@
|
||||
# CONFIG_NAME: Casual PvE Server
|
||||
# CONFIG_DESCRIPTION: Relaxed PvE experience with 2x rates and social features
|
||||
# Casual PvE Server - Relaxed PvE Experience with Quality of Life
|
||||
|
||||
[worldserver.conf]
|
||||
# Moderate XP rates for casual play
|
||||
Rate.XP.Kill = 2.0
|
||||
Rate.XP.Quest = 2.0
|
||||
Rate.XP.Explore = 2.0
|
||||
Rate.Rest.InGame = 2.5
|
||||
Rate.Rest.Offline.InTavernOrCity = 2.5
|
||||
|
||||
# Generous drops for casual enjoyment
|
||||
Rate.Drop.Money = 2.0
|
||||
Rate.Drop.Items = 1.3
|
||||
|
||||
# Standard level cap
|
||||
MaxPlayerLevel = 80
|
||||
GM.InGMList.Level = 3
|
||||
|
||||
# Full cross-faction interaction for social play
|
||||
AllowTwoSide.Interaction.Chat = 1
|
||||
AllowTwoSide.Interaction.Channel = 1
|
||||
AllowTwoSide.Interaction.Group = 1
|
||||
AllowTwoSide.Interaction.Guild = 1
|
||||
AllowTwoSide.Interaction.Auction = 1
|
||||
AllowTwoSide.Interaction.Mail = 1
|
||||
|
||||
# Forgiving death mechanics
|
||||
Death.CorpseDecayType = 2
|
||||
Death.Bones.BattlegroundOrArena = 1
|
||||
Corpse.Decay.NORMAL = 600
|
||||
Corpse.Decay.RARE = 1200
|
||||
Corpse.Decay.ELITE = 2400
|
||||
Corpse.Decay.RAREELITE = 4800
|
||||
Corpse.Decay.WORLDBOSS = 9600
|
||||
|
||||
[playerbots.conf]
|
||||
# High bot population for social atmosphere
|
||||
AiPlayerbot.RandomBotMinLevel = 5
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
AiPlayerbot.MinRandomBots = 200
|
||||
AiPlayerbot.MaxRandomBots = 500
|
||||
AiPlayerbot.RandomBotJoinLfg = 1
|
||||
AiPlayerbot.RandomBotJoinBG = 1
|
||||
AiPlayerbot.RandomBotRpg = 1
|
||||
|
||||
[mod_transmog.conf]
|
||||
# Affordable transmogrification
|
||||
Transmogrification.Enable = 1
|
||||
Transmogrification.Cost = 50000
|
||||
Transmogrification.AllowMixedArmorTypes = 1
|
||||
|
||||
[mod_autobalance.conf]
|
||||
# Enable autobalance for solo dungeon content
|
||||
AutoBalance.enable = 1
|
||||
AutoBalance.MinPlayerReward = 1
|
||||
AutoBalance.InflectionPoint = 3
|
||||
|
||||
[mod_solo_lfg.conf]
|
||||
# Enable solo LFG for convenience
|
||||
SoloLFG.Enable = 1
|
||||
SoloLFG.Announce = 1
|
||||
53
config/presets/fast-leveling.conf
Normal file
53
config/presets/fast-leveling.conf
Normal file
@@ -0,0 +1,53 @@
|
||||
# CONFIG_NAME: Fast Leveling Server
|
||||
# CONFIG_DESCRIPTION: 3x XP rates with quality of life improvements and cross-faction features
|
||||
# Fast Leveling Server - 3x XP with Quality of Life Features
|
||||
|
||||
[worldserver.conf]
|
||||
# Fast leveling rates (3x)
|
||||
Rate.XP.Kill = 3.0
|
||||
Rate.XP.Quest = 3.0
|
||||
Rate.XP.Explore = 3.0
|
||||
Rate.Rest.InGame = 3.0
|
||||
Rate.Rest.Offline.InTavernOrCity = 3.0
|
||||
|
||||
# Generous drop rates
|
||||
Rate.Drop.Money = 2.5
|
||||
Rate.Drop.Items = 1.5
|
||||
|
||||
# Standard level cap
|
||||
MaxPlayerLevel = 80
|
||||
GM.InGMList.Level = 3
|
||||
|
||||
# Quality of life - cross-faction interaction
|
||||
AllowTwoSide.Interaction.Chat = 1
|
||||
AllowTwoSide.Interaction.Channel = 1
|
||||
AllowTwoSide.Interaction.Group = 1
|
||||
AllowTwoSide.Interaction.Guild = 1
|
||||
AllowTwoSide.Interaction.Auction = 1
|
||||
AllowTwoSide.Interaction.Mail = 1
|
||||
|
||||
# Faster corpse recovery
|
||||
Death.CorpseDecayType = 2
|
||||
Death.Bones.BattlegroundOrArena = 1
|
||||
Corpse.Decay.NORMAL = 180
|
||||
Corpse.Decay.RARE = 360
|
||||
Corpse.Decay.ELITE = 720
|
||||
Corpse.Decay.RAREELITE = 1440
|
||||
Corpse.Decay.WORLDBOSS = 2880
|
||||
|
||||
[playerbots.conf]
|
||||
# Moderate bot population for fast leveling
|
||||
AiPlayerbot.RandomBotMinLevel = 10
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
AiPlayerbot.MinRandomBots = 100
|
||||
AiPlayerbot.MaxRandomBots = 300
|
||||
|
||||
[mod_transmog.conf]
|
||||
# Cheap transmogrification for appearance customization
|
||||
Transmogrification.Enable = 1
|
||||
Transmogrification.Cost = 10000
|
||||
|
||||
[mod_autobalance.conf]
|
||||
# Enable autobalance for solo dungeon experience
|
||||
AutoBalance.enable = 1
|
||||
AutoBalance.MinPlayerReward = 1
|
||||
49
config/presets/hardcore-pvp.conf
Normal file
49
config/presets/hardcore-pvp.conf
Normal file
@@ -0,0 +1,49 @@
|
||||
# CONFIG_NAME: Hardcore PvP Server
|
||||
# CONFIG_DESCRIPTION: Competitive PvP environment with 1.5x leveling and minimal bots
|
||||
# Hardcore PvP Server - Competitive WotLK PvP Experience
|
||||
|
||||
[worldserver.conf]
|
||||
# Slightly accelerated leveling to reach endgame faster
|
||||
Rate.XP.Kill = 1.5
|
||||
Rate.XP.Quest = 1.5
|
||||
Rate.XP.Explore = 1.0
|
||||
Rate.Rest.InGame = 1.0
|
||||
Rate.Rest.Offline.InTavernOrCity = 1.0
|
||||
|
||||
# Standard drop rates
|
||||
Rate.Drop.Money = 1.0
|
||||
Rate.Drop.Items = 1.0
|
||||
|
||||
# Level cap
|
||||
MaxPlayerLevel = 80
|
||||
GM.InGMList.Level = 3
|
||||
|
||||
# No cross-faction interaction (competitive environment)
|
||||
AllowTwoSide.Interaction.Chat = 0
|
||||
AllowTwoSide.Interaction.Channel = 0
|
||||
AllowTwoSide.Interaction.Group = 0
|
||||
AllowTwoSide.Interaction.Guild = 0
|
||||
AllowTwoSide.Interaction.Auction = 0
|
||||
AllowTwoSide.Interaction.Mail = 0
|
||||
|
||||
# Standard death penalties
|
||||
Death.CorpseDecayType = 0
|
||||
Death.Bones.BattlegroundOrArena = 1
|
||||
Corpse.Decay.NORMAL = 300
|
||||
Corpse.Decay.RARE = 900
|
||||
Corpse.Decay.ELITE = 1800
|
||||
Corpse.Decay.RAREELITE = 3600
|
||||
Corpse.Decay.WORLDBOSS = 7200
|
||||
|
||||
[playerbots.conf]
|
||||
# Minimal bot population to maintain authentic PvP experience
|
||||
AiPlayerbot.RandomBotMinLevel = 70
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
AiPlayerbot.MinRandomBots = 20
|
||||
AiPlayerbot.MaxRandomBots = 50
|
||||
AiPlayerbot.RandomBotJoinBG = 1
|
||||
|
||||
[mod_transmog.conf]
|
||||
# Expensive transmogrification (cosmetic luxury)
|
||||
Transmogrification.Enable = 1
|
||||
Transmogrification.Cost = 500000
|
||||
6
config/presets/none.conf
Normal file
6
config/presets/none.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
# CONFIG_NAME: Default (No Preset)
|
||||
# CONFIG_DESCRIPTION: Use default AzerothCore settings without any modifications
|
||||
# Default Configuration - No modifications applied
|
||||
|
||||
# This preset intentionally contains no configuration overrides.
|
||||
# The server will use default AzerothCore settings from .dist files.
|
||||
134
config/server-overrides.conf
Normal file
134
config/server-overrides.conf
Normal file
@@ -0,0 +1,134 @@
|
||||
# AzerothCore Server Configuration Overrides
|
||||
#
|
||||
# This file allows you to easily customize server settings without editing
|
||||
# individual .conf files. Settings defined here will override defaults.
|
||||
#
|
||||
# Format: [filename.conf] followed by key = value pairs
|
||||
# Boolean values: 0 = disabled, 1 = enabled
|
||||
# Strings: Can be quoted or unquoted
|
||||
# Numbers: Integer or decimal values
|
||||
#
|
||||
# After editing this file, run: ./scripts/apply-config.py
|
||||
|
||||
# =====================
|
||||
# Core Server Settings
|
||||
# =====================
|
||||
|
||||
[worldserver.conf]
|
||||
# Experience and progression rates
|
||||
Rate.XP.Kill = 2.0
|
||||
Rate.XP.Quest = 2.0
|
||||
Rate.XP.Explore = 1.5
|
||||
Rate.Rest.InGame = 2.0
|
||||
Rate.Rest.Offline.InTavernOrCity = 2.0
|
||||
|
||||
# Drop rates
|
||||
Rate.Drop.Money = 1.5
|
||||
Rate.Drop.Items = 1.2
|
||||
|
||||
# Player limits and GM settings
|
||||
MaxPlayerLevel = 80
|
||||
GM.InGMList.Level = 3
|
||||
GM.InWhoList.Level = 3
|
||||
|
||||
# Quality of life features
|
||||
AllowTwoSide.Interaction.Chat = 1
|
||||
AllowTwoSide.Interaction.Channel = 1
|
||||
AllowTwoSide.Interaction.Group = 1
|
||||
AllowTwoSide.Interaction.Guild = 1
|
||||
AllowTwoSide.Interaction.Auction = 1
|
||||
AllowTwoSide.Interaction.Mail = 1
|
||||
|
||||
# Death and corpse settings
|
||||
Death.CorpseDecayType = 2
|
||||
Death.Bones.BattlegroundOrArena = 1
|
||||
Corpse.Decay.NORMAL = 300
|
||||
Corpse.Decay.RARE = 900
|
||||
Corpse.Decay.ELITE = 1800
|
||||
Corpse.Decay.RAREELITE = 3600
|
||||
Corpse.Decay.WORLDBOSS = 7200
|
||||
|
||||
[authserver.conf]
|
||||
# Login settings
|
||||
LoginDatabase.WorkerThreads = 2
|
||||
LoginDatabase.SynchThreads = 1
|
||||
|
||||
# Updates
|
||||
Updates.AutoSetup = 1
|
||||
Updates.EnableDatabases = 7
|
||||
|
||||
# =====================
|
||||
# Playerbot Settings (if MODULE_PLAYERBOTS enabled)
|
||||
# =====================
|
||||
|
||||
[playerbots.conf]
|
||||
# Bot population control
|
||||
AiPlayerbot.RandomBotMinLevel = 15
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
AiPlayerbot.MinRandomBots = 50
|
||||
AiPlayerbot.MaxRandomBots = 200
|
||||
|
||||
# Bot behavior
|
||||
AiPlayerbot.RandomBotJoinLfg = 1
|
||||
AiPlayerbot.RandomBotJoinBG = 1
|
||||
AiPlayerbot.RandomBotAutoJoinAR = 1
|
||||
AiPlayerbot.RandomBotRpg = 1
|
||||
|
||||
# Bot restrictions
|
||||
AiPlayerbot.DisableRandomLevels = 0
|
||||
AiPlayerbot.RandomBotInGuildFraction = 0.1
|
||||
|
||||
# =====================
|
||||
# Module Configurations
|
||||
# =====================
|
||||
|
||||
[mod_transmog.conf]
|
||||
# Transmogrification settings
|
||||
Transmogrification.Enable = 1
|
||||
Transmogrification.Cost = 100000
|
||||
Transmogrification.MaxSets = 10
|
||||
Transmogrification.AllowMixedArmorTypes = 0
|
||||
Transmogrification.AllowMixedWeaponTypes = 0
|
||||
|
||||
[mod_aoe_loot.conf]
|
||||
# AOE Looting configuration
|
||||
AOELoot.Enable = 1
|
||||
|
||||
[mod_learnspells.conf]
|
||||
# Auto-learn spells configuration
|
||||
LearnSpells.Enable = 1
|
||||
LearnSpells.Gamemasters = 1
|
||||
|
||||
[mod_fireworks.conf]
|
||||
# Fireworks on level up
|
||||
Fireworks.Enable = 1
|
||||
|
||||
[mod_solo_lfg.conf]
|
||||
# Solo LFG settings
|
||||
SoloLFG.Enable = 1
|
||||
SoloLFG.Announce = 1
|
||||
|
||||
[mod_autobalance.conf]
|
||||
# Autobalance for solo play (if enabled)
|
||||
AutoBalance.enable = 1
|
||||
AutoBalance.MinPlayerReward = 1
|
||||
AutoBalance.InflectionPoint = 5
|
||||
AutoBalance.InflectionPointRaid10M = 7.5
|
||||
AutoBalance.InflectionPointRaid25M = 17
|
||||
AutoBalance.InflectionPointRaidHeroic = 5
|
||||
AutoBalance.InflectionPointHeroic = 5
|
||||
|
||||
[mod_individual_progression.conf]
|
||||
# Individual progression settings (if enabled)
|
||||
IndividualProgression.Enable = 1
|
||||
IndividualProgression.VanillaExp = 80000000
|
||||
IndividualProgression.TbcExp = 150000000
|
||||
IndividualProgression.WotlkExp = 220000000
|
||||
|
||||
# =====================
|
||||
# Additional Module Settings
|
||||
# =====================
|
||||
# Add more module configurations as needed.
|
||||
# Module conf files are typically named: mod_<modulename>.conf
|
||||
# Check your storage/config/ directory for available .conf files
|
||||
# to see what settings are available for each module.
|
||||
67
deploy.sh
67
deploy.sh
@@ -17,6 +17,7 @@ WATCH_LOGS=1
|
||||
KEEP_RUNNING=0
|
||||
WORLD_LOG_SINCE=""
|
||||
ASSUME_YES=0
|
||||
SKIP_CONFIG=0
|
||||
|
||||
REMOTE_MODE=0
|
||||
REMOTE_HOST=""
|
||||
@@ -209,6 +210,7 @@ Options:
|
||||
--remote-identity PATH SSH private key for remote migration
|
||||
--remote-project-dir DIR Remote project directory (default: ~/AzerothCore-RealmMaster)
|
||||
--remote-skip-storage Skip syncing the storage directory during migration
|
||||
--skip-config Skip applying server configuration preset
|
||||
-h, --help Show this help
|
||||
|
||||
This command automates deployment: sync modules, stage the correct compose profile,
|
||||
@@ -234,6 +236,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--remote-identity) REMOTE_IDENTITY="$2"; REMOTE_MODE=1; REMOTE_ARGS_PROVIDED=1; shift 2;;
|
||||
--remote-project-dir) REMOTE_PROJECT_DIR="$2"; REMOTE_MODE=1; REMOTE_ARGS_PROVIDED=1; shift 2;;
|
||||
--remote-skip-storage) REMOTE_SKIP_STORAGE=1; REMOTE_MODE=1; REMOTE_ARGS_PROVIDED=1; shift;;
|
||||
--skip-config) SKIP_CONFIG=1; shift;;
|
||||
-h|--help) usage; exit 0;;
|
||||
*) err "Unknown option: $1"; usage; exit 1;;
|
||||
esac
|
||||
@@ -667,6 +670,59 @@ wait_for_worldserver_ready(){
|
||||
done
|
||||
}
|
||||
|
||||
apply_server_config(){
|
||||
if [ "$SKIP_CONFIG" -eq 1 ]; then
|
||||
info "Skipping server configuration application (--skip-config flag set)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Read the SERVER_CONFIG_PRESET from .env
|
||||
local server_config_preset
|
||||
server_config_preset="$(read_env SERVER_CONFIG_PRESET "none")"
|
||||
|
||||
if [ "$server_config_preset" = "none" ] || [ -z "$server_config_preset" ]; then
|
||||
info "No server configuration preset selected - using defaults"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "Applying server configuration preset: $server_config_preset"
|
||||
|
||||
local config_script="$ROOT_DIR/scripts/apply-config.py"
|
||||
if [ ! -x "$config_script" ]; then
|
||||
warn "Configuration script not found or not executable: $config_script"
|
||||
warn "Server will use default settings"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local storage_path
|
||||
storage_path="$(read_env STORAGE_PATH "./storage")"
|
||||
|
||||
# Check if preset file exists
|
||||
local preset_file="$ROOT_DIR/config/presets/${server_config_preset}.conf"
|
||||
if [ ! -f "$preset_file" ]; then
|
||||
warn "Server configuration preset not found: $preset_file"
|
||||
warn "Server will use default settings"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Apply the configuration
|
||||
if python3 "$config_script" --storage-path "$storage_path" --preset "$server_config_preset"; then
|
||||
ok "Server configuration preset '$server_config_preset' applied successfully"
|
||||
info "Restart worldserver to apply configuration changes"
|
||||
|
||||
# Restart worldserver if it's running to apply config changes
|
||||
if docker ps --format '{{.Names}}' | grep -q '^ac-worldserver$'; then
|
||||
info "Restarting worldserver to apply configuration changes..."
|
||||
docker restart ac-worldserver
|
||||
info "Waiting for worldserver to become healthy after configuration..."
|
||||
sleep 5 # Brief pause before health check
|
||||
fi
|
||||
else
|
||||
warn "Failed to apply server configuration preset '$server_config_preset'"
|
||||
warn "Server will continue with existing settings"
|
||||
fi
|
||||
}
|
||||
|
||||
main(){
|
||||
if [ "$ASSUME_YES" -ne 1 ]; then
|
||||
if [ -t 0 ]; then
|
||||
@@ -723,11 +779,18 @@ main(){
|
||||
stop_runtime_stack
|
||||
fi
|
||||
|
||||
show_step 3 4 "Bringing your realm online"
|
||||
show_step 3 5 "Importing user database files"
|
||||
info "Checking for database files in ./database-import/"
|
||||
bash "$ROOT_DIR/scripts/import-database-files.sh"
|
||||
|
||||
show_step 4 6 "Bringing your realm online"
|
||||
info "Pulling images and waiting for containers to become healthy; this may take a few minutes on first deploy."
|
||||
stage_runtime
|
||||
|
||||
show_step 4 4 "Finalizing deployment"
|
||||
show_step 5 6 "Applying server configuration"
|
||||
apply_server_config
|
||||
|
||||
show_step 6 6 "Finalizing deployment"
|
||||
mark_deployment_complete
|
||||
|
||||
show_realm_ready
|
||||
|
||||
323
scripts/apply-config.py
Executable file
323
scripts/apply-config.py
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AzerothCore Configuration Manager
|
||||
|
||||
Reads server-overrides.conf and preset files to update actual .conf files
|
||||
while preserving comments and structure.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Manages AzerothCore configuration file updates."""
|
||||
|
||||
def __init__(self, storage_path: str, overrides_file: str, dry_run: bool = False):
|
||||
self.storage_path = Path(storage_path)
|
||||
self.config_dir = self.storage_path / "config"
|
||||
self.modules_config_dir = self.storage_path / "config" / "modules"
|
||||
self.overrides_file = Path(overrides_file)
|
||||
self.dry_run = dry_run
|
||||
|
||||
if not self.config_dir.exists():
|
||||
raise FileNotFoundError(f"Config directory not found: {self.config_dir}")
|
||||
|
||||
def load_overrides(self) -> Dict[str, Dict[str, str]]:
|
||||
"""Load configuration overrides from INI-style file."""
|
||||
if not self.overrides_file.exists():
|
||||
print(f"⚠️ Override file not found: {self.overrides_file}")
|
||||
return {}
|
||||
|
||||
config = configparser.ConfigParser(interpolation=None)
|
||||
config.optionxform = str # Preserve case sensitivity
|
||||
|
||||
try:
|
||||
config.read(self.overrides_file, encoding='utf-8')
|
||||
except Exception as e:
|
||||
print(f"❌ Error reading override file: {e}")
|
||||
return {}
|
||||
|
||||
overrides = {}
|
||||
for section in config.sections():
|
||||
overrides[section] = dict(config.items(section))
|
||||
|
||||
return overrides
|
||||
|
||||
def find_conf_file(self, filename: str) -> Optional[Path]:
|
||||
"""Find a configuration file in the config directory."""
|
||||
# Check main config directory first (for core server configs)
|
||||
conf_file = self.config_dir / filename
|
||||
|
||||
if conf_file.exists():
|
||||
return conf_file
|
||||
|
||||
# Check modules config directory (for module configs)
|
||||
modules_conf_file = self.modules_config_dir / filename
|
||||
if modules_conf_file.exists():
|
||||
return modules_conf_file
|
||||
|
||||
# Try to create from .dist file in main config directory
|
||||
dist_file = self.config_dir / f"{filename}.dist"
|
||||
if dist_file.exists():
|
||||
print(f"📄 Creating {filename} from {filename}.dist")
|
||||
if not self.dry_run:
|
||||
shutil.copy2(dist_file, conf_file)
|
||||
return conf_file
|
||||
|
||||
# Try to create from .dist file in modules directory
|
||||
modules_dist_file = self.modules_config_dir / f"{filename}.dist"
|
||||
if modules_dist_file.exists():
|
||||
print(f"📄 Creating {filename} from modules/{filename}.dist")
|
||||
if not self.dry_run:
|
||||
if not self.modules_config_dir.exists():
|
||||
self.modules_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(modules_dist_file, modules_conf_file)
|
||||
return modules_conf_file
|
||||
|
||||
return None
|
||||
|
||||
def update_conf_file(self, conf_file: Path, settings: Dict[str, str]) -> bool:
|
||||
"""Update a .conf file with new settings while preserving structure."""
|
||||
if not conf_file.exists():
|
||||
print(f"❌ Configuration file not found: {conf_file}")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(conf_file, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
except Exception as e:
|
||||
print(f"❌ Error reading {conf_file}: {e}")
|
||||
return False
|
||||
|
||||
updated_lines = []
|
||||
updated_keys = set()
|
||||
|
||||
# Process each line
|
||||
for line in lines:
|
||||
original_line = line
|
||||
stripped = line.strip()
|
||||
|
||||
# Skip empty lines and comments
|
||||
if not stripped or stripped.startswith('#'):
|
||||
updated_lines.append(original_line)
|
||||
continue
|
||||
|
||||
# Check if this line contains a setting we want to override
|
||||
setting_match = re.match(r'^([^=]+?)\s*=\s*(.*)$', stripped)
|
||||
if setting_match:
|
||||
key = setting_match.group(1).strip()
|
||||
|
||||
if key in settings:
|
||||
# Replace with our override value
|
||||
new_value = settings[key]
|
||||
# Preserve the original indentation
|
||||
indent = len(line) - len(line.lstrip())
|
||||
new_line = ' ' * indent + f"{key} = {new_value}\n"
|
||||
updated_lines.append(new_line)
|
||||
updated_keys.add(key)
|
||||
print(f" ✅ {key} = {new_value}")
|
||||
else:
|
||||
# Keep original line
|
||||
updated_lines.append(original_line)
|
||||
else:
|
||||
# Keep original line (could be section header or other content)
|
||||
updated_lines.append(original_line)
|
||||
|
||||
# Add any settings that weren't found in the file
|
||||
for key, value in settings.items():
|
||||
if key not in updated_keys:
|
||||
updated_lines.append(f"{key} = {value}\n")
|
||||
print(f" ➕ {key} = {value} (added)")
|
||||
|
||||
# Write the updated file
|
||||
if not self.dry_run:
|
||||
try:
|
||||
with open(conf_file, 'w', encoding='utf-8') as f:
|
||||
f.writelines(updated_lines)
|
||||
except Exception as e:
|
||||
print(f"❌ Error writing {conf_file}: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def apply_overrides(self, overrides: Dict[str, Dict[str, str]],
|
||||
filter_files: Optional[Set[str]] = None) -> bool:
|
||||
"""Apply all configuration overrides."""
|
||||
success = True
|
||||
|
||||
if not overrides:
|
||||
print("ℹ️ No configuration overrides to apply")
|
||||
return True
|
||||
|
||||
print(f"🔧 Applying configuration overrides{' (DRY RUN)' if self.dry_run else ''}...")
|
||||
|
||||
for conf_filename, settings in overrides.items():
|
||||
# Skip if we're filtering and this file isn't in the filter
|
||||
if filter_files and conf_filename not in filter_files:
|
||||
continue
|
||||
|
||||
if not settings:
|
||||
continue
|
||||
|
||||
print(f"\n📝 Updating {conf_filename}:")
|
||||
|
||||
# Find the configuration file
|
||||
conf_file = self.find_conf_file(conf_filename)
|
||||
if not conf_file:
|
||||
print(f" ⚠️ Configuration file not found: {conf_filename}")
|
||||
success = False
|
||||
continue
|
||||
|
||||
# Update the file
|
||||
if not self.update_conf_file(conf_file, settings):
|
||||
success = False
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def load_preset(preset_file: Path) -> Dict[str, Dict[str, str]]:
|
||||
"""Load a preset configuration file."""
|
||||
if not preset_file.exists():
|
||||
raise FileNotFoundError(f"Preset file not found: {preset_file}")
|
||||
|
||||
config = configparser.ConfigParser(interpolation=None)
|
||||
config.optionxform = str # Preserve case sensitivity
|
||||
config.read(preset_file, encoding='utf-8')
|
||||
|
||||
overrides = {}
|
||||
for section in config.sections():
|
||||
overrides[section] = dict(config.items(section))
|
||||
|
||||
return overrides
|
||||
|
||||
|
||||
def list_available_presets(preset_dir: Path) -> List[str]:
|
||||
"""List available preset files."""
|
||||
if not preset_dir.exists():
|
||||
return []
|
||||
|
||||
presets = []
|
||||
for preset_file in preset_dir.glob("*.conf"):
|
||||
presets.append(preset_file.stem)
|
||||
|
||||
return sorted(presets)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Apply AzerothCore configuration overrides and presets"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--storage-path",
|
||||
default="./storage",
|
||||
help="Path to storage directory (default: ./storage)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--overrides-file",
|
||||
default="./config/server-overrides.conf",
|
||||
help="Path to server overrides file (default: ./config/server-overrides.conf)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--preset",
|
||||
help="Apply a preset from config/presets/<name>.conf"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-presets",
|
||||
action="store_true",
|
||||
help="List available presets"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--files",
|
||||
help="Comma-separated list of .conf files to update (default: all)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be changed without making modifications"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Handle list presets
|
||||
if args.list_presets:
|
||||
preset_dir = Path("./config/presets")
|
||||
presets = list_available_presets(preset_dir)
|
||||
|
||||
if presets:
|
||||
print("📋 Available presets:")
|
||||
for preset in presets:
|
||||
preset_file = preset_dir / f"{preset}.conf"
|
||||
print(f" • {preset}")
|
||||
# Try to read description from preset file
|
||||
if preset_file.exists():
|
||||
try:
|
||||
with open(preset_file, 'r') as f:
|
||||
first_line = f.readline().strip()
|
||||
if first_line.startswith('#') and len(first_line) > 1:
|
||||
description = first_line[1:].strip()
|
||||
print(f" {description}")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print("ℹ️ No presets found in config/presets/")
|
||||
return
|
||||
|
||||
try:
|
||||
# Initialize configuration manager
|
||||
config_manager = ConfigManager(
|
||||
storage_path=args.storage_path,
|
||||
overrides_file=args.overrides_file,
|
||||
dry_run=args.dry_run
|
||||
)
|
||||
|
||||
# Determine which files to filter (if any)
|
||||
filter_files = None
|
||||
if args.files:
|
||||
filter_files = set(f.strip() for f in args.files.split(','))
|
||||
|
||||
# Load configuration overrides
|
||||
overrides = {}
|
||||
|
||||
# Load preset if specified
|
||||
if args.preset:
|
||||
preset_file = Path(f"./config/presets/{args.preset}.conf")
|
||||
print(f"📦 Loading preset: {args.preset}")
|
||||
try:
|
||||
preset_overrides = load_preset(preset_file)
|
||||
overrides.update(preset_overrides)
|
||||
except FileNotFoundError as e:
|
||||
print(f"❌ {e}")
|
||||
return 1
|
||||
|
||||
# Load server overrides (this can override preset values)
|
||||
server_overrides = config_manager.load_overrides()
|
||||
overrides.update(server_overrides)
|
||||
|
||||
# Apply all overrides
|
||||
success = config_manager.apply_overrides(overrides, filter_files)
|
||||
|
||||
if success:
|
||||
if args.dry_run:
|
||||
print("\n✅ Configuration validation complete")
|
||||
else:
|
||||
print("\n✅ Configuration applied successfully")
|
||||
print("ℹ️ Restart your server to apply changes")
|
||||
return 0
|
||||
else:
|
||||
print("\n❌ Some configuration updates failed")
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
162
scripts/configure-server.sh
Executable file
162
scripts/configure-server.sh
Executable file
@@ -0,0 +1,162 @@
|
||||
#!/bin/bash
|
||||
# Simple wrapper script for server configuration management
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_header() {
|
||||
echo -e "\n${BLUE}🔧 AzerothCore Configuration Manager${NC}\n"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat << EOF
|
||||
Usage: $(basename "$0") [COMMAND] [OPTIONS]
|
||||
|
||||
Commands:
|
||||
apply Apply configuration overrides from config/server-overrides.conf
|
||||
preset <name> Apply a preset configuration
|
||||
list List available presets
|
||||
edit Open server-overrides.conf in editor
|
||||
status Show current configuration status
|
||||
|
||||
Examples:
|
||||
$(basename "$0") apply # Apply custom overrides
|
||||
$(basename "$0") preset fast-leveling # Apply fast-leveling preset
|
||||
$(basename "$0") list # Show available presets
|
||||
$(basename "$0") edit # Edit configuration file
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
edit_config() {
|
||||
local config_file="$PROJECT_DIR/config/server-overrides.conf"
|
||||
local editor="${EDITOR:-nano}"
|
||||
|
||||
echo -e "${YELLOW}📝 Opening configuration file in $editor...${NC}"
|
||||
|
||||
if [[ ! -f "$config_file" ]]; then
|
||||
echo -e "${YELLOW}⚠️ Configuration file doesn't exist. Creating template...${NC}"
|
||||
mkdir -p "$(dirname "$config_file")"
|
||||
# Create a minimal template if it doesn't exist
|
||||
cat > "$config_file" << 'EOF'
|
||||
# AzerothCore Server Configuration Overrides
|
||||
# Edit this file and run './scripts/configure-server.sh apply' to update settings
|
||||
|
||||
[worldserver.conf]
|
||||
# Example settings - uncomment and modify as needed
|
||||
# Rate.XP.Kill = 2.0
|
||||
# Rate.XP.Quest = 2.0
|
||||
# MaxPlayerLevel = 80
|
||||
|
||||
[playerbots.conf]
|
||||
# Example playerbot settings
|
||||
# AiPlayerbot.MinRandomBots = 100
|
||||
# AiPlayerbot.MaxRandomBots = 300
|
||||
EOF
|
||||
echo -e "${GREEN}✅ Created template configuration file${NC}"
|
||||
fi
|
||||
|
||||
"$editor" "$config_file"
|
||||
|
||||
echo -e "\n${YELLOW}Would you like to apply these changes now? (y/N)${NC}"
|
||||
read -r response
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
python3 "$SCRIPT_DIR/apply-config.py"
|
||||
else
|
||||
echo -e "${BLUE}ℹ️ Run '$(basename "$0") apply' when ready to apply changes${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo -e "${BLUE}📊 Configuration Status${NC}\n"
|
||||
|
||||
# Check if config files exist
|
||||
local storage_path="${STORAGE_PATH:-./storage}"
|
||||
local config_dir="$storage_path/config"
|
||||
|
||||
if [[ -d "$config_dir" ]]; then
|
||||
echo -e "${GREEN}✅ Config directory found: $config_dir${NC}"
|
||||
|
||||
local conf_count
|
||||
conf_count=$(find "$config_dir" -name "*.conf" -type f | wc -l)
|
||||
echo -e "${GREEN}📄 Configuration files: $conf_count${NC}"
|
||||
|
||||
# Show some key files
|
||||
for conf in worldserver.conf authserver.conf playerbots.conf; do
|
||||
if [[ -f "$config_dir/$conf" ]]; then
|
||||
echo -e "${GREEN} ✅ $conf${NC}"
|
||||
else
|
||||
echo -e "${YELLOW} ⚠️ $conf (missing)${NC}"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${RED}❌ Config directory not found: $config_dir${NC}"
|
||||
echo -e "${YELLOW}ℹ️ Run './deploy.sh' first to initialize storage${NC}"
|
||||
fi
|
||||
|
||||
# Check override file
|
||||
local override_file="$PROJECT_DIR/config/server-overrides.conf"
|
||||
if [[ -f "$override_file" ]]; then
|
||||
echo -e "${GREEN}✅ Override file: $override_file${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Override file not found${NC}"
|
||||
echo -e "${BLUE}ℹ️ Run '$(basename "$0") edit' to create one${NC}"
|
||||
fi
|
||||
|
||||
# Show available presets
|
||||
echo -e "\n${BLUE}📋 Available Presets:${NC}"
|
||||
python3 "$SCRIPT_DIR/apply-config.py" --list-presets
|
||||
}
|
||||
|
||||
main() {
|
||||
print_header
|
||||
|
||||
case "${1:-}" in
|
||||
"apply")
|
||||
echo -e "${YELLOW}🔄 Applying configuration overrides...${NC}"
|
||||
python3 "$SCRIPT_DIR/apply-config.py" "${@:2}"
|
||||
echo -e "\n${GREEN}✅ Configuration applied!${NC}"
|
||||
echo -e "${YELLOW}ℹ️ Restart your server to apply changes:${NC} docker compose restart"
|
||||
;;
|
||||
"preset")
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo -e "${RED}❌ Please specify a preset name${NC}"
|
||||
echo -e "Available presets:"
|
||||
python3 "$SCRIPT_DIR/apply-config.py" --list-presets
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${YELLOW}🎯 Applying preset: $2${NC}"
|
||||
python3 "$SCRIPT_DIR/apply-config.py" --preset "$2" "${@:3}"
|
||||
echo -e "\n${GREEN}✅ Preset '$2' applied!${NC}"
|
||||
echo -e "${YELLOW}ℹ️ Restart your server to apply changes:${NC} docker compose restart"
|
||||
;;
|
||||
"list")
|
||||
python3 "$SCRIPT_DIR/apply-config.py" --list-presets
|
||||
;;
|
||||
"edit")
|
||||
edit_config
|
||||
;;
|
||||
"status")
|
||||
show_status
|
||||
;;
|
||||
"help"|"--help"|"-h"|"")
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}❌ Unknown command: $1${NC}"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
68
scripts/import-database-files.sh
Executable file
68
scripts/import-database-files.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# Copy user database files from database-import/ to backup system
|
||||
set -e
|
||||
|
||||
# Source environment variables
|
||||
if [ -f ".env" ]; then
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
IMPORT_DIR="./database-import"
|
||||
STORAGE_PATH="${STORAGE_PATH:-./storage}"
|
||||
STORAGE_PATH_LOCAL="${STORAGE_PATH_LOCAL:-./local-storage}"
|
||||
BACKUP_DIR="${STORAGE_PATH}/backups/daily"
|
||||
TIMESTAMP=$(date +%Y-%m-%d)
|
||||
|
||||
# Exit if no import directory or empty
|
||||
if [ ! -d "$IMPORT_DIR" ] || [ -z "$(ls -A "$IMPORT_DIR" 2>/dev/null | grep -E '\.(sql|sql\.gz)$')" ]; then
|
||||
echo "📁 No database files found in $IMPORT_DIR - skipping import"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Exit if backup system already has databases restored
|
||||
if [ -f "${STORAGE_PATH_LOCAL}/mysql-data/.restore-completed" ]; then
|
||||
echo "✅ Database already restored - skipping import"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "📥 Found database files in $IMPORT_DIR"
|
||||
echo "📂 Copying to backup system for import..."
|
||||
|
||||
# Ensure backup directory exists
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Copy files with smart naming
|
||||
for file in "$IMPORT_DIR"/*.sql "$IMPORT_DIR"/*.sql.gz; do
|
||||
[ -f "$file" ] || continue
|
||||
|
||||
filename=$(basename "$file")
|
||||
|
||||
# Try to detect database type by filename
|
||||
if echo "$filename" | grep -qi "auth"; then
|
||||
target_name="acore_auth_${TIMESTAMP}.sql"
|
||||
elif echo "$filename" | grep -qi "world"; then
|
||||
target_name="acore_world_${TIMESTAMP}.sql"
|
||||
elif echo "$filename" | grep -qi "char"; then
|
||||
target_name="acore_characters_${TIMESTAMP}.sql"
|
||||
else
|
||||
# Fallback - use original name with timestamp
|
||||
base_name="${filename%.*}"
|
||||
ext="${filename##*.}"
|
||||
target_name="${base_name}_${TIMESTAMP}.${ext}"
|
||||
fi
|
||||
|
||||
# Add .gz extension if source is compressed
|
||||
if [[ "$filename" == *.sql.gz ]]; then
|
||||
target_name="${target_name}.gz"
|
||||
fi
|
||||
|
||||
target_path="$BACKUP_DIR/$target_name"
|
||||
|
||||
echo "📋 Copying $filename → $target_name"
|
||||
cp "$file" "$target_path"
|
||||
done
|
||||
|
||||
echo "✅ Database files copied to backup system"
|
||||
echo "💡 Files will be automatically imported during deployment"
|
||||
92
scripts/parse-config-presets.py
Executable file
92
scripts/parse-config-presets.py
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Parse configuration preset metadata for setup.sh
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def parse_preset_metadata(preset_file: Path):
|
||||
"""Parse CONFIG_NAME and CONFIG_DESCRIPTION from a preset file."""
|
||||
if not preset_file.exists():
|
||||
return None, None
|
||||
|
||||
config_name = None
|
||||
config_description = None
|
||||
|
||||
try:
|
||||
with open(preset_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('# CONFIG_NAME:'):
|
||||
config_name = line[14:].strip()
|
||||
elif line.startswith('# CONFIG_DESCRIPTION:'):
|
||||
config_description = line[21:].strip()
|
||||
elif not line.startswith('#'):
|
||||
# Stop at first non-comment line
|
||||
break
|
||||
except Exception:
|
||||
return None, None
|
||||
|
||||
return config_name, config_description
|
||||
|
||||
|
||||
def list_presets(presets_dir: Path):
|
||||
"""List all available presets with their metadata."""
|
||||
if not presets_dir.exists():
|
||||
return
|
||||
|
||||
presets = []
|
||||
for preset_file in presets_dir.glob("*.conf"):
|
||||
preset_key = preset_file.stem
|
||||
config_name, config_description = parse_preset_metadata(preset_file)
|
||||
|
||||
if config_name is None:
|
||||
config_name = preset_key.replace('-', ' ').title()
|
||||
if config_description is None:
|
||||
config_description = f"Configuration preset: {preset_key}"
|
||||
|
||||
presets.append((preset_key, config_name, config_description))
|
||||
|
||||
# Sort presets, but ensure 'none' comes first
|
||||
presets.sort(key=lambda x: (0 if x[0] == 'none' else 1, x[0]))
|
||||
|
||||
for preset_key, config_name, config_description in presets:
|
||||
print(f"{preset_key}\t{config_name}\t{config_description}")
|
||||
|
||||
|
||||
def get_preset_info(presets_dir: Path, preset_key: str):
|
||||
"""Get information for a specific preset."""
|
||||
preset_file = presets_dir / f"{preset_key}.conf"
|
||||
config_name, config_description = parse_preset_metadata(preset_file)
|
||||
|
||||
if config_name is None:
|
||||
config_name = preset_key.replace('-', ' ').title()
|
||||
if config_description is None:
|
||||
config_description = f"Configuration preset: {preset_key}"
|
||||
|
||||
print(f"{config_name}\t{config_description}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Parse configuration preset metadata")
|
||||
parser.add_argument("command", choices=["list", "info"], help="Command to execute")
|
||||
parser.add_argument("--presets-dir", default="./config/presets", help="Presets directory")
|
||||
parser.add_argument("--preset", help="Preset name for 'info' command")
|
||||
|
||||
args = parser.parse_args()
|
||||
presets_dir = Path(args.presets_dir)
|
||||
|
||||
if args.command == "list":
|
||||
list_presets(presets_dir)
|
||||
elif args.command == "info":
|
||||
if not args.preset:
|
||||
print("Error: --preset required for 'info' command", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
get_preset_info(presets_dir, args.preset)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
66
setup.sh
66
setup.sh
@@ -578,6 +578,7 @@ Options:
|
||||
--backup-daily-time HH Daily backup hour 00-23 (default 09)
|
||||
--module-mode MODE suggested, playerbots, manual, or none
|
||||
--module-config NAME Use preset NAME from config/module-profiles/<NAME>.json
|
||||
--server-config NAME Use server preset NAME from config/presets/<NAME>.conf
|
||||
--enable-modules LIST Comma-separated module list (MODULE_* or shorthand)
|
||||
--playerbot-enabled 0|1 Override PLAYERBOT_ENABLED flag
|
||||
--playerbot-min-bots N Override PLAYERBOT_MIN_BOTS value
|
||||
@@ -705,6 +706,13 @@ EOF
|
||||
--module-config=*)
|
||||
CLI_MODULE_PRESET="${1#*=}"; shift
|
||||
;;
|
||||
--server-config)
|
||||
[[ $# -ge 2 ]] || { say ERROR "--server-config requires a value"; exit 1; }
|
||||
CLI_CONFIG_PRESET="$2"; shift 2
|
||||
;;
|
||||
--server-config=*)
|
||||
CLI_CONFIG_PRESET="${1#*=}"; shift
|
||||
;;
|
||||
--enable-modules)
|
||||
[[ $# -ge 2 ]] || { say ERROR "--enable-modules requires a value"; exit 1; }
|
||||
CLI_ENABLE_MODULES_RAW+=("$2"); shift 2
|
||||
@@ -930,6 +938,61 @@ fi
|
||||
BACKUP_RETENTION_HOURS=$(ask "Hourly backups retention (hours)" "${CLI_BACKUP_HOURS:-$DEFAULT_BACKUP_HOURS}" validate_number)
|
||||
BACKUP_DAILY_TIME=$(ask "Daily backup hour (00-23, UTC)" "${CLI_BACKUP_TIME:-$DEFAULT_BACKUP_TIME}" validate_number)
|
||||
|
||||
# Server configuration
|
||||
say HEADER "SERVER CONFIGURATION PRESET"
|
||||
local SERVER_CONFIG_PRESET
|
||||
|
||||
if [ -n "$CLI_CONFIG_PRESET" ]; then
|
||||
SERVER_CONFIG_PRESET="$CLI_CONFIG_PRESET"
|
||||
say INFO "Using preset from command line: $SERVER_CONFIG_PRESET"
|
||||
else
|
||||
declare -A CONFIG_PRESET_NAMES=()
|
||||
declare -A CONFIG_PRESET_DESCRIPTIONS=()
|
||||
declare -A CONFIG_MENU_INDEX=()
|
||||
local config_dir="$SCRIPT_DIR/config/presets"
|
||||
local menu_index=1
|
||||
|
||||
echo "Choose a server configuration preset:"
|
||||
|
||||
if [ -x "$SCRIPT_DIR/scripts/parse-config-presets.py" ] && [ -d "$config_dir" ]; then
|
||||
while IFS=$'\t' read -r preset_key preset_name preset_desc; do
|
||||
[ -n "$preset_key" ] || continue
|
||||
CONFIG_PRESET_NAMES["$preset_key"]="$preset_name"
|
||||
CONFIG_PRESET_DESCRIPTIONS["$preset_key"]="$preset_desc"
|
||||
CONFIG_MENU_INDEX[$menu_index]="$preset_key"
|
||||
echo "$menu_index) $preset_name"
|
||||
echo " $preset_desc"
|
||||
menu_index=$((menu_index + 1))
|
||||
done < <(python3 "$SCRIPT_DIR/scripts/parse-config-presets.py" list --presets-dir "$config_dir")
|
||||
else
|
||||
# Fallback if parser script not available
|
||||
CONFIG_MENU_INDEX[1]="none"
|
||||
CONFIG_PRESET_NAMES["none"]="Default (No Preset)"
|
||||
CONFIG_PRESET_DESCRIPTIONS["none"]="Use default AzerothCore settings"
|
||||
echo "1) Default (No Preset)"
|
||||
echo " Use default AzerothCore settings without any modifications"
|
||||
fi
|
||||
|
||||
local max_config_option=$((menu_index - 1))
|
||||
|
||||
if [ "$NON_INTERACTIVE" = "1" ]; then
|
||||
SERVER_CONFIG_PRESET="none"
|
||||
say INFO "Non-interactive mode: Using default configuration preset"
|
||||
else
|
||||
while true; do
|
||||
read -p "$(echo -e "${YELLOW}🎯 Select server configuration [1-$max_config_option]: ${NC}")" choice
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$max_config_option" ]; then
|
||||
SERVER_CONFIG_PRESET="${CONFIG_MENU_INDEX[$choice]}"
|
||||
local chosen_name="${CONFIG_PRESET_NAMES[$SERVER_CONFIG_PRESET]}"
|
||||
say INFO "Selected: $chosen_name"
|
||||
break
|
||||
else
|
||||
say ERROR "Please select a number between 1 and $max_config_option"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
local MODE_SELECTION=""
|
||||
local MODE_PRESET_NAME=""
|
||||
declare -A MODULE_PRESET_CONFIGS=()
|
||||
@@ -1604,6 +1667,9 @@ EOF
|
||||
# Client data
|
||||
CLIENT_DATA_VERSION=${CLIENT_DATA_VERSION:-$DEFAULT_CLIENT_DATA_VERSION}
|
||||
|
||||
# Server configuration
|
||||
SERVER_CONFIG_PRESET=$SERVER_CONFIG_PRESET
|
||||
|
||||
# Playerbot runtime
|
||||
PLAYERBOT_ENABLED=$PLAYERBOT_ENABLED
|
||||
PLAYERBOT_MIN_BOTS=$PLAYERBOT_MIN_BOTS
|
||||
|
||||
Reference in New Issue
Block a user