diff --git a/docker-compose.mythica.yml b/docker-compose.mythica.yml new file mode 100644 index 0000000..cfaef68 --- /dev/null +++ b/docker-compose.mythica.yml @@ -0,0 +1,80 @@ +# Portainer stack for the Mythica armory. +# +# Differs from the upstream docker-compose.yml in three ways, all of them +# because Portainer deploys from a git checkout it manages itself: +# +# 1. Configuration comes from environment variables set in the Portainer +# stack UI, not from a .env file in the repo (upstream uses env_file, +# which Portainer git stacks do not provide). +# 2. Only the four large model-data directories are bind-mounted from the +# host. The DBC .csv files stay in the image so they stay versioned with +# the code, and Portainer re-cloning the repo cannot wipe the 2 GB of +# model data. +# 3. The unused ac-network block is dropped. +# +# See docs/mythica-deploy.md for the full procedure. + +services: + armory: + build: + context: . + dockerfile: Dockerfile + container_name: mythica-armory + restart: unless-stopped + + environment: + # --- site identity ------------------------------------------------- + ACORE_ARMORY_WEBSITE_NAME: ${ARMORY_WEBSITE_NAME:-Mythica} + ACORE_ARMORY_WEBSITE_URL: ${ARMORY_WEBSITE_URL:-http://192.168.1.77:48733} + ACORE_ARMORY_WEBSITE_ROOT: ${ARMORY_WEBSITE_ROOT:-} + + # Item tooltips and item icons are loaded from this AoWoW instance. + # The default is a public third-party site: point this at your own + # AoWoW if you do not want that dependency. + ACORE_ARMORY_AOWOW_URL: ${ARMORY_AOWOW_URL:-https://wowgaming.altervista.org/aowow} + + # --- embedding ----------------------------------------------------- + # Set both when embedding the armory in a page on the main site. + ACORE_ARMORY_IFRAME_MODE__ENABLED: ${ARMORY_IFRAME_ENABLED:-0} + ACORE_ARMORY_IFRAME_MODE__URL: ${ARMORY_IFRAME_URL:-} + + # --- behaviour ----------------------------------------------------- + ACORE_ARMORY_LOAD_DBCS: ${ARMORY_LOAD_DBCS:-1} + ACORE_ARMORY_HIDE_GAME_MASTERS: ${ARMORY_HIDE_GMS:-1} + ACORE_ARMORY_TRANSMOG_MODULE: ${ARMORY_TRANSMOG:-0} + + # 0 = serve 3D model assets from the local data directory. + # Leave it at 0: the upstream fetch tool can no longer rebuild this + # data from the Zam CDN, so the local copy is the only source. + ACORE_ARMORY_USE_ZAM_CDN: ${ARMORY_USE_ZAM_CDN:-0} + + # --- realm --------------------------------------------------------- + ACORE_ARMORY_REALMS__0__NAME: ${ARMORY_REALM_NAME:-Mythica} + ACORE_ARMORY_REALMS__0__REALM_ID: ${ARMORY_REALM_ID:-1} + ACORE_ARMORY_REALMS__0__AUTH_DATABASE: ${ARMORY_AUTH_DB:-acore_auth} + ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__HOST: ${ARMORY_DB_HOST:-192.168.1.60} + ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PORT: ${ARMORY_DB_PORT:-3306} + ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__USER: ${ARMORY_DB_USER:?set ARMORY_DB_USER} + ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PASSWORD: ${ARMORY_DB_PASS:?set ARMORY_DB_PASS} + ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__DATABASE: ${ARMORY_CHARACTERS_DB:-acore_characters} + + # --- world database ------------------------------------------------- + ACORE_ARMORY_WORLD_DATABASE__HOST: ${ARMORY_DB_HOST:-192.168.1.60} + ACORE_ARMORY_WORLD_DATABASE__PORT: ${ARMORY_DB_PORT:-3306} + ACORE_ARMORY_WORLD_DATABASE__USER: ${ARMORY_DB_USER:?set ARMORY_DB_USER} + ACORE_ARMORY_WORLD_DATABASE__PASSWORD: ${ARMORY_DB_PASS:?set ARMORY_DB_PASS} + ACORE_ARMORY_WORLD_DATABASE__DATABASE: ${ARMORY_WORLD_DB:-acore_world} + + ACORE_ARMORY_DB_QUERY_TIMEOUT: ${ARMORY_DB_TIMEOUT:-10000} + + volumes: + # The 3D model payload, extracted from the release archive. These + # four directories are exactly the top level of data.tar.gz. + - ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/meta:/data/meta + - ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/bone:/data/bone + - ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/mo3:/data/mo3 + - ${ARMORY_DATA_DIR:-/opt/mythica-armory/data}/textures:/data/textures + - ${ARMORY_LOG_DIR:-/opt/mythica-armory/logs}:/logs + + ports: + - "${ARMORY_PORT:-48733}:48733" diff --git a/docs/mythica-deploy.md b/docs/mythica-deploy.md new file mode 100644 index 0000000..8668941 --- /dev/null +++ b/docs/mythica-deploy.md @@ -0,0 +1,129 @@ +# Deploying the armory on Nox + +This fork of [r-o-b-o-t-o/azerothcore-armory](https://github.com/r-o-b-o-t-o/azerothcore-armory) +(MIT) is set up to run as a Portainer stack on Nox, alongside the Mythica web +page, reading the AzerothCore databases on the game server over the LAN. + +**It must not run on the AzerothCore VM (192.168.1.60).** That box is tuned for +the playerbot load; anything extra there means redoing that balancing. Nothing +in this procedure touches the game server — the armory only reads its databases. + +--- + +## Why the model data is a manual step + +The armory needs about 2 GB of processed 3D model data — bone sets, mo3 meshes, +textures, and the per-race character descriptors. + +Upstream ships a `fetchdata` tool that downloads this from Zam's CDN. **It no +longer works.** Zam reorganised that part of the CDN, and every character path +now 404s (`meta/character/*`, `meta/charactercustomization2/*` — checked against +the `live`, `wrath` and `classic` prefixes and several filename variants). The +armor and item paths still resolve, so a run appears to succeed while silently +producing no character data, and the app then fails at startup on +`data/meta/charactercustomization2/1_0.json`. + +The only remaining source is the snapshot in upstream's v1.0.0 release. That +snapshot is mirrored into this fork's own release so the fork does not depend on +GitHub staying up for the one piece that cannot be regenerated. + +The mirrored copy is **split into five parts** because Nginx Proxy Manager in +front of the forge rejects request bodies at that size (500 MB uploads fine, +2 GB returns 413). Raising `client_max_body_size` on the forge's proxy host +would let it be stored as a single file, if that is ever worth doing. + +--- + +## 1. Put the model data on Nox + +Once. Redeploying the stack does not disturb it. + +```bash +sudo mkdir -p /opt/mythica-armory/data /opt/mythica-armory/logs +cd /opt/mythica-armory/data + +BASE=https://gitlab.hallsworth.ca/yrtria/azerothcore-armory/releases/download/v1.0.0 +for i in 0 1 2 3 4; do curl -LO $BASE/data.tar.gz.part-$i; done +curl -LO $BASE/SHA256SUMS.txt + +cat data.tar.gz.part-* > data.tar.gz +sha256sum -c --ignore-missing SHA256SUMS.txt # data.tar.gz must say OK + +tar xzf data.tar.gz +rm -f data.tar.gz data.tar.gz.part-* +``` + +Afterwards `/opt/mythica-armory/data` holds four directories — `meta`, `bone`, +`mo3`, `textures` — totalling roughly 2.5 GB unpacked. + +The DBC `.csv` files are **not** part of this. They are committed to the repo and +travel in the image, so they stay in step with the code. + +> `data.tar.gz.UPSTREAM-GITHUB-LINK.txt` on the same release is not the archive. +> It is the original external reference Forgejo recorded during migration — a +> short-lived signed GitHub URL, kept only for provenance. + +## 2. Create the Portainer stack + +Repository stack, same pattern as `wow-web-page`: + +| Field | Value | +|---|---| +| Repository URL | `https://gitlab.hallsworth.ca/yrtria/azerothcore-armory.git` | +| Reference | `refs/heads/master` | +| Compose path | `docker-compose.mythica.yml` | + +Environment variables — `ARMORY_DB_USER` and `ARMORY_DB_PASS` are required and +the stack refuses to start without them: + +| Variable | Value | +|---|---| +| `ARMORY_DB_USER` | database user (see §4) | +| `ARMORY_DB_PASS` | its password | +| `ARMORY_DB_HOST` | `192.168.1.60` | +| `ARMORY_WEBSITE_URL` | `https://armory.dionysismedia.ca` once proxied, otherwise `http://192.168.1.77:48733` | +| `ARMORY_WEBSITE_NAME` | `Mythica` | +| `ARMORY_REALM_ID` | `1` | +| `ARMORY_DATA_DIR` | `/opt/mythica-armory/data` | +| `ARMORY_LOG_DIR` | `/opt/mythica-armory/logs` | + +The first build compiles TypeScript, so it takes a few minutes. It listens on +48733. + +## 3. Reverse proxy + +Add a proxy host in NPM pointing at `192.168.1.77:48733`. + +To embed it in the main site instead of giving it its own subdomain, set +`ARMORY_IFRAME_ENABLED=1` and `ARMORY_IFRAME_URL` to the page that hosts the +iframe, then use the snippet in the upstream README. That path keeps the site's +own header and navigation around it. + +## 4. Database access + +`acore` is already granted from `192.168.1.%` and MySQL on the game server binds +`0.0.0.0`, so this works with no changes. But the armory only ever reads, and it +is public-facing, so a dedicated read-only user is worth the two minutes: + +```sql +CREATE USER 'armory'@'192.168.1.%' IDENTIFIED BY ''; +GRANT SELECT ON acore_characters.* TO 'armory'@'192.168.1.%'; +GRANT SELECT ON acore_world.* TO 'armory'@'192.168.1.%'; +GRANT SELECT ON acore_auth.* TO 'armory'@'192.168.1.%'; +FLUSH PRIVILEGES; +``` + +## 5. Things to know + +- **Item icons and tooltips come from a third-party AoWoW instance** + (`wowgaming.altervista.org` by default) — they are hotlinked, so they break if + that site moves or goes down. Point `ARMORY_AOWOW_URL` at your own AoWoW to + remove that dependency. This is the one remaining external asset dependency; + the model data is now self-hosted. +- **The armory has its own talent view** with dual-spec support, which overlaps + the talent page already in `WoW-Web-Page`. Decide which one owns the character + page rather than shipping both. +- **The Dockerfile builds from `node:16`**, which is end-of-life. It builds and + runs fine; bumping it is a separate change from getting this deployed. +- **`hideGameMasters` defaults to on** here, so GM characters are hidden from + search and return 404.