renmori protect
renmori protect 将一个 Ren'Py 发行版构建转换为受保护的发行版构建:game/ 下的资产与已编译脚本被封入加密容器 game/renmori.rmc,已编译脚本额外经过脚本层变换,明文原件被删除,加载器扩展 game/renmori.rpe 与构建清单 renmori-manifest.json 被写入。游戏启动时,Ren'Py 自动加载该扩展,由其在引擎内部解密并供给资源。
语法
输入
构建目录
输入目录必须直接包含 game/ 子目录,否则命令以退出码 2 失败。若 game/ 中已存在 renmori.rmc,说明该构建已经受保护,命令同样以退出码 2 失败,不会进行二次保护。请始终以全新的发行版构建,或保护前生成的备份作为输入。
发行归档(.zip)
当输入是一个有效的 .zip 文件时,protect 先将其解压,再对解压结果执行保护:
- 确定输出目录:指定
-o时使用该目录,否则使用与归档同级、以归档名(不含扩展名)命名的目录,例如MyGame-1.0-pc.zip对应MyGame-1.0-pc/。该目录必须不存在 - 在输出目录的上级目录中创建临时目录并解压。解压前会逐项检查归档成员路径,任何会解压到目标目录之外的成员(zip-slip)都会导致命令失败
- 定位构建根目录:解压根目录本身包含
game/时直接使用;否则在顶层子目录中查找唯一一个包含game/的目录,这对应启动器将构建包裹在一层同名文件夹中的常见情况。找不到或找到多个时命令失败 - 将构建根目录移动到输出目录,随后按原地保护的方式处理该目录,并清理临时目录
原归档始终保持不变并可作为原件,因此从归档输入时不会另外创建 _bak 备份。
输出模式
原地保护会删除 game/ 下的明文原件。若 <dir>_bak 已存在,命令会在执行任何改动之前以退出码 2 失败;请删除旧备份、改用 -o,或在确认无需备份时使用 --no-backup。备份目录包含完整明文资源,切勿将其随发行版一同上传。
执行流程
命令按以下顺序执行,所有可预见的冲突都在任何文件被改动之前检查完毕。
- 解析输入:目录直接使用;
.zip先解压并定位构建根目录 - 前置检查:确认
game/存在且尚未受保护 - 合并配置:读取
renmori.toml(若存在),与命令行参数合并,参阅 配置文件 - 确定保护强度:未指定强度时使用完整保护,需要闭源插件;指定
baseline时使用开源基线格式且不下载插件;指定auto时插件可用则使用完整保护,否则使用基线。插件的获取方式参阅 保护强度与闭源插件 - 准备密钥(仅基线):从
[keys]指定的环境变量读取容器密钥与脚本密钥,未指定或变量未设置时为本次构建生成随机密钥 - 预检输出位置:检查
-o目录或<dir>_bak是否已存在,统计待封包文件与脚本数量,没有可封包的文件时失败 - 打印摘要并确认:在 stderr 输出输入、模式、文件数量、脚本数量、锁定状态与密钥来源;交互式终端下请求确认
[y/N],回答y或yes以外的内容都会取消,取消时输出{"status": "aborted"}并以退出码 2 结束 - 复制或备份:
-o模式复制整个构建到输出目录;原地模式先复制到<dir>_bak(--no-backup时跳过) - 封包与加密:将待封包文件逐个写入
game/renmori.rmc,.rpa封包被拆开后逐项写入,已编译脚本经脚本层变换后写入 - 删除明文:删除已封包的原件与被剔除的
.rpy源码,并移除因此变空的子目录(--keep-plaintext时跳过) - 写入加载器扩展:生成
game/renmori.rpe - 写入构建清单:在构建根目录生成
renmori-manifest.json,并在 stdout 输出结果 JSON
使用闭源插件时不执行第 5 步,密钥由插件自行派生;第 8 至 11 步由插件完成,构建清单仍由 renmori 写入。原地模式下 renmori 先将原始构建移至备份目录(--no-backup 时为临时目录,成功后删除),再由插件从该目录读取并写出全新的构建;插件失败时原始构建会被移回原位,-o 模式下产生的不完整输出会被删除。
文件处理规则
protect 遍历 game/ 下的全部文件,并按以下规则分类。下表描述基线保护流程;完整保护由插件执行封包,其文件选择同样遵循 exclude 与 keep_on_disk 的设置。
容器中的条目名称使用 Ren'Py 的游戏相对路径形式:正斜杠分隔,无前导斜杠,例如 images/bg room.png。运行时,容器在加载器链中处于最高优先级,因此即使磁盘上残留同名明文文件,引擎读取的也始终是容器中的版本。
不要通过 exclude 排除 .rpy、.rpyc、.rpym、.rpymc 等脚本文件。运行时加载器会拒绝打开 game/ 中位于磁盘上的脚本类文件,以阻止反编译器注入,因此被排除的脚本将无法加载。exclude 只应用于由引擎之外的程序读取的文件。
选项
输出与备份
-o, --output DIR
将受保护构建写入 DIR,原始构建保持不变。DIR 必须不存在,否则以退出码 2 失败;其上级目录不存在时会自动创建。该模式不创建备份。输入为 .zip 时,DIR 是解压并保护的目标目录。
--no-backup
原地保护时不创建 <dir>_bak 备份,直接改写原始构建。该操作不可撤销,仅建议在 CI 中对一次性的构建目录使用。指定 -o 或输入为 .zip 时此选项无效果。
-y, --yes
跳过保护前的确认提示,以及首次下载闭源插件时的确认提示。stdin 不是终端,或指定了 --quiet 时,提示同样会被跳过。
配置
-c, --config FILE
从 FILE 读取配置。文件不存在时以退出码 2 失败。未指定时,依次查找 <game-dir>/renmori.toml 与当前工作目录下的 renmori.toml,使用找到的第一个;均不存在时使用默认配置。全部配置键参阅 配置文件。
保护强度与插件
--strength S
设置保护强度,对应配置键 strength。
两者的安全性差异参阅 保护强度与闭源插件。
--plugin PATH
使用 PATH 处的闭源插件,不再查找缓存或自动下载。等同于设置 RENMORI_PLUGIN,命令行参数优先。路径不存在时会提示并忽略该参数,继续查找缓存或自动下载。PATH 可以是可执行文件,也可以是由当前 Python 解释器运行的 .py 脚本。
--platforms LIST
仅对插件生效。以逗号分隔的目标平台列表,例如 win-x64,linux-x64,mac-universal,对应配置键 platforms。未指定时由插件根据构建中的 lib/py3-* 目录自动检测。
--launcher
仅对插件生效。将密钥绑定到原生启动器并随构建一同分发,使密钥只在通过该启动器启动游戏时才被重建。对应配置键 launcher。
加密参数
--chunk-size N
容器分块大小,单位为字节,对应配置键 chunk_size,默认 65536。每个分块独立加密并附带 16 字节认证标签。较大的分块有利于大型 CG 与视频,较小的分块可降低大量小文件的额外开销。该参数决定基线容器的分块大小;完整保护的分块参数由插件按构建确定。
--exclude GLOB
使匹配 GLOB 的文件保留在磁盘上且不加密,可重复指定。命令行中的模式追加在配置文件的 exclude 与 keep_on_disk 之后,不会替换它们。
匹配规则:
- 模式与游戏相对路径进行匹配,路径以正斜杠分隔,例如
thirdparty/*匹配game/thirdparty/下的文件 - 使用 Python
fnmatch语义:*匹配任意字符且可以跨越目录分隔符,?匹配单个字符,[seq]匹配字符集合 - 在 Windows 上匹配不区分大小写,在 macOS 与 Linux 上区分大小写
- 建议为每个模式加引号,避免被 shell 提前展开,例如
--exclude "thirdparty/*"
运行时行为
--no-lockdown
在受保护构建中保留开发者功能。默认开启的锁定会在运行时:
- 关闭
config.developer与config.console,使开发者模式与开发者控制台不可用 - 拒绝执行可导出脚本与文本数据的 Ren'Py 命令行子命令:
compile、dialogue、translate、add_from、merge_strings
对应配置键 lockdown,发布版本应保持锁定开启。
--keep-plaintext
封包完成后不删除明文原件,也不删除被剔除的 .rpy 源码,仅用于排查问题。对应配置键 keep_plaintext。该选项作用于基线保护流程。
启用 --keep-plaintext 生成的构建不具备任何保护效果,renmori verify 也会因明文残留而报错。切勿发布此类构建。
输出控制
--no-hints
保护成功后不打印“打包并发布”指引。默认会提示:校验构建、为 itch.io 或直接下载压缩整个目录、通过 SteamPipe 上传目录内容、renmori.rmc 与 renmori.rpe 须一同分发,以及不要再次对该目录运行 Ren'Py 启动器构建。
通用选项
--lang、--no-color、--quiet 同样适用,参阅 CLI 概览。
生成的文件
运行时,Ren'Py 还会在 game/ 下生成 cache/ 目录,这是引擎的正常行为。
示例
保护一份副本,保留原始构建:
原地保护,原件备份到 ./dist/MyGame-1.0-pc_bak:
直接处理启动器生成的发行归档,结果写入 ./dist/MyGame-1.0-pc/:
在 CI 中以非交互方式执行,只输出 JSON:
排除由第三方程序直接读取的文件,并显式使用开源基线:
使用本地已有的插件并指定目标平台:
受保护目录即是发行版本身。不要再对其运行 Ren'Py 启动器的 Build Distributions,启动器会从源码工程重新构建并丢弃全部保护。