How It Works
Map coordinates, cell math, and save file structure for PZ Build 42.
01 Map Hierarchy
Project Zomboid organizes its world in three nested levels. Everything scales up from a single tile:
Save files use cell coordinates for directories and chunk coordinates for individual file names.
02 B41 vs B42
Build 42 changed the grid constants. Scripts written for B41 will calculate wrong chunk ranges on B42 saves.
- Cell size
- 300 × 300 tiles
- Chunk size
- 10 × 10 tiles
- Chunks / cell
- 30 × 30
- Cell size
- 256 × 256 tiles
- Chunk size
- 8 × 8 tiles
- Chunks / cell
- 32 × 32
03 Coordinate Calculation
To find which chunk files belong to a cell, multiply the cell coordinate by chunks-per-cell:
$ chunk_start = cell_coord × 32
$ chunk_end = chunk_start + 31
Worked example — Cell (6, 57)
Any save file with chunk coordinates in those ranges belongs to cell (6, 57).
04 Save File Structure
The cleanup script needs to know which directories use cell vs chunk coordinates:
| Directory | Coords | Example file |
|---|---|---|
map_* |
chunk | map_192_1824.bin |
chunkdata_* |
chunk | chunkdata_192_1824.bin |
zpop_* |
cell | zpop_6_57.bin |
For chunk directories, the script expands the cell into the full 32×32 range and deletes every match. For cell directories it deletes by cell coordinate directly.
05 map_meta.bin Format
The map_meta.bin file sits at the save root and stores server-wide metadata: room exploration state, building alarms, safehouses, factions, and more. It’s a flat binary blob read sequentially — no headers, no offsets, just one section after another.
String encoding
All strings use a simple format: a 2-byte signed length followed by that many bytes of standard UTF-8. A length of 0 means an empty string.
┌────────────┬───────────────────────┐
│ int16 len │ byte[] utf8 (len) │
└────────────┴───────────────────────┘
File layout
Each section is read in order. Counts precede repeated entries, so the parser always knows how many items to expect.
-
1
Header
4 magic bytes,
int32worldVersion, grid boundsx1 y1 x2 y2 -
2
Rooms per cell
int64metaID,int16flags — explored, lights, spawn, roof -
3
Buildings per cell
int64metaID, alarm state, key, visited, loot timer, decay* - 4 Safehouses Rect, owner, members, title, location — see detail below
-
5
Non-PvP Zones
Rect
x y x2 y2, size, title - 6 Factions Name, owner, optional tag with RGB color, member list
-
7
Designation Zones
float64id, position, type, name, last seen hour - 8 Stash System Possible stashes, buildings to do, already-read maps
- 9 Unique RDS Spawned Item names already spawned by the random distribution system
* Some fields are version-gated: alarmDecay requires worldVersion ≥ 201, hitPoints ≥ 216, created and location ≥ 223.
Safehouse detail
Each safehouse entry defines a rectangle in world tile coordinates with ownership and member lists:
int32 count
┌─ int32 x, y, w, h ← bounding rect (tile coords)
│ string owner ← who claimed it
│ int32 hitPoints (v≥216)
│ int32 nPlayers
│ └─ string playerName[] ← authorized members
│ int64 lastVisited ← epoch ms
│ string title ← display name
│ int64 datetimeCreated (v≥223)
│ string location (v≥223) ← town name
│ int32 nRespawn
└── └─ string respawnName[] ← players respawning here
Coordinates are world tiles, not cells. To convert: cell = floor(tile / 256). Width and height define how many tiles the safehouse covers.
projectzomboid.jar using CFR. Key classes: IsoMetaGrid.load(), SafeHouse.load(), GameWindow$StringUTF. The file uses Java’s ByteBuffer — big-endian, no alignment padding.
06 CLI Script
Prefer the command line? pzcc.sh runs the cleanup directly on your server — no browser needed.
Usage
$ chmod +x pzcc.sh
$ ./pzcc.sh --path /server/Zomboid/Saves/Sandbox/myworld --purge 6,57 8,60
Options
| Flag | Description |
|---|---|
--path <dir> | Path to the PZ server save directory |
--purge <cx,cy> | Cell coordinate to purge (repeatable) |
--dry-run | Show what would be deleted without deleting anything |
-h, --help | Show help |
Dry run first
Always preview before deleting. The --dry-run flag lists every file that would be removed:
$ ./pzcc.sh --path /server/saves --purge 6,57 --dry-run
=== DRY RUN — no files will be deleted ===
Base path: /server/saves
Cells to purge: 6,57
--- Cell 6,57 ---
[dry-run] rm /server/saves/chunkdata/chunkdata_6_57.bin
[dry-run] rm /server/saves/zpop/zpop_6_57.bin
[dry-run] rm /server/saves/map/192/1824.bin
...
Would delete 7 files. Run without --dry-run to execute.
With the web tool
Select cells in the map tool, then click Copy cell coords to get a ready-to-paste command.