Compare commits
10 Commits
31c61dd639
...
0a3d3b6afa
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a3d3b6afa | |||
| c9b1100e3a | |||
| b608754d36 | |||
| 119b2a500b | |||
| 83262368ac | |||
| 0f2ec7c1b3 | |||
| b0d62064ba | |||
| ba63751269 | |||
| dc835fa9f8 | |||
| d03e96cc9b |
+29
-1
@@ -261,7 +261,7 @@ ACORE_PROGRESSION_LIMIT=0
|
|||||||
ACORE_PROGRESSION_ENFORCE_GROUP_RULES=1
|
ACORE_PROGRESSION_ENFORCE_GROUP_RULES=1
|
||||||
|
|
||||||
# На какой стадии открываются расы TBC.
|
# На какой стадии открываются расы TBC.
|
||||||
ACORE_PROGRESSION_TBC_RACES_UNLOCK=8
|
ACORE_PROGRESSION_TBC_RACES_UNLOCK=0
|
||||||
|
|
||||||
# На какой стадии открывается создание Death Knight.
|
# На какой стадии открывается создание Death Knight.
|
||||||
ACORE_PROGRESSION_DK_UNLOCK=13
|
ACORE_PROGRESSION_DK_UNLOCK=13
|
||||||
@@ -276,6 +276,34 @@ ACORE_PROGRESSION_MC_AQUAL_COOLDOWN_REDUCTION=60
|
|||||||
# 0 = отключены, 1 = включены.
|
# 0 = отключены, 1 = включены.
|
||||||
ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS=0
|
ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS=0
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# mod-progressive-quest-xp
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 1 = включает прогрессивный множитель XP за квесты.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_ENABLE=1
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 1-20.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_1_20=1.5
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 21-30.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_21_30=1.3
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 31-40.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_31_40=1.1
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 41-50.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_41_50=1.0
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 51-60.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_51_60=1.0
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 61-70.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_61_70=1.0
|
||||||
|
|
||||||
|
# Множитель квестового XP для уровней 70-80.
|
||||||
|
ACORE_DYNAMIC_QUEST_XP_LEVEL_70_PLUS=1.0
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# mod-aoe-loot
|
# mod-aoe-loot
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,401 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Project-specific instructions for coding agents working in this repository.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
These instructions apply to the whole repository rooted at this directory.
|
||||||
|
|
||||||
|
## Project Shape
|
||||||
|
|
||||||
|
- This repo is an AzerothCore 3.3.5a server with local custom deployment glue.
|
||||||
|
- It is run primarily through Docker Compose, not through direct host binaries.
|
||||||
|
- The important local customizations live in:
|
||||||
|
- `.env` and `.env.example`
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- `start-server.sh`
|
||||||
|
- `create-account.sh`
|
||||||
|
- `sync-realmlist.sh`
|
||||||
|
- `import-custom-sql.sh`
|
||||||
|
- `lua_scripts/`
|
||||||
|
- `sql/`
|
||||||
|
- vendored modules in `modules/`
|
||||||
|
|
||||||
|
## Source Of Truth
|
||||||
|
|
||||||
|
- Treat `.env` plus the helper scripts as the source of truth for deployment behavior.
|
||||||
|
- Do not hand-edit `env/dist/etc/*` as a permanent fix. Those files are generated runtime config.
|
||||||
|
- If a change must persist, make it in `.env`, `.env.example`, or the generating shell script.
|
||||||
|
- Keep `.env.example` aligned with `.env` defaults when changing deployment settings.
|
||||||
|
|
||||||
|
## Generated And Ignored Paths
|
||||||
|
|
||||||
|
- `env/dist/*` is generated and gitignored.
|
||||||
|
- `var/*` is generated and gitignored.
|
||||||
|
- `.env` is local and gitignored.
|
||||||
|
- `src/server/scripts/Custom/*` is ignored except for:
|
||||||
|
- `src/server/scripts/Custom/README.md`
|
||||||
|
- `src/server/scripts/Custom/custom_script_loader.cpp`
|
||||||
|
- Do not rely on generated files being committed.
|
||||||
|
|
||||||
|
## Docker Workflow
|
||||||
|
|
||||||
|
Prefer the local wrapper scripts over raw compose commands when they already encode repo-specific logic.
|
||||||
|
|
||||||
|
### Primary commands
|
||||||
|
|
||||||
|
- Full startup:
|
||||||
|
- `./start-server.sh`
|
||||||
|
- `./start-server.sh --no-build`
|
||||||
|
- `./start-server.sh --logs`
|
||||||
|
- Regenerate module and world configs:
|
||||||
|
- `./setup-modules.sh`
|
||||||
|
- `./setup-modules.sh --dry-run`
|
||||||
|
- Sync realm address and gamebuild into auth DB:
|
||||||
|
- `./sync-realmlist.sh`
|
||||||
|
- Create or update an account:
|
||||||
|
- `./create-account.sh <username> <password> --gmlevel 0`
|
||||||
|
- Import custom SQL that is outside the normal module update path:
|
||||||
|
- `./import-custom-sql.sh`
|
||||||
|
|
||||||
|
### Useful raw compose commands
|
||||||
|
|
||||||
|
- `docker compose ps`
|
||||||
|
- `docker compose logs -f ac-worldserver ac-authserver`
|
||||||
|
- `docker compose stop`
|
||||||
|
- `docker compose down`
|
||||||
|
|
||||||
|
## Compose And Runtime Conventions
|
||||||
|
|
||||||
|
- `docker-compose.yml` is upstream-style base config. Prefer local changes in `docker-compose.override.yml`.
|
||||||
|
- Service names used by local scripts:
|
||||||
|
- `ac-database`
|
||||||
|
- `ac-db-import`
|
||||||
|
- `ac-client-data-init`
|
||||||
|
- `ac-authserver`
|
||||||
|
- `ac-worldserver`
|
||||||
|
- The deployment expects Docker Compose v2 (`docker compose`, not `docker-compose`).
|
||||||
|
|
||||||
|
## Config Layering Rules
|
||||||
|
|
||||||
|
### World config
|
||||||
|
|
||||||
|
- `setup-modules.sh` manages selected keys in `env/dist/etc/worldserver.conf`.
|
||||||
|
- If you need a persistent world config change, add it to `setup-modules.sh` and usually expose it through `.env`.
|
||||||
|
|
||||||
|
### Module config
|
||||||
|
|
||||||
|
- This repo must write effective module overrides to `env/dist/etc/modules/*.conf.dist`.
|
||||||
|
- Do not introduce a parallel `*.conf` override layer for modules here.
|
||||||
|
- AzerothCore loads module config files from the compiled module config list, and in this repo that list resolves to `*.conf.dist`.
|
||||||
|
- If you change module defaults, update the corresponding writer function in `setup-modules.sh`.
|
||||||
|
|
||||||
|
### Playerbots
|
||||||
|
|
||||||
|
- `mod-playerbots` is supported here, but it is a fragile area.
|
||||||
|
- Keep the repo default conservative:
|
||||||
|
- no random bot autologin
|
||||||
|
- no random bot startup population
|
||||||
|
- small addclass pool
|
||||||
|
- Do not accidentally reintroduce the old broken pattern where only `playerbots.conf` was written.
|
||||||
|
- Be careful with Docker environment overrides that map to `AiPlayerbot.*`; they can silently override file config.
|
||||||
|
|
||||||
|
## Active Module Map
|
||||||
|
|
||||||
|
Use this as the fast path before opening upstream module READMEs.
|
||||||
|
|
||||||
|
### `mod-autobalance`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- scales dungeon and raid mobs for solo, duo, and undersized groups
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-autobalance/conf/AutoBalance.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/AutoBalance.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Current repo intent:
|
||||||
|
- solo-friendly PvE tuning
|
||||||
|
- `MinPlayers = 1` for dungeons and raids
|
||||||
|
- softened raid boss health and damage for incomplete groups
|
||||||
|
- Useful diagnostics:
|
||||||
|
- `.ab mapstat`
|
||||||
|
- `.ab creaturestat`
|
||||||
|
|
||||||
|
### `mod-individual-progression`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- per-character expansion and tier progression on a WotLK client
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-individual-progression/conf/individualProgression.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/individualProgression.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Hard prerequisites already encoded in this repo:
|
||||||
|
- `EnablePlayerSettings = 1`
|
||||||
|
- `DBC.EnforceItemAttributes = 0`
|
||||||
|
- Important behavior:
|
||||||
|
- touches visibility, quest flow, content gating, item stats, restored legacy content, and progression unlocks
|
||||||
|
- Caveat:
|
||||||
|
- optional SQL under this module is not automatically imported unless explicitly wired in
|
||||||
|
|
||||||
|
### `mod-playerbots`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- addclass bots, alt bots, and optional random world bots
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-playerbots/conf/playerbots.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/playerbots.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Separate database:
|
||||||
|
- `acore_playerbots`
|
||||||
|
- Repo-specific caveat:
|
||||||
|
- upstream documents a custom `Playerbot` AzerothCore branch, but this repo carries local compatibility work on top of the current tree
|
||||||
|
- treat changes here as high risk
|
||||||
|
- Current repo intent:
|
||||||
|
- playerbots available for solo/small-group support
|
||||||
|
- random bot population disabled by default
|
||||||
|
- small addclass pool
|
||||||
|
- Fragile points:
|
||||||
|
- Docker environment overrides can silently beat file config
|
||||||
|
- avoid reintroducing large random bot defaults
|
||||||
|
- if startup crashes occur around bot creation, inspect effective `playerbots.conf.dist` first
|
||||||
|
|
||||||
|
### `mod-eluna`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- Lua scripting engine used by local custom packages in `lua_scripts/`
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-eluna/conf/mod_eluna.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/mod_eluna.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Important note:
|
||||||
|
- this repo uses AzerothCore `mod-eluna`, not original Eluna compatibility mode
|
||||||
|
- do not assume third-party Eluna scripts are portable
|
||||||
|
|
||||||
|
### `mod-aoe-loot`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- area looting QoL
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-aoe-loot/conf/mod_aoe_loot.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/mod_aoe_loot.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Extra SQL:
|
||||||
|
- module strings are imported by `import-custom-sql.sh`
|
||||||
|
- Caveat:
|
||||||
|
- if `module_string` rows are missing, world logs show `Module string module mod-aoe-loot id 1 not found in DB`
|
||||||
|
|
||||||
|
### `mod-quest-loot-party`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- grouped quest-loot quality of life
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-quest-loot-party/conf/mod-quest-loot-party.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/mod-quest-loot-party.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
|
||||||
|
### `mod-learnspells`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- auto-grants class spells, talents, proficiencies, and optional riding/quest spells
|
||||||
|
- Source config:
|
||||||
|
- `modules/mod-learnspells/conf/mod_learnspells.conf.dist`
|
||||||
|
- Effective generated config:
|
||||||
|
- `env/dist/etc/modules/mod_learnspells.conf.dist`
|
||||||
|
- Managed by:
|
||||||
|
- `setup-modules.sh`
|
||||||
|
- Current repo intent:
|
||||||
|
- class/proficiency/quest spell automation enabled
|
||||||
|
- riding automation deliberately conservative in the local `.env`
|
||||||
|
|
||||||
|
## Local Lua Package Map
|
||||||
|
|
||||||
|
These packages are part of the active customization layer and are more important than generic upstream documentation.
|
||||||
|
|
||||||
|
### `lua_scripts/AIO_Server`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- shared AIO transport/UI layer for Lua client-server addons
|
||||||
|
- Consumers in this repo:
|
||||||
|
- `MythicPlus`
|
||||||
|
- `Store_System`
|
||||||
|
- If AIO breaks, those packages effectively break too
|
||||||
|
|
||||||
|
### `lua_scripts/MythicPlus`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- Mythic+ gameplay, scoring, keys, affixes, vault, AIO UI
|
||||||
|
- Data dependencies:
|
||||||
|
- character tables:
|
||||||
|
- `character_mythic_history`
|
||||||
|
- `character_mythic_keys`
|
||||||
|
- `character_mythic_rating`
|
||||||
|
- `character_mythic_vault`
|
||||||
|
- `character_mythic_weekly_affixes`
|
||||||
|
- world tables:
|
||||||
|
- `world_mythic_loot`
|
||||||
|
- `world_vault_loot`
|
||||||
|
- Bootstrap:
|
||||||
|
- handled by `import-custom-sql.sh`
|
||||||
|
- Extra world bootstrap:
|
||||||
|
- repairs creature/item/gameobject entries around `900001`, `900100`, `900000`
|
||||||
|
- Caveat:
|
||||||
|
- missing bootstrap often shows up as `Couldn't find a creature with (ID: 900001)!`
|
||||||
|
- empty `world_vault_loot` is non-fatal but means no vault rewards
|
||||||
|
|
||||||
|
### `lua_scripts/Store_System`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- AIO-based in-game store UI and purchase handling
|
||||||
|
- Data dependencies:
|
||||||
|
- separate MySQL schema `store`
|
||||||
|
- Bootstrap:
|
||||||
|
- handled by `import-custom-sql.sh`
|
||||||
|
- imports `sql/Store.sql`
|
||||||
|
- Caveat:
|
||||||
|
- if `store` database is missing, Lua throws SQL errors and the store becomes unusable
|
||||||
|
|
||||||
|
### `lua_scripts/StartZoneRaceTeleporters`
|
||||||
|
|
||||||
|
- Purpose:
|
||||||
|
- race-specific start-zone teleporter NPCs
|
||||||
|
- Files:
|
||||||
|
- Lua logic:
|
||||||
|
- `lua_scripts/StartZoneRaceTeleporters/start_zone_race_teleporters.lua`
|
||||||
|
- base SQL:
|
||||||
|
- `lua_scripts/StartZoneRaceTeleporters/sql/world/start_zone_race_teleporters.sql`
|
||||||
|
- Bootstrap:
|
||||||
|
- imported automatically by `setup-modules.sh` when `ac-database` is already running
|
||||||
|
- Caveat:
|
||||||
|
- base SQL does not create all world spawns automatically
|
||||||
|
- example/manual spawn SQL still needs to be added per start zone
|
||||||
|
|
||||||
|
### `lua_scripts/Battlepass`
|
||||||
|
|
||||||
|
- Current state:
|
||||||
|
- present in tree but effectively empty
|
||||||
|
- Guidance:
|
||||||
|
- do not assume it is an active, wired gameplay system until code and bootstrap are actually added
|
||||||
|
|
||||||
|
### `lua_scripts/hello_world.lua`
|
||||||
|
|
||||||
|
- Current state:
|
||||||
|
- simple local script/example
|
||||||
|
- Guidance:
|
||||||
|
- low-risk when testing Eluna loading, not a core gameplay system
|
||||||
|
|
||||||
|
## Operational Feature Map
|
||||||
|
|
||||||
|
This repo is not just “AzerothCore plus random modules”. The intended gameplay stack is:
|
||||||
|
|
||||||
|
- solo/small-group PvE:
|
||||||
|
- `mod-autobalance`
|
||||||
|
- softened elite HP/damage in `worldserver.conf`
|
||||||
|
- progression playthrough:
|
||||||
|
- `mod-individual-progression`
|
||||||
|
- optional bot-assisted party filling:
|
||||||
|
- `mod-playerbots`
|
||||||
|
- QoL:
|
||||||
|
- `mod-aoe-loot`
|
||||||
|
- `mod-quest-loot-party`
|
||||||
|
- `mod-learnspells`
|
||||||
|
- custom Lua systems:
|
||||||
|
- `MythicPlus`
|
||||||
|
- `Store_System`
|
||||||
|
- `StartZoneRaceTeleporters`
|
||||||
|
|
||||||
|
When making a change, identify which layer you are actually modifying:
|
||||||
|
|
||||||
|
- core C++ behavior
|
||||||
|
- module config generation
|
||||||
|
- custom SQL bootstrap
|
||||||
|
- Eluna Lua logic
|
||||||
|
- Docker/deployment behavior
|
||||||
|
|
||||||
|
## Custom SQL Rules
|
||||||
|
|
||||||
|
- Normal AzerothCore/module SQL is handled by `ac-db-import`.
|
||||||
|
- This repo also has custom SQL outside standard module paths. That logic lives in `import-custom-sql.sh`.
|
||||||
|
- At the time of writing, this includes bootstrap/import logic for:
|
||||||
|
- MythicPlus SQL under `sql/Mythic+/`
|
||||||
|
- Store SQL under `sql/Store.sql`
|
||||||
|
- `mod-aoe-loot` module strings
|
||||||
|
- If you add a new Lua package or custom SQL tree outside the standard module import layout, extend `import-custom-sql.sh`.
|
||||||
|
|
||||||
|
## Local Lua And Custom Content
|
||||||
|
|
||||||
|
- `lua_scripts/` contains active server-side custom Lua packages.
|
||||||
|
- Some Lua features depend on SQL bootstrap outside the normal module importer.
|
||||||
|
- If you add or rename a Lua package that requires DB state, update both:
|
||||||
|
- the file tree
|
||||||
|
- `import-custom-sql.sh`
|
||||||
|
|
||||||
|
## Build And Edit Guidance
|
||||||
|
|
||||||
|
- Do not rebuild the whole core unless the change actually requires it.
|
||||||
|
- For shell-script changes, validate with:
|
||||||
|
- `bash -n <script>`
|
||||||
|
- For compose changes, validate with:
|
||||||
|
- `docker compose config`
|
||||||
|
- For config-generation changes, validate by running:
|
||||||
|
- `./setup-modules.sh --dry-run`
|
||||||
|
- then `./setup-modules.sh`
|
||||||
|
- If you change realm-related settings, run:
|
||||||
|
- `./sync-realmlist.sh`
|
||||||
|
- If you change custom SQL bootstrap, run:
|
||||||
|
- `./import-custom-sql.sh`
|
||||||
|
- with `ac-database` already running
|
||||||
|
|
||||||
|
## C++ And Core Notes
|
||||||
|
|
||||||
|
- This is still a normal AzerothCore source tree under `src/`.
|
||||||
|
- `authserver` and `worldserver` are the two runtime executables.
|
||||||
|
- `modules/` is vendored into this repo. Do not treat it as git submodules.
|
||||||
|
- Keep `src/server/scripts/Custom/custom_script_loader.cpp` present and tracked; removing it breaks linking of `AddCustomScripts()`.
|
||||||
|
|
||||||
|
## What To Change For Common Tasks
|
||||||
|
|
||||||
|
### Change deployment defaults
|
||||||
|
|
||||||
|
- Edit `.env.example`
|
||||||
|
- If the repo default should also change for the current working copy, edit `.env`
|
||||||
|
- Update any helper script that derives behavior from those variables
|
||||||
|
|
||||||
|
### Change module behavior
|
||||||
|
|
||||||
|
- Edit `setup-modules.sh`
|
||||||
|
- Regenerate configs with `./setup-modules.sh`
|
||||||
|
|
||||||
|
### Change auth/world exposure or realm address
|
||||||
|
|
||||||
|
- Edit `.env`
|
||||||
|
- Run `./sync-realmlist.sh`
|
||||||
|
|
||||||
|
### Change account bootstrap behavior
|
||||||
|
|
||||||
|
- Edit `create-account.sh`
|
||||||
|
- Keep SRP/account logic self-contained there
|
||||||
|
|
||||||
|
### Change first-run database bootstrap
|
||||||
|
|
||||||
|
- Edit `import-custom-sql.sh`
|
||||||
|
- Keep imports idempotent
|
||||||
|
|
||||||
|
## Practical Safety Notes
|
||||||
|
|
||||||
|
- Prefer additive, idempotent changes in shell scripts.
|
||||||
|
- Preserve current local server assumptions unless the task explicitly changes them:
|
||||||
|
- solo/small-group friendly tuning
|
||||||
|
- generated config flow
|
||||||
|
- Docker-first operation
|
||||||
|
- If a fix only works by editing generated files under `env/dist`, it is incomplete.
|
||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Production environment — copy to .env before deploying
|
||||||
|
# cp .env.production .env
|
||||||
|
|
||||||
|
# Docker image tag (master = latest stable build)
|
||||||
|
DOCKER_IMAGE_TAG=master
|
||||||
|
|
||||||
|
# Database root password — CHANGE THIS
|
||||||
|
DOCKER_DB_ROOT_PASSWORD=CHANGE_ME_STRONG_PASSWORD
|
||||||
|
|
||||||
|
# External ports
|
||||||
|
DOCKER_WORLD_EXTERNAL_PORT=8085
|
||||||
|
DOCKER_AUTH_EXTERNAL_PORT=3724
|
||||||
|
DOCKER_SOAP_EXTERNAL_PORT=7878
|
||||||
|
DOCKER_DB_EXTERNAL_PORT=3306
|
||||||
|
|
||||||
|
# Run server as this user (match host UID/GID to avoid volume permission issues)
|
||||||
|
# Run: id -u && id -g to get your values
|
||||||
|
DOCKER_USER=acore
|
||||||
|
DOCKER_USER_ID=1000
|
||||||
|
DOCKER_GROUP_ID=1000
|
||||||
|
|
||||||
|
# Volume paths (use absolute paths in production)
|
||||||
|
DOCKER_VOL_ETC=./env/dist/etc
|
||||||
|
DOCKER_VOL_LOGS=./env/dist/logs
|
||||||
|
DOCKER_VOL_DATA=ac-client-data
|
||||||
|
|
||||||
|
# Game rates (can also be set in worldserver.conf)
|
||||||
|
AC_RATE_XP_KILL=1
|
||||||
|
ACORE_PLAYERBOTS_RANDOM_BOT_AUTOLOGIN=0
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
services:
|
||||||
|
ac-database:
|
||||||
|
command: >
|
||||||
|
--innodb_buffer_pool_size=4G
|
||||||
|
--innodb_redo_log_capacity=1G
|
||||||
|
--innodb_flush_log_at_trx_commit=2
|
||||||
|
--innodb_flush_method=O_DIRECT
|
||||||
|
--max_connections=300
|
||||||
|
--slow_query_log=1
|
||||||
|
--long_query_time=2
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '2'
|
||||||
|
memory: 5G
|
||||||
|
|
||||||
|
ac-worldserver:
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '6'
|
||||||
|
memory: 6G
|
||||||
|
ulimits:
|
||||||
|
nofile:
|
||||||
|
soft: 65536
|
||||||
|
hard: 65536
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "100m"
|
||||||
|
max-file: "5"
|
||||||
|
|
||||||
|
ac-authserver:
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1'
|
||||||
|
memory: 512M
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "3"
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#
|
||||||
|
# Progressive Quest XP module configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# DynamicQuestXP settings
|
||||||
|
########################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Enable
|
||||||
|
# Description: Enable progressive quest XP multiplier
|
||||||
|
# Default: 1 - (Enabled)
|
||||||
|
# 0 - (Disabled)
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Enable = 1
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_1_20
|
||||||
|
# Description: XP multiplier for quest rewards at levels 1-20
|
||||||
|
# Default: 1.5
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_1_20 = 1.5
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_21_30
|
||||||
|
# Description: XP multiplier for quest rewards at levels 21-30
|
||||||
|
# Default: 1.3
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_21_30 = 1.3
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_31_40
|
||||||
|
# Description: XP multiplier for quest rewards at levels 31-40
|
||||||
|
# Default: 1.1
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_31_40 = 1.1
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_41_50
|
||||||
|
# Description: XP multiplier for quest rewards at levels 41-50
|
||||||
|
# Default: 1.0
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_41_50 = 1.0
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_51_60
|
||||||
|
# Description: XP multiplier for quest rewards at levels 51-60
|
||||||
|
# Default: 1.0
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_51_60 = 1.0
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_61_70
|
||||||
|
# Description: XP multiplier for quest rewards at levels 61-70
|
||||||
|
# Default: 1.0
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_61_70 = 1.0
|
||||||
|
|
||||||
|
#
|
||||||
|
# DynamicQuestXP.Level_70_plus
|
||||||
|
# Description: XP multiplier for quest rewards at levels 70-80
|
||||||
|
# Default: 1.0
|
||||||
|
#
|
||||||
|
|
||||||
|
DynamicQuestXP.Level_70_plus = 1.0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Progressive Quest XP Module for AzerothCore
|
||||||
|
*
|
||||||
|
* Applies a level-based XP multiplier to quest rewards only.
|
||||||
|
* Does not affect kill XP, exploration XP, or PvP XP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Player.h"
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
|
||||||
|
class ProgressiveQuestXPPlayer : public PlayerScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProgressiveQuestXPPlayer() : PlayerScript("ProgressiveQuestXPPlayer",
|
||||||
|
{PLAYERHOOK_ON_QUEST_COMPUTE_EXP}) { }
|
||||||
|
|
||||||
|
void OnPlayerQuestComputeXP(Player* player, Quest const* /*quest*/,
|
||||||
|
uint32& xpValue) override
|
||||||
|
{
|
||||||
|
if (!sConfigMgr->GetOption<bool>("DynamicQuestXP.Enable", true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!xpValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8 level = player->GetLevel();
|
||||||
|
float multiplier;
|
||||||
|
|
||||||
|
if (level <= 20)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_1_20", 1.5f);
|
||||||
|
else if (level <= 30)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_21_30", 1.3f);
|
||||||
|
else if (level <= 40)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_31_40", 1.1f);
|
||||||
|
else if (level <= 50)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_41_50", 1.0f);
|
||||||
|
else if (level <= 60)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_51_60", 1.0f);
|
||||||
|
else if (level <= 70)
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_61_70", 1.0f);
|
||||||
|
else
|
||||||
|
multiplier = sConfigMgr->GetOption<float>(
|
||||||
|
"DynamicQuestXP.Level_70_plus", 1.0f);
|
||||||
|
|
||||||
|
xpValue = static_cast<uint32>(xpValue * multiplier);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddSC_ProgressiveQuestXP()
|
||||||
|
{
|
||||||
|
new ProgressiveQuestXPPlayer();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
void AddSC_ProgressiveQuestXP();
|
||||||
|
|
||||||
|
void Addmod_progressive_quest_xpScripts()
|
||||||
|
{
|
||||||
|
AddSC_ProgressiveQuestXP();
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
|
||||||
|
OUTPUT_FILE=""
|
||||||
|
COMPRESS=1
|
||||||
|
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") [--output FILE] [--no-compress]
|
||||||
|
|
||||||
|
Exports all AzerothCore databases from the running ac-database container
|
||||||
|
into a single dump file suitable for migration to another server.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--output FILE Output file path
|
||||||
|
(default: ./backups/YYYY-MM-DD_HH-MM-SS.sql.gz)
|
||||||
|
--no-compress Write plain .sql instead of .sql.gz
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$(basename "$0")
|
||||||
|
$(basename "$0") --output /mnt/backup/moonwell.sql.gz
|
||||||
|
$(basename "$0") --no-compress --output /tmp/dump.sql
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
log() { printf '[db-export] %s\n' "$*"; }
|
||||||
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
require_running_db() {
|
||||||
|
if ! docker compose ps --status running --services 2>/dev/null | grep -qx 'ac-database'; then
|
||||||
|
die "ac-database is not running. Start it with: docker compose up -d ac-database"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns space-separated list of user databases (excludes MySQL system schemas)
|
||||||
|
get_user_databases() {
|
||||||
|
docker compose exec -T ac-database bash -lc \
|
||||||
|
"mysql -h 127.0.0.1 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -s information_schema" \
|
||||||
|
<<<"SELECT schema_name FROM schemata
|
||||||
|
WHERE schema_name NOT IN ('mysql','information_schema','performance_schema','sys')
|
||||||
|
ORDER BY schema_name;"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── argument parsing ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
while (($#)); do
|
||||||
|
case "$1" in
|
||||||
|
--output) OUTPUT_FILE="$2"; shift ;;
|
||||||
|
--no-compress) COMPRESS=0 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) printf 'Unknown option: %s\n\n' "$1" >&2; usage >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── main ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
require_running_db
|
||||||
|
|
||||||
|
if [[ -z "$OUTPUT_FILE" ]]; then
|
||||||
|
mkdir -p "$ROOT_DIR/backups"
|
||||||
|
timestamp="$(date '+%Y-%m-%d_%H-%M-%S')"
|
||||||
|
if [[ "$COMPRESS" -eq 1 ]]; then
|
||||||
|
OUTPUT_FILE="$ROOT_DIR/backups/${timestamp}.sql.gz"
|
||||||
|
else
|
||||||
|
OUTPUT_FILE="$ROOT_DIR/backups/${timestamp}.sql"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$OUTPUT_FILE")"
|
||||||
|
|
||||||
|
log "discovering user databases..."
|
||||||
|
databases="$(get_user_databases | tr '\n' ' ' | xargs)"
|
||||||
|
[[ -z "$databases" ]] && die "no user databases found"
|
||||||
|
log "databases: $databases"
|
||||||
|
log "exporting → $OUTPUT_FILE"
|
||||||
|
log "this may take a few minutes for large worlds..."
|
||||||
|
|
||||||
|
if [[ "$COMPRESS" -eq 1 ]]; then
|
||||||
|
docker compose exec -T ac-database bash -lc \
|
||||||
|
"mysqldump -h 127.0.0.1 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \
|
||||||
|
--databases $databases \
|
||||||
|
--add-drop-database \
|
||||||
|
--single-transaction \
|
||||||
|
--quick \
|
||||||
|
--routines \
|
||||||
|
--triggers \
|
||||||
|
--events" \
|
||||||
|
| gzip -6 > "$OUTPUT_FILE"
|
||||||
|
else
|
||||||
|
docker compose exec -T ac-database bash -lc \
|
||||||
|
"mysqldump -h 127.0.0.1 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \
|
||||||
|
--databases $databases \
|
||||||
|
--add-drop-database \
|
||||||
|
--single-transaction \
|
||||||
|
--quick \
|
||||||
|
--routines \
|
||||||
|
--triggers \
|
||||||
|
--events" \
|
||||||
|
> "$OUTPUT_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "done: $OUTPUT_FILE ($(du -sh "$OUTPUT_FILE" | cut -f1))"
|
||||||
|
log ""
|
||||||
|
log "to restore on the new server:"
|
||||||
|
if [[ "$OUTPUT_FILE" == *.gz ]]; then
|
||||||
|
log " bash scripts/db-import.sh --from $OUTPUT_FILE"
|
||||||
|
else
|
||||||
|
log " bash scripts/db-import.sh --from $OUTPUT_FILE"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
|
||||||
|
DUMP_FILE=""
|
||||||
|
DRY_RUN=0
|
||||||
|
FORCE=0
|
||||||
|
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") --from FILE [--dry-run] [--force]
|
||||||
|
|
||||||
|
Restores a full database dump (produced by db-export.sh) into the
|
||||||
|
ac-database Docker container. The container must be running.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--from FILE Dump file to restore (.sql or .sql.gz) (required)
|
||||||
|
--dry-run Show what would happen without executing
|
||||||
|
--force Skip the confirmation prompt
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$(basename "$0") --from backups/2025-01-01_12-00-00.sql.gz
|
||||||
|
$(basename "$0") --from /mnt/backup/dump.sql.gz --force
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
log() { printf '[db-import] %s\n' "$*"; }
|
||||||
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
require_ready_db() {
|
||||||
|
if ! docker compose ps --status running --services 2>/dev/null | grep -qx 'ac-database'; then
|
||||||
|
die "ac-database is not running. Start it with: docker compose up -d ac-database"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "waiting for MySQL to be ready..."
|
||||||
|
local attempts=0
|
||||||
|
until docker compose exec -T ac-database bash -lc \
|
||||||
|
"mysql -h 127.0.0.1 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -e 'SELECT 1;'" \
|
||||||
|
>/dev/null 2>&1; do
|
||||||
|
attempts=$((attempts + 1))
|
||||||
|
if [[ $attempts -ge 30 ]]; then
|
||||||
|
die "MySQL did not become ready in time. Check: docker logs ac-database"
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
log "MySQL is ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_exec() {
|
||||||
|
docker compose exec -T ac-database bash -lc \
|
||||||
|
"mysql -h 127.0.0.1 -uroot -p\"\$MYSQL_ROOT_PASSWORD\""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── argument parsing ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
while (($#)); do
|
||||||
|
case "$1" in
|
||||||
|
--from) DUMP_FILE="$2"; shift ;;
|
||||||
|
--dry-run) DRY_RUN=1 ;;
|
||||||
|
--force) FORCE=1 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) printf 'Unknown option: %s\n\n' "$1" >&2; usage >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── validation ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
[[ -z "$DUMP_FILE" ]] && { usage >&2; die "--from FILE is required"; }
|
||||||
|
[[ -f "$DUMP_FILE" ]] || die "file not found: $DUMP_FILE"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
# ── preview ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
file_size="$(du -sh "$DUMP_FILE" | cut -f1)"
|
||||||
|
|
||||||
|
printf '\nMigration plan:\n'
|
||||||
|
printf ' Source : %s (%s)\n' "$DUMP_FILE" "$file_size"
|
||||||
|
printf ' Target : ac-database container\n\n'
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
|
log "dry-run mode — nothing imported"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$FORCE" -eq 0 ]]; then
|
||||||
|
printf 'WARNING: this will DROP and recreate all databases in the dump.\n'
|
||||||
|
read -r -p "Continue? [y/N] " confirm
|
||||||
|
[[ "$confirm" =~ ^[Yy]$ ]] || { log "aborted"; exit 0; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── import ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
require_ready_db
|
||||||
|
|
||||||
|
log "restoring $DUMP_FILE into ac-database..."
|
||||||
|
log "this may take several minutes..."
|
||||||
|
|
||||||
|
if [[ "$DUMP_FILE" == *.gz ]]; then
|
||||||
|
zcat "$DUMP_FILE" | mysql_exec
|
||||||
|
else
|
||||||
|
mysql_exec < "$DUMP_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "import complete"
|
||||||
|
log ""
|
||||||
|
log "next steps:"
|
||||||
|
log " 1. Update realmlist: bash sync-realmlist.sh"
|
||||||
|
log " 2. Start the server: bash scripts/prod-deploy.sh --no-build"
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
|
||||||
|
BUILD_FLAG="--build"
|
||||||
|
|
||||||
|
# Production compose stack — layered on top of the standard override
|
||||||
|
COMPOSE="docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.prod.yml"
|
||||||
|
|
||||||
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
log() { printf '[prod-deploy] %s\n' "$*"; }
|
||||||
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
print_service_logs() {
|
||||||
|
local service="$1"
|
||||||
|
printf '\n[%s logs]\n' "$service" >&2
|
||||||
|
$COMPOSE logs --tail=120 "$service" >&2 || true
|
||||||
|
printf '\n' >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_healthy() {
|
||||||
|
local container_name="$1"
|
||||||
|
local timeout_seconds="${2:-180}"
|
||||||
|
local start_ts now status
|
||||||
|
|
||||||
|
start_ts="$(date +%s)"
|
||||||
|
while :; do
|
||||||
|
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_name" 2>/dev/null || true)"
|
||||||
|
case "$status" in
|
||||||
|
healthy|running) return 0 ;;
|
||||||
|
unhealthy|exited|dead) return 1 ;;
|
||||||
|
esac
|
||||||
|
now="$(date +%s)"
|
||||||
|
if (( now - start_ts >= timeout_seconds )); then return 1; fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
run_one_shot_service() {
|
||||||
|
local service="$1"
|
||||||
|
local container_name="$2"
|
||||||
|
|
||||||
|
if [[ -n "$BUILD_FLAG" ]]; then
|
||||||
|
if ! $COMPOSE up --build --no-deps "$service"; then
|
||||||
|
print_service_logs "$service"
|
||||||
|
printf '%s failed during startup\n' "$service" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! $COMPOSE up --no-deps "$service"; then
|
||||||
|
print_service_logs "$service"
|
||||||
|
printf '%s failed during startup\n' "$service" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local exit_code
|
||||||
|
exit_code="$(docker inspect -f '{{.State.ExitCode}}' "$container_name" 2>/dev/null || printf '1')"
|
||||||
|
if [[ "$exit_code" != "0" ]]; then
|
||||||
|
print_service_logs "$service"
|
||||||
|
printf '%s failed with exit code %s\n' "$service" "$exit_code" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_docker() {
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
die "docker is not installed or not in PATH"
|
||||||
|
fi
|
||||||
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
|
die "docker compose plugin is not available. On Ubuntu: apt install docker-compose-plugin"
|
||||||
|
fi
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
die "docker daemon is not reachable. Run: sudo systemctl enable --now docker"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_host_permissions() {
|
||||||
|
local target_uid="${DOCKER_USER_ID:-1000}"
|
||||||
|
local target_gid="${DOCKER_GROUP_ID:-1000}"
|
||||||
|
local dirs=(
|
||||||
|
"$ROOT_DIR/env/dist/etc"
|
||||||
|
"$ROOT_DIR/env/dist/logs"
|
||||||
|
"$ROOT_DIR/env/dist/temp"
|
||||||
|
)
|
||||||
|
|
||||||
|
mkdir -p "${dirs[@]}"
|
||||||
|
|
||||||
|
if [[ "$(id -u)" -eq 0 ]]; then
|
||||||
|
chown -R "${target_uid}:${target_gid}" "${dirs[@]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local dir
|
||||||
|
for dir in "${dirs[@]}"; do
|
||||||
|
if [[ ! -w "$dir" ]]; then
|
||||||
|
printf 'host directory is not writable: %s\n' "$dir" >&2
|
||||||
|
printf 'Run: sudo chown -R %s:%s %q %q %q\n' \
|
||||||
|
"$target_uid" "$target_gid" \
|
||||||
|
"${dirs[0]}" "${dirs[1]}" "${dirs[2]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_sysctl() {
|
||||||
|
log "applying sysctl settings..."
|
||||||
|
sysctl -w net.core.somaxconn=4096 2>/dev/null || true
|
||||||
|
sysctl -w net.ipv4.tcp_max_syn_backlog=4096 2>/dev/null || true
|
||||||
|
sysctl -w fs.file-max=100000 2>/dev/null || true
|
||||||
|
# Prevent Docker conntrack table overflow (causes sudden disconnects)
|
||||||
|
sysctl -w net.netfilter.nf_conntrack_max=131072 2>/dev/null || true
|
||||||
|
sysctl -w net.ipv4.tcp_keepalive_time=120 2>/dev/null || true
|
||||||
|
sysctl -w net.ipv4.tcp_keepalive_intvl=30 2>/dev/null || true
|
||||||
|
sysctl -w net.ipv4.tcp_keepalive_probes=5 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── argument parsing ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $(basename "$0") [--no-build]
|
||||||
|
|
||||||
|
Starts the AzerothCore production docker stack.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--no-build Start containers without rebuilding images
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while (($#)); do
|
||||||
|
case "$1" in
|
||||||
|
--no-build) BUILD_FLAG="" ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) printf 'Unknown option: %s\n\n' "$1" >&2; usage >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── pre-flight ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
if [[ -f "$ROOT_DIR/.env.production" ]]; then
|
||||||
|
log ".env not found — copying from .env.production"
|
||||||
|
cp "$ROOT_DIR/.env.production" "$ENV_FILE"
|
||||||
|
die "Fill in $ENV_FILE (at minimum DOCKER_DB_ROOT_PASSWORD) and re-run."
|
||||||
|
else
|
||||||
|
die "$ENV_FILE not found. Create it based on .env.production."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "CHANGE_ME" "$ENV_FILE"; then
|
||||||
|
die "Change DOCKER_DB_ROOT_PASSWORD in $ENV_FILE before deploying to production."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source env so ensure_host_permissions picks up DOCKER_USER_ID / DOCKER_GROUP_ID
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
require_docker
|
||||||
|
|
||||||
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
apply_sysctl
|
||||||
|
else
|
||||||
|
log "not root — skipping sysctl (run with sudo to apply kernel settings)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── startup sequence (mirrors start-server.sh) ───────────────────────────────
|
||||||
|
|
||||||
|
log "applying module configuration"
|
||||||
|
bash "$ROOT_DIR/setup-modules.sh"
|
||||||
|
|
||||||
|
log "ensuring host directory permissions"
|
||||||
|
ensure_host_permissions
|
||||||
|
|
||||||
|
log "starting database"
|
||||||
|
if [[ -n "$BUILD_FLAG" ]]; then
|
||||||
|
$COMPOSE up -d --build ac-database
|
||||||
|
else
|
||||||
|
$COMPOSE up -d ac-database
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! wait_for_healthy "ac-database" 180; then
|
||||||
|
print_service_logs "ac-database"
|
||||||
|
die "ac-database did not become healthy"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "running database import"
|
||||||
|
run_one_shot_service "ac-db-import" "ac-db-import"
|
||||||
|
|
||||||
|
log "importing custom SQL"
|
||||||
|
bash "$ROOT_DIR/import-custom-sql.sh"
|
||||||
|
|
||||||
|
log "applying post-database module setup"
|
||||||
|
bash "$ROOT_DIR/setup-modules.sh"
|
||||||
|
|
||||||
|
log "initializing client data"
|
||||||
|
run_one_shot_service "ac-client-data-init" "ac-client-data-init"
|
||||||
|
|
||||||
|
log "starting authserver and worldserver"
|
||||||
|
if [[ -n "$BUILD_FLAG" ]]; then
|
||||||
|
$COMPOSE up -d --build ac-authserver ac-worldserver
|
||||||
|
else
|
||||||
|
$COMPOSE up -d ac-authserver ac-worldserver
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "synchronizing realmlist"
|
||||||
|
bash "$ROOT_DIR/sync-realmlist.sh"
|
||||||
|
|
||||||
|
log "current service status"
|
||||||
|
$COMPOSE ps
|
||||||
|
|
||||||
|
printf '\n'
|
||||||
|
printf 'Realm: %s:%s\n' "${ACORE_REALMLIST_PUBLIC_ADDRESS:-127.0.0.1}" "${ACORE_REALMLIST_PORT:-${DOCKER_WORLD_EXTERNAL_PORT:-8085}}"
|
||||||
|
printf 'World: 127.0.0.1:%s\n' "${DOCKER_WORLD_EXTERNAL_PORT:-8085}"
|
||||||
|
printf 'Auth: 127.0.0.1:%s\n' "${DOCKER_AUTH_EXTERNAL_PORT:-3724}"
|
||||||
|
printf 'SOAP: 127.0.0.1:%s\n' "${DOCKER_SOAP_EXTERNAL_PORT:-7878}"
|
||||||
|
printf 'Account: ./create-account.sh admin your_password\n'
|
||||||
|
printf 'Logs: %s logs -f ac-worldserver ac-authserver\n' "$COMPOSE"
|
||||||
@@ -65,6 +65,15 @@ FORCE_PLAYERBOTS="${FORCE_PLAYERBOTS:-${ACORE_PLAYERBOTS_FORCE:-0}}"
|
|||||||
: "${ACORE_PROGRESSION_MC_AQUAL_COOLDOWN_REDUCTION:=60}"
|
: "${ACORE_PROGRESSION_MC_AQUAL_COOLDOWN_REDUCTION:=60}"
|
||||||
: "${ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS:=0}"
|
: "${ACORE_PROGRESSION_PVP_GEAR_REQUIREMENTS:=0}"
|
||||||
|
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_ENABLE:=1}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_1_20:=1.5}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_21_30:=1.3}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_31_40:=1.1}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_41_50:=1.0}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_51_60:=1.0}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_61_70:=1.0}"
|
||||||
|
: "${ACORE_DYNAMIC_QUEST_XP_LEVEL_70_PLUS:=1.0}"
|
||||||
|
|
||||||
: "${ACORE_AOE_LOOT_ENABLE:=1}"
|
: "${ACORE_AOE_LOOT_ENABLE:=1}"
|
||||||
: "${ACORE_AOE_LOOT_MESSAGE:=0}"
|
: "${ACORE_AOE_LOOT_MESSAGE:=0}"
|
||||||
: "${ACORE_AOE_LOOT_RANGE:=55.0}"
|
: "${ACORE_AOE_LOOT_RANGE:=55.0}"
|
||||||
@@ -316,6 +325,8 @@ ensure_world_conf() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sync_module_dists() {
|
sync_module_dists() {
|
||||||
|
sync_file "$ROOT_DIR/modules/mod-progressive-quest-xp/conf/mod_progressive_quest_xp.conf.dist" \
|
||||||
|
"$MODULES_ETC_DIR/mod_progressive_quest_xp.conf.dist"
|
||||||
sync_file "$ROOT_DIR/modules/mod-aoe-loot/conf/mod_aoe_loot.conf.dist" \
|
sync_file "$ROOT_DIR/modules/mod-aoe-loot/conf/mod_aoe_loot.conf.dist" \
|
||||||
"$MODULES_ETC_DIR/mod_aoe_loot.conf.dist"
|
"$MODULES_ETC_DIR/mod_aoe_loot.conf.dist"
|
||||||
sync_file "$ROOT_DIR/modules/mod-autobalance/conf/AutoBalance.conf.dist" \
|
sync_file "$ROOT_DIR/modules/mod-autobalance/conf/AutoBalance.conf.dist" \
|
||||||
@@ -393,6 +404,20 @@ IndividualProgression.PvPGearRequirements = ${ACORE_PROGRESSION_PVP_GEAR_REQUIRE
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_progressive_quest_xp_conf() {
|
||||||
|
write_file "$MODULES_ETC_DIR/mod_progressive_quest_xp.conf.dist" <<EOF
|
||||||
|
# Managed by ./setup-modules.sh
|
||||||
|
DynamicQuestXP.Enable = ${ACORE_DYNAMIC_QUEST_XP_ENABLE}
|
||||||
|
DynamicQuestXP.Level_1_20 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_1_20}
|
||||||
|
DynamicQuestXP.Level_21_30 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_21_30}
|
||||||
|
DynamicQuestXP.Level_31_40 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_31_40}
|
||||||
|
DynamicQuestXP.Level_41_50 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_41_50}
|
||||||
|
DynamicQuestXP.Level_51_60 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_51_60}
|
||||||
|
DynamicQuestXP.Level_61_70 = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_61_70}
|
||||||
|
DynamicQuestXP.Level_70_plus = ${ACORE_DYNAMIC_QUEST_XP_LEVEL_70_PLUS}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
write_aoe_loot_conf() {
|
write_aoe_loot_conf() {
|
||||||
write_file "$MODULES_ETC_DIR/mod_aoe_loot.conf.dist" <<EOF
|
write_file "$MODULES_ETC_DIR/mod_aoe_loot.conf.dist" <<EOF
|
||||||
# Managed by ./setup-modules.sh
|
# Managed by ./setup-modules.sh
|
||||||
@@ -538,6 +563,7 @@ ensure_runtime_dirs() {
|
|||||||
|
|
||||||
cleanup_legacy_module_overrides() {
|
cleanup_legacy_module_overrides() {
|
||||||
local legacy_files=(
|
local legacy_files=(
|
||||||
|
"$MODULES_ETC_DIR/mod_progressive_quest_xp.conf"
|
||||||
"$MODULES_ETC_DIR/AutoBalance.conf"
|
"$MODULES_ETC_DIR/AutoBalance.conf"
|
||||||
"$MODULES_ETC_DIR/individualProgression.conf"
|
"$MODULES_ETC_DIR/individualProgression.conf"
|
||||||
"$MODULES_ETC_DIR/mod_aoe_loot.conf"
|
"$MODULES_ETC_DIR/mod_aoe_loot.conf"
|
||||||
@@ -595,6 +621,7 @@ main() {
|
|||||||
configure_worldserver
|
configure_worldserver
|
||||||
write_autobalance_conf
|
write_autobalance_conf
|
||||||
write_individual_progression_conf
|
write_individual_progression_conf
|
||||||
|
write_progressive_quest_xp_conf
|
||||||
write_aoe_loot_conf
|
write_aoe_loot_conf
|
||||||
write_quest_loot_party_conf
|
write_quest_loot_party_conf
|
||||||
write_eluna_conf
|
write_eluna_conf
|
||||||
|
|||||||
Reference in New Issue
Block a user