For AI agents: the complete documentation index is available at https://renmori.mewbaka.cn/llms.txt, the full documentation bundle is available at https://renmori.mewbaka.cn/llms-full.txt, and this page is available as Markdown at https://renmori.mewbaka.cn/cli/protect.md.
  • 简体中文
  • renmori protect

    renmori protect 将一个 Ren'Py 发行版构建转换为受保护的发行版构建:game/ 下的资产与已编译脚本被封入加密容器 game/renmori.rmc,已编译脚本额外经过脚本层变换,明文原件被删除,加载器扩展 game/renmori.rpe 与构建清单 renmori-manifest.json 被写入。游戏启动时,Ren'Py 自动加载该扩展,由其在引擎内部解密并供给资源。

    语法

    renmori protect <game-dir | game.zip> [options]
    参数说明
    game-dir发行版构建的根目录,即直接包含 game/ 子目录的目录。不要传入 game/ 本身
    game.zipRen'Py 启动器生成的发行归档,例如 MyGame-1.0-pc.zip、MyGame-1.0-mac.zip

    输入

    构建目录

    输入目录必须直接包含 game/ 子目录,否则命令以退出码 2 失败。若 game/ 中已存在 renmori.rmc,说明该构建已经受保护,命令同样以退出码 2 失败,不会进行二次保护。请始终以全新的发行版构建,或保护前生成的备份作为输入。

    发行归档(.zip)

    当输入是一个有效的 .zip 文件时,protect 先将其解压,再对解压结果执行保护:

    1. 确定输出目录:指定 -o 时使用该目录,否则使用与归档同级、以归档名(不含扩展名)命名的目录,例如 MyGame-1.0-pc.zip 对应 MyGame-1.0-pc/。该目录必须不存在
    2. 在输出目录的上级目录中创建临时目录并解压。解压前会逐项检查归档成员路径,任何会解压到目标目录之外的成员(zip-slip)都会导致命令失败
    3. 定位构建根目录:解压根目录本身包含 game/ 时直接使用;否则在顶层子目录中查找唯一一个包含 game/ 的目录,这对应启动器将构建包裹在一层同名文件夹中的常见情况。找不到或找到多个时命令失败
    4. 将构建根目录移动到输出目录,随后按原地保护的方式处理该目录,并清理临时目录

    原归档始终保持不变并可作为原件,因此从归档输入时不会另外创建 _bak 备份。

    输出模式

    模式触发条件原始构建备份
    原地保护(默认)未指定 -o,输入为目录被改写为受保护构建先复制到同级目录 <dir>_bak
    原地保护,不备份未指定 -o,指定 --no-backup被改写为受保护构建不创建,操作不可撤销
    输出到新目录指定 -o OUTPUT保持不变不需要,OUTPUT 必须不存在
    从归档保护输入为 .zip归档保持不变不需要,归档即原件
    Warning

    原地保护会删除 game/ 下的明文原件。若 <dir>_bak 已存在,命令会在执行任何改动之前以退出码 2 失败;请删除旧备份、改用 -o,或在确认无需备份时使用 --no-backup。备份目录包含完整明文资源,切勿将其随发行版一同上传。

    执行流程

    命令按以下顺序执行,所有可预见的冲突都在任何文件被改动之前检查完毕。

    1. 解析输入:目录直接使用;.zip 先解压并定位构建根目录
    2. 前置检查:确认 game/ 存在且尚未受保护
    3. 合并配置:读取 renmori.toml(若存在),与命令行参数合并,参阅 配置文件
    4. 确定保护强度:未指定强度时使用完整保护,需要闭源插件;指定 baseline 时使用开源基线格式且不下载插件;指定 auto 时插件可用则使用完整保护,否则使用基线。插件的获取方式参阅 保护强度与闭源插件
    5. 准备密钥(仅基线):从 [keys] 指定的环境变量读取容器密钥与脚本密钥,未指定或变量未设置时为本次构建生成随机密钥
    6. 预检输出位置:检查 -o 目录或 <dir>_bak 是否已存在,统计待封包文件与脚本数量,没有可封包的文件时失败
    7. 打印摘要并确认:在 stderr 输出输入、模式、文件数量、脚本数量、锁定状态与密钥来源;交互式终端下请求确认 [y/N],回答 y 或 yes 以外的内容都会取消,取消时输出 {"status": "aborted"} 并以退出码 2 结束
    8. 复制或备份:-o 模式复制整个构建到输出目录;原地模式先复制到 <dir>_bak(--no-backup 时跳过)
    9. 封包与加密:将待封包文件逐个写入 game/renmori.rmc,.rpa 封包被拆开后逐项写入,已编译脚本经脚本层变换后写入
    10. 删除明文:删除已封包的原件与被剔除的 .rpy 源码,并移除因此变空的子目录(--keep-plaintext 时跳过)
    11. 写入加载器扩展:生成 game/renmori.rpe
    12. 写入构建清单:在构建根目录生成 renmori-manifest.json,并在 stdout 输出结果 JSON

    使用闭源插件时不执行第 5 步,密钥由插件自行派生;第 8 至 11 步由插件完成,构建清单仍由 renmori 写入。原地模式下 renmori 先将原始构建移至备份目录(--no-backup 时为临时目录,成功后删除),再由插件从该目录读取并写出全新的构建;插件失败时原始构建会被移回原位,-o 模式下产生的不完整输出会被删除。

    文件处理规则

    protect 遍历 game/ 下的全部文件,并按以下规则分类。下表描述基线保护流程;完整保护由插件执行封包,其文件选择同样遵循 exclude 与 keep_on_disk 的设置。

    文件处理方式
    图像、音频、视频、字体、翻译等资产封入容器,删除明文原件
    .rpyc、.rpymc 已编译脚本经脚本层变换后封入容器,删除明文原件
    .rpy、.rpym 源码,且存在同名已编译文件不封包,直接删除。引擎只执行已编译脚本,随发行版附带源码会使任何一次解密都直接获得原始剧本
    .rpy、.rpym 源码,但不存在已编译文件封入容器,确保内容不会丢失
    .rpa 封包(RPA-3.0、RPA-2.0)拆开后将其中的每个条目作为独立文件封入容器,删除封包。与磁盘上同名的散文件冲突时,先遇到的一方保留
    game/saves/整个目录跳过,存档保持原样
    game/cache/ 中的 .rpyb封入容器。这类文件是由脚本派生的已编译缓存
    game/cache/ 中的其他文件跳过,保持原样,例如 shaders.txt、build_info.json
    steam_appid.txt默认保留在磁盘上,不加密。Steam SDK 直接读取该文件,不经过 Ren'Py 加载器
    匹配 exclude 或 keep_on_disk 的文件保留在磁盘上,不加密
    符号链接及非常规文件跳过

    容器中的条目名称使用 Ren'Py 的游戏相对路径形式:正斜杠分隔,无前导斜杠,例如 images/bg room.png。运行时,容器在加载器链中处于最高优先级,因此即使磁盘上残留同名明文文件,引擎读取的也始终是容器中的版本。

    Danger

    不要通过 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。

    取值行为
    不指定(默认)完整保护:使用闭源插件声明的最强强度。插件不可用时以退出码 2 失败,不会自动退化为基线
    baseline开源基线格式:固定的容器格式与随附的占位密钥,不下载、不调用插件
    auto插件可用时使用其最强强度,否则使用基线
    插件声明的其他取值交由插件处理,插件不可用或不支持该取值时以退出码 2 失败

    两者的安全性差异参阅 保护强度与闭源插件。

    --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。该选项作用于基线保护流程。

    Danger

    启用 --keep-plaintext 生成的构建不具备任何保护效果,renmori verify 也会因明文残留而报错。切勿发布此类构建。

    输出控制

    --no-hints

    保护成功后不打印“打包并发布”指引。默认会提示:校验构建、为 itch.io 或直接下载压缩整个目录、通过 SteamPipe 上传目录内容、renmori.rmc 与 renmori.rpe 须一同分发,以及不要再次对该目录运行 Ren'Py 启动器构建。

    通用选项

    --lang、--no-color、--quiet 同样适用,参阅 CLI 概览。

    生成的文件

    路径说明
    game/renmori.rmc加密容器,包含全部被封包的资产与已变换的脚本
    game/renmori.rpe加载器扩展,Ren'Py 启动时自动加载,负责在引擎内部解密并供给资源
    renmori-manifest.json构建清单,记录工具版本、容器哈希、文件数量等信息,供 renmori verify 使用,格式参阅 输出、构建清单与退出码
    <dir>_bak/原地保护时生成的完整明文备份,不属于发行版

    运行时,Ren'Py 还会在 game/ 下生成 cache/ 目录,这是引擎的正常行为。

    示例

    保护一份副本,保留原始构建:

    renmori protect ./dist/MyGame-1.0-pc -o ./dist/MyGame-1.0-pc-protected

    原地保护,原件备份到 ./dist/MyGame-1.0-pc_bak:

    renmori protect ./dist/MyGame-1.0-pc

    直接处理启动器生成的发行归档,结果写入 ./dist/MyGame-1.0-pc/:

    renmori protect ./dist/MyGame-1.0-pc.zip

    在 CI 中以非交互方式执行,只输出 JSON:

    renmori protect ./build/MyGame-pc --no-backup --quiet -y

    排除由第三方程序直接读取的文件,并显式使用开源基线:

    renmori protect ./dist/MyGame-1.0-pc -o ./out --strength baseline --exclude "thirdparty/*"

    使用本地已有的插件并指定目标平台:

    renmori protect ./dist/MyGame-1.0-pc -o ./out --plugin ./renmori-plugin-linux-x64 --platforms win-x64,linux-x64
    Warning

    受保护目录即是发行版本身。不要再对其运行 Ren'Py 启动器的 Build Distributions,启动器会从源码工程重新构建并丢弃全部保护。

    © 2026 笨猫工作室 MewBaka Studio. All Rights Reserved.