6.0 KiB
AI Disclosure
yeah, 100%. I read the code, should be fine.
Zomboid savefiles are a mess IMHO, this was the quickest way to get to the goal.
If you don't like it, don't use it, idc.
Zomboid Converter
client-hosted → dedicated server
Rust CLI that converts a locally hosted Project Zomboid multiplayer world into a dedicated server layout.
The world save format is the same (B42 Saves/Multiplayer/…). Conversion is packaging + naming + players.db world rename + mod recovery — not a binary rewrite.
This repository is only the converter. Sibling folders next to a checkout (e.g. original/, server/) are optional local testing aids and are not part of the git tree.
Build
cargo build --release
# binary: target/release/zomboid-converter
Copy the binary wherever you run the dedicated server (example below: ~/docker/zomboid/bin/).
Inputs / outputs
Typical source pair from a host:
Saves/Multiplayer/<WorldName>/— authoritative world (use this)Saves/Multiplayer/<WorldName>_player/— client cache (ignore)
Server settings live separately under Zomboid/Server/<WorldName>.* on the host. They are not inside the world folder. Pass the directory that contains those files with --server-config.
--input may be a world directory or a .zip (opened read-only; extracted to a temp dir). Paths ending in _player are rejected.
--output is the dedicated server-data root (writes Saves/Multiplayer/<server-name>/ and patches Server/<server-name>.ini).
Example (Hetzner / Docker layout)
Assumes:
| Path | Role |
|---|---|
~/docker/zomboid/ |
Dedicated server (docker-compose.yml, .env) |
~/docker/zomboid/server-data/ |
Zomboid userdir (Saves, Server, db, …) |
~/docker/zomboid/server-files/ |
Game install (Steam download target) |
~/docker/zomboid/bin/zomboid-converter |
This binary |
~/docker/copyparty/data/zomboid/… |
Uploaded host world + configs |
# stop the server first if it is running
cd ~/docker/zomboid
docker compose stop
./bin/zomboid-converter \
--input ~/docker/copyparty/data/zomboid/alt/serverb42stable \
--output ~/docker/zomboid/server-data \
--server-name pzserver \
--server-config ~/docker/copyparty/data/zomboid/alt \
--force
docker compose up -d
docker compose logs -f
Or edit and run ~/docker/zomboid/convert-and-start.sh (config block at the top).
--server-name (default pzserver) must match SERVER_NAME in the server .env. It is only a basename for the dedicated world and Server/*.ini files.
--workshop-dir defaults to <output>/../server-files/steamapps/workshop/content/108600 when that directory exists (fills WorkshopItems=). Until Steam has downloaded mods once, re-run after the first install if needed.
Arguments
| Flag | Description |
|---|---|
-i, --input |
Hosted world dir or zip |
-o, --output |
Dedicated server-data root |
-n, --server-name |
World + config basename (default: pzserver) |
--server-config |
Dir with host <WorldName>.ini, _SandboxVars.lua, spawn files |
--workshop-dir |
…/workshop/content/108600 — fills WorkshopItems= (auto if present next to server-data) |
--password |
Server join password (Password= in ini); overrides .env |
--env-file |
.env for password (default: <output>/../.env) |
--force |
Replace existing Saves/Multiplayer/<server-name>/ |
What it does
- Copies the world into
output/Saves/Multiplayer/<server-name>/ - Skips syncthing conflict sidecars (
*-ZwS1g3qi*),*.db-journal, and player-cache junk - Sets
networkPlayers.worldinplayers.dbto<server-name> - Recovers mod IDs from
WorldDictionaryReadable.luaintoServer/<name>.ini(Mods=) - Resolves Steam workshop IDs into
WorkshopItems=when a workshop dir is available - Optionally sets
Password=from--passwordorSERVER_PASSWORD/PASSWORDin.env
It does not overwrite db/<server-name>.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 notes
Use any Project Zomboid dedicated image/layout you like. With Docker next to server-data:
SERVER_NAMEmust match converter--server-name- Optional:
SERVER_PASSWORDin.envfor the converter’sPassword=patch - Also typical:
ADMIN_USERNAME/ADMIN_PASSWORD,RCON_PASSWORD, memory, ports - Ports:
16261/udp,16262/udp,27015/tcp(RCON) - Stop the container before re-running the converter with
--force - After first start, wait until workshop items finish installing (
Workshop: … Installedin logs)
Recommended migration flow
- Stop the dedicated server.
- Upload/copy the host world (not the
_playerfolder) and ideally the hostServer/<WorldName>.*configs. - Run the converter with
--server-configpointing at the config directory. - Start the dedicated server; wait for workshop installs if needed.
- 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, dedicated defaults remain. |
| Mods with no WorldDictionary entries | Pure Lua/QoL mods may not appear in recovered Mods=. |
| Workshop versions | IDs come from the workshop cache on disk; newer mod builds than the host used can cause chunk LENGTH mismatch / blam errors. |
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 the original host world if that happened. |
Example passwords
# next to docker-compose (.env)
SERVER_PASSWORD=hunter2
# or CLI override
zomboid-converter … --password 'hunter2'