# Zomboid client-hosted → dedicated server Convert a locally **hosted** Project Zomboid multiplayer world into a **dedicated** server layout, then run it with Docker. The world save format is the same (B42 `Saves/Multiplayer/…`). Conversion is packaging + naming + `players.db` world rename + mod recovery — not a binary rewrite. ## Layout Paths below are relative to the **repo root** (parent of this `converter/` folder): | Path | Role | |------|------| | `original/` | Source saves / zips (**do not edit the zips**) | | `converter/` | This crate — Rust CLI (`zomboid-converter`) | | `server/` | Docker dedicated server (`docker-compose.yml`, `.env`) | | `server/server-data/` | Zomboid userdir (Saves, Server, db, Logs) | | `server/server-files/` | Game install (Steam download target) | Typical source pair from a host: - `Saves/Multiplayer//` — authoritative world (**use this**) - `Saves/Multiplayer/_player/` — client cache (**ignore**) Server settings live separately under `Zomboid/Server/.*` on the host machine. They are **not** inside the world folder. ## Prerequisites - Rust toolchain (for the converter) - Docker + Docker Compose - Optional: local Steam workshop cache for app `108600` (to resolve `WorkshopItems`) ## Converter ```bash cd converter cargo build --release cargo run --release -- \ --input ../original/serverb42stable \ --output ../server/server-data \ --server-name pzserver \ --workshop-dir /path/to/steamapps/workshop/content/108600 \ --force ``` `--input` may be a world **directory** or a **`.zip`** (opened read-only; extracted to a temp dir). Paths ending in `_player` are rejected. ### Arguments | Flag | Description | |------|-------------| | `-i`, `--input` | Hosted world dir or zip | | `-o`, `--output` | Dedicated `server-data` root | | `-n`, `--server-name` | World + config basename (default: `pzserver`) | | `--workshop-dir` | `…/workshop/content/108600` — fills `WorkshopItems=` | | `--server-config` | Optional host `Server/` dir to copy/rename configs | | `--password` | Server join password (`Password=` in ini); overrides `.env` | | `--env-file` | `.env` for password (default: `/../.env`) | | `--force` | Replace existing `Saves/Multiplayer//` | ### What it does 1. Copies the world into `output/Saves/Multiplayer//` 2. Skips syncthing conflict sidecars (`*-ZwS1g3qi*`), `*.db-journal`, and player-cache junk 3. Sets `networkPlayers.world` in `players.db` to `` 4. Recovers mod IDs from `WorldDictionaryReadable.lua` into `Server/.ini` (`Mods=`) 5. If `--workshop-dir` is set, resolves Steam workshop IDs (`WorkshopItems=`) 6. Optionally sets `Password=` from `--password` or `SERVER_PASSWORD` / `PASSWORD` in `.env` It does **not** overwrite `db/.db` (accounts / whitelist stay managed by the server). ### Characters / login Characters bind to the **account username** in `players.db` (`networkPlayers.username`), not Steam display name. Players must log in with that exact username (or you rename the column before they join). ## Dedicated server (Docker) ```bash cd ../server cp .env.example .env # if needed; set passwords / SERVER_NAME docker compose up -d docker compose logs -f ``` Important `.env` keys: - `SERVER_NAME` — must match converter `--server-name` (default `pzserver`) - `ADMIN_USERNAME` / `ADMIN_PASSWORD` — admin account - `RCON_PASSWORD` — RCON - `SERVER_PASSWORD` — optional join password for the converter (`Password=` in ini) Ports: `16261/udp`, `16262/udp`, `27015/tcp`. Stop the container before re-running the converter with `--force`. ## Recommended migration flow 1. Stop the dedicated server. 2. Copy the host world into `original/` (keep zips untouched as backups). 3. Ideally also copy the host’s `Zomboid/Server/.ini` (+ `_SandboxVars.lua`, spawn files) and pass `--server-config`. 4. Run the converter with `--workshop-dir` and `--force`. 5. Start Docker; wait until workshop items finish installing (`Workshop: … Installed` in logs). 6. Join with the **same username** as in the hosted `players.db`. ## Known gaps / caveats | Item | Notes | |------|--------| | Host `SandboxVars` / spawn / `Map=` | Not in the world save. Without `--server-config`, the dedicated defaults remain. | | Mods with no WorldDictionary entries | Pure Lua/QoL mods may not appear in recovered `Mods=`. | | Workshop **versions** | IDs are resolved from your local workshop cache; newer mod builds than the host used can cause chunk `LENGTH mismatch` / `blam` errors and odd world state (e.g. zombies in cleared bases). | | `ZombieMigrate` | Dedicated sandbox may allow migration even when respawn is off; align sandbox with the host when possible. | | Running without mods first | Can rewrite `WorldDictionary` and map chunks. Re-import from `original/` if that happened. | ## Example passwords ```bash # ../server/.env (next to docker-compose) SERVER_PASSWORD=hunter2 # or CLI override (from this directory) cargo run --release -- … --password 'hunter2' ```