在 Project Zomboid Build 42 专用服务器存档上,删除除安全屋范围以外的所有区块文件
(map/<X>/<Y>.bin),服务器重启后这些区域按地图包重新生成——地形、建筑、战利品、丧尸全部刷新,
而玩家的安全屋(含基地建筑与箱子)原样保留。
适合:跑久了的世界想"翻新",但不想动任何玩家的家。
关于
map_meta.binBuild 42 没有公开这个文件的格式。本项目靠一条特征签名(屋主字符串在记录里出现两次) 给记录定界,再按固定偏移读矩形和字符串。字段布局跟游戏版本绑定,官方一改就可能失效, 届时跑一次--list立刻能看出来。图形界面、备份还原、批处理流程、中文界面均为本项目自加。
从源码跑(图形界面本身只依赖 Python 自带的 tkinter):
pip install pywebview # 只有"地图嵌进窗口"需要;没装会自动退回系统浏览器
python gui.py # 打开图形界面
python tests/gui_test.py # 界面回归测试(22 项)
python tests/web_server_test.py # 内置预览服务测试(12 项)
node tests/coord_test.js # 网页回归测试(26 项,需要 node)
python build.bat # 打包成 dist\区块清理器.exe(需要 pyinstaller)生成底图的两个脚本要额外装 pillow,用法见下面「底图」一节。打包成 exe 后这些都自带,用户不用装任何东西。
- 把
区块清理器.exe和web\文件夹放到同一个文件夹(拷整个dist\目录最省事)。 - 先关闭僵尸毁灭工程服务器(关掉服务器进程/窗口)。
- 双击
区块清理器.exe—— 打开图形界面,没有黑框。 - 选存档(「自动查找」会扫常见位置),需要的话填备份目录和外扩格数。
- 点 ① 分析 → 界面上出现:已生成区块 / 保留 / 将被删除 / 安全屋数量,以及安全屋清单。
- 想在地图上核对就点 打开网页预览(紫=已有区块 绿=保留 红=将被删除 黄框=安全屋)。
- 点 ② 执行清理,在确认框里手动输入
删除两个字,开始删除。 - 看到"清理完成"后启动服务器,被删区域已重新生成。
命令行用法(可选,需要装 Python,在源码目录运行):
python pz_chunk_cleaner.py "D:\Zomboid\Saves\Multiplayer\servertest" # 指定存档
python pz_chunk_cleaner.py --dry # 只分析,不删除
python pz_chunk_cleaner.py --list # 只列出安全屋
python pz_chunk_cleaner.py --pad 20 --backup-dir "E:\PZ备份" # 外扩 20 格 + 备份到 E 盘
| 参数 | 说明 |
|---|---|
--pad N |
安全屋四周额外保护 N 格(默认读配置,0) |
--backup-dir DIR |
本次备份到 DIR(不写则用 config.json 的 backup_root) |
--no-backup |
本次不备份,即使 backup_root 已设置 |
--dry |
只分析 + 生成网页索引,不删任何文件 |
--yes / -y |
跳过二次确认(危险,慎用) |
--force |
跳过服务器进程检测 |
--list |
列出所有安全屋(屋主/城镇/坐标/成员)后退出 |
--no-keep |
忽略 keep.json 额外保护区 |
--no-web |
不提示打开网页预览 |
备不备份,只看 backup_root:填了路径就备份,留 null/"" 就完全不备份(也不会建任何目录)。
界面上"备份目录"留空就是这个意思。
| 机制 | 说明 |
|---|---|
| 进程检测 | 每次删除前用 PowerShell 查 ProjectZomboid* 进程与 zombie.network.GameServer 命令行,运行中直接拒绝;检测不了则要求手动输 yes 确认 |
| 安全屋为 0 则中止 | 解析不到任何安全屋时拒绝执行(否则会删光全图),这是最重要的一道闸 |
| 输入确认 | 必须手动输入 删除 两个字(可在配置里改)才能继续 |
| 默认保留安全屋 | 安全屋矩形内(+可配置缓冲)的区块一律不动 |
| 备份(可选) | backup_root 填了路径才会备份:文件先移动到 <backup_root>\存档名_时间戳\,保留 X\Y.bin 目录结构,拷回即还原。留空则完全不备份 |
| 范围预览 | 删除前生成 web\save_index.json,可在网页地图上核对范围 |
| 只删区块文件 | 只处理 map/<数字>/<数字>.bin;map_zone.bin、map_meta.bin 等其它文件一律不碰 |
{
"save_root": "", // 上次用的存档路径
"pad_tiles": 0, // 安全屋外扩保护格数
"backup_root": null, // 备份目录;null 或 "" = 不备份(也可以填 "E:\\PZ备份" 这种绝对/相对路径)
"confirm_word": "删除", // 需要手动输入的确认词
"check_server_process": true,
"use_keep_file": true, // 是否读取 keep.json 额外保护区
"keep_file": "keep.json",
"open_web": true // 分析完成后询问是否打开网页预览
}备份开关就是 backup_root 这一项:填了路径才备份,留 null 就一个文件都不动、
也不会创建任何备份目录。程序在实际删除前会把最终备份路径(含时间戳)打印在清理计划里。
分发出去的文件夹长这样(也就是 dist\ 的内容):
区块清理器.exe
config.json # 运行时生成:上次的存档、缓冲格数、备份目录
keep.json # 可选:网页导出的额外保护区
web\
├── index.html # 网页预览(内置在 exe 里,首次运行释放出来)
├── map.jpg # 底图总览(已随包提供)
├── tiles\ # 全分辨率瓦片(已随包提供,约 25 MB)
└── save_index.json# 每次分析后生成
<backup_root>\ # 只有设置了备份目录才会出现
主窗口有两个标签页,「② 地图预览」里的地图是直接嵌在程序窗口里的(WebView2),不弹浏览器。
进入这个标签页时程序会:
- 在本机起一个内置小服务(只监听
127.0.0.1,端口自动挑); - 把 WebView2 控件挂进窗口那块区域,加载
http://127.0.0.1:<端口>/?auto=1; - 页面自动拉取当前这次分析的结果——不用找文件、不用手动载入。
区块清理器.exe
├─ 内置服务(127.0.0.1) / 网页 · /save_index.json 分析结果
│ /api/state 状态 · POST /api/keep 存回保护区
└─ WebView2 嵌进窗口 ←───────┘
- 往返是通的:在地图上
Shift拖拽框出要额外保住的区域 → 点「保存到清理器」→ 写入 exe 同目录的keep.json,窗口那边会自动重新分析把新保护区算进去。 - 关掉程序时 WebView2 会被显式释放(不释放会留下 Chromium 子进程占内存)。
- 万一嵌入失败(缺 WebView2 运行时等),会自动退回用系统浏览器打开,功能一样。
想手动确认组件是否就绪:
区块清理器.exe --selftest。
想单独跑一次嵌入自检(会短暂显示窗口 5~10 秒,跑完自动释放):
区块清理器.exe --selftest-embed
地图上能看到:
- 紫 已生成区块 绿 保留(安全屋) 红 将被删除
- 左上角实时显示光标处的坐标:
坐标= 世界方格(和游戏内坐标一致),区块= 世界坐标 ÷ 8 的区块号; 平移/缩放时读数会跟着刷新,鼠标移出画布显示–,框选时改为显示选区的范围与尺寸 - 黄色虚线 = 安全屋矩形,洋红虚线 = 本页框选的额外保护区; 悬停安全屋可看屋主 / 城镇 / 成员
- 底图是 8192×6578 的正投影俯视图(北在上)+ 一整套瓦片金字塔,由
assets\Map.png生成—— 1 像素 = 1 世界格的原版地图渲染,已配准;放大后自动换成全分辨率瓦片(见下面"底图"一节) - 底图对不齐时改左上角的
TR/BL世界坐标锚点(当前底图对应19797,0/0,15897) - 网格下拉只影响网格线,
1 格 / 8 格(区块)/ 50 / 150 / 300; 颜色叠加层最细按 1 个区块统计(存档数据本身就只到区块级)
性能:底图和叠加层画在底层画布,光标十字/拖拽框画在上层。鼠标移动只重画上层, 平移缩放才重画底层;叠加层只遍历视野内的格子。实测 116 万区块的存档: 载入 41 ms、缩放时每帧 0.2~4.6 ms。
想额外保住某个基地/公共仓库:按住 Shift 拖拽框选,然后点"导出 keep.json",
把文件放到 exe 同目录,下次运行清理器会自动把这块也算进保护范围。
| 操作 | 作用 |
|---|---|
| 拖拽 / 滚轮 | 平移 / 缩放 |
Shift + 拖拽 |
增加一块额外保护区 |
Ctrl+Shift + 拖拽 |
减去一块 |
| 框内右键 | 删除该框 |
退格 |
撤销上一块 |
- B42 存档结构:每个区块 =
map/<X>/<Y>.bin一个文件,X = 世界x ÷ 8、Y = 世界y ÷ 8(1 区块 = 8×8 世界方格)。删掉文件后,服务器加载到该位置发现没有存档数据, 就按地图包重新生成整块内容。 - 安全屋解析:
map_meta.bin(B42 未公开格式)里,安全屋记录长这样:[int32 x][int32 y][int32 w][int32 h][长度+屋主][int64][长度+屋主]——屋主字符串出现两次, 用它作为签名一次性定位。本工具用一条正则扫完整个文件(4.5 MB 约 0.05 秒), 比逐字节遍历快约 90 倍。 - 保护范围:安全屋矩形(可外扩
pad_tiles格)+keep.json里的额外保护区, 换算成区块坐标后,只删除不在保护集合里的.bin。
说明:
zpop/apop/chunkdata等 metacell 级统计数据不随区块删除(格式不公开,不动它), 游戏会在区块重新生成后自行处理。
区块重置器\ ← 仓库根
├── README.md
├── .gitignore / .gitattributes
├── gui.py # 图形界面(tkinter,Python 自带,无第三方依赖)
├── pz_chunk_cleaner.py # 引擎 + 命令行模式(纯标准库)
├── web_server.py # 内置预览服务(只监听 127.0.0.1,纯标准库)
├── embedded_view.py # 把 WebView2 嵌进 Tk 窗口(Windows,需 pywebview)
├── pz-chunk-cleaner.spec # PyInstaller 配置(窗口模式,产品名在 spec 里指定)
├── build.bat # 双击打包
├── web\
│ └── index.html # 汉化网页预览(会被打进 exe)
├── tools\
│ ├── make_tiles.py # 切瓦片金字塔 → web\tiles\(需 Pillow)
│ └── make_basemap.py # 生成底图总览 → web\map.jpg(需 Pillow)
├── tests\
│ ├── fixture.py # 生成样例存档(测试用,不依赖外部文件)
│ ├── coord_test.js # 网页逻辑回归测试(node tests/coord_test.js)
│ ├── gui_test.py # 界面回归测试(python tests/gui_test.py)
│ └── web_server_test.py # 内置预览服务测试(python tests/web_server_test.py)
├── assets\ # 底图源图放这里,见 assets\README.md
└── dist\ # 打包产物(.gitignore,不进版本库)
哪些进版本库:源码、网页、测试、脚本。
哪些不进(.gitignore 挡住了,但会留在你本地磁盘上):
assets\Map.png、assets\world.png(共 95 MB 的底图源图)、
web\map.jpg、web\tiles\(由脚本生成)、dist\(打包产物)、
config.json / save_index.json / keep.json(运行时生成)。
源图是放在 assets\ 的 Map.png / world.png:1 像素 = 1 世界格,内容左上角就是世界坐标 (0,0)。
这个对应关系不是猜的,是用配准算出来的(与 1:1 世界坐标基准做多尺度互相关,
结果 k=1.0000 像素/格、偏移 (0,0),峰值尖锐);world.png 与之坐标完全一致(平移 0,0)。
为什么要切瓦片:源图 19797×15897 = 3.15 亿像素,整张解码要占 1.26 GB 内存 (Chrome 的图片上限约 2.68 亿像素,直接超了),所以单张图最多只能压到 8192 宽(0.41 像素/格)。 切成瓦片后网页只解码视野内的块——全分辨率 1 像素 = 1 格,内存反而只有几十 MB。
web\map.jpg 8192×6578 总览图(3.8 MB):秒出、垫底,瓦片没到位时先顶
web\tiles\<级别>\<x>_<y>.jpg 金字塔,级别 0 = 全分辨率,共 1677 块 24.7 MB
pip install pillow
# 重新生成瓦片(推荐做法)
python tools/make_tiles.py # 从 assets/Map.png,级别 0..6
python tools/make_tiles.py --from-image assets/world.png # 换成地形渲染
python tools/make_tiles.py --tile 1024 --quality 80 # 块更大 / 压得更狠
# 总览图(一并换掉;瓦片缺失时靠它兜底)
python tools/make_basemap.py --from-image assets/Map.png --width 8192网页按缩放级别自动选瓦片(放大到 1:1 时用第 0 级),只请求视野内、每块只请求一次,
视野外按 LRU 淘汰(上限约 360 块)。瓦片文件夹不存在也没关系——自动退到总览图,不会一直刷 404。
生成后脚本会打印该用的 TR / BL 锚点(当前是 19797,0 / 0,15897)。
为什么没用 pzmap.org 的"俯视图":网站上的 Scheme 俯视模式是浏览器端用矢量数据实时画的, 没有可下载的图片;它唯一的栅格瓦片是 45° 等距渲染,且东侧部分瓦片缺失(404)。
重新打包(需要 pip install pyinstaller):双击 build.bat,或
python -m PyInstaller --noconfirm --clean pz-chunk-cleaner.spec
开发调试:python gui.py(开图形界面)、python pz_chunk_cleaner.py --dry(命令行模式),都不用打包。
Q: 界面上「执行清理」是灰的? 要先点「分析」。另外改了存档路径/外扩格数之后,之前的结果会作废,需要重新分析—— 这是故意的,防止拿旧结果去删新参数下的区块。
Q: 双击 exe 没反应?
这是窗口程序,不会弹黑框。如果窗口没出来,多半是缺东西;用 区块清理器.exe --selftest
自检一下(退出码 0 正常,出错详情写在 %TEMP%\pz_cleaner_selftest.txt)。
Q: 提示"没有解析到任何安全屋"?
用 --list 看看。若存档确实没有安全屋,工具会拒绝执行(防误删全图)。
真要清空全图,请手动删除或改用其它工具。
Q: 想保留某个区域(公共农场、活动场地)?
打开网页预览,Shift 框选后导出 keep.json 放到 exe 同目录。
Q: 删错了想恢复?
只有在配置里填了 backup_root 才会备份,才可恢复:把
<backup_root>\存档名_时间戳\X\Y.bin 按原路径拷回存档的 map\ 目录即可。
没设备份位置就删了,无法恢复。
Q: 安全屋名/成员名单不准?
map_meta.bin 是未公开格式,屋主和坐标可靠,名字和成员是启发式猜的,仅供参考。
Q: 杀毒软件报警?
PyInstaller 打包的 exe 常见误报。可改用源码运行:python pz_chunk_cleaner.py。
本工具直接修改服务器存档文件。使用前请务必停止服务器,并建议首次使用时开启
--backup。地图版权归 The Indie Stone 所有。
本仓库代码由 Arwy269 编写,按 MIT 许可发布。