剧情写成 JSON,
引擎专注执行。一行代码完成挂载

RNE 是一个跑在浏览器里的剧情运行时:剧情就是 JSON 文件, 指令、条件、UI 这些能力由 JS 或 Rust 插件在运行时注册。 内核 255KB(gzip 后 50KB),没有运行时依赖。

v1.5.0Apache-2.0TypeScript + RustJS / WASM 插件

439 个测试用例守护 · 手写安全表达式解析器 · 绝不使用 eval

适用:视觉小说·文字冒险·互动教学·世界模拟·NPC 跨周目记忆


  "start" "s1"
  "nodes"
    "s1" "type" "say" "speaker" "旁白"
            "text" "铁门被推开,灰尘在灯影里浮沉。" "next" "s2" 
    "s2" "type" "say" "speaker" "铁颚"
            "text" "外头雨大。炉火还温着——进来,或者继续走你的。" "next" "s3" 
    "s3" "type" "choice" "options"
       "text" "推门,借一夜炉火" "goto" "s4" 
       "text" "道谢,走进雨里" "goto" "s_end" 
    
  
Terminal
439测试用例全绿
50KBgzip 后库体积
0运行时依赖
01 · FEATURES

引擎与剧情解耦

剧情是纯 JSON 数据,替换文件即替换内容;玩法由插件在运行时注册。两侧独立演进,互不牵制。

核心引擎 CORE ENGINE

  1. 安全表达式 DSL

    手写词法 + 递归下降语法分析,绝不使用 eval / new Function。标识符规则支持 Unicode —— var.金币 >= 10 可以直接写。

  2. 存档全家桶

    localStorage 多槽位 + 节点级自动存档;带信封的存档文件可导出 / 导入;逐级版本迁移、老档可读、拿错文件明确报错,多端冲突能比较出「分叉」。

  3. 世界模拟器

    WorldLoop 以固定节拍广播 world:tick;剧情写一个 observe 节点就能被动听世界说话 —— 世界持续运转,剧情永不结束。

  4. 实时帧循环

    固定步长逻辑(帧率无关)+ 渲染帧插值,没有订阅就不烧帧。性能看门狗实测帧耗时、自动降档带防抖,?perf=1 挂只读浮层。

扩展能力 EXTENSIBILITY

  1. 双形态插件体系

    JS 插件(ES Module 动态 import())与 Rust WASM 插件(裸 C ABI,不用 wasm-bindgen)统一管理,注册命令、条件、效果、自定义节点与 UI 组件。

  2. 21 项权限 + 运行期审计

    manifest 声明权限,宿主对每一次能力调用做校验;支持作用域限定(如 state:write:var.arena)。每次尝试都留痕 —— 拒绝也记。

  3. 确定性与回放

    种子化随机(同种子必然复现)、稳定运行指纹、无头回放 CLI —— 在另一台宿主跑同一条命令比对指纹,跨宿主一致性可被验证。

  4. 错误隔离与熔断

    单个插件崩溃 / 超时 / 连续失败自动熔断,都拖不垮主引擎与其它插件;切剧情后的「漏网定时器」能被 leakReport() 点名。

工具与生态 TOOLING & ECOSYSTEM

  1. 给编剧的剧本编辑器

    表单 / 文本 / JSON 三种模式;结构体检(断头路、无结局、条件写错)、一键试读、撤销重做、可打印剧本 HTML 导出,产物一定过引擎校验。

  2. 库形态 + iframe 嵌入

    import { Runtime } from 'plugin-story-engine' 直接用(克隆即用、免构建);或 iframe + postMessage 桥,父页不 import 引擎也能驱动剧情。

  3. 认领式输入 + 触屏

    插件「认领」键盘与指针,引擎独占捕获层仲裁(优先级 + 作用域);触屏控件把手指翻译成相同键码,插件零改动支持移动端。

  4. 可观测性

    状态来源追踪(explain('var.金币') → 谁写的、哪一步)、每插件开销归因、诊断包一键导出 —— 玩家说卡住了,让他导出,你就能远程复现。

02 · ARCHITECTURE

三层架构,职责清晰

引擎按 type 分派节点处理器;插件拿不到引擎内部对象,只能通过经过权限校验的 ctx 说话。

宿主层 · 你的界面引擎不认识任何具体 UI
自己实现 RuntimeUi mount() 一行挂载(可选默认 UI) iframe 父页(只认协议) 剧本编辑器
Runtime 内核 · 53 个公开导出src/index.ts 是唯一契约面
StoryEngine StateStore PluginManager EventBus WorldLoop FrameLoop InputRouter AssetStore RandomSource SaveFile PerfWatchdog ContractRegistry
内容层 · 纯数据 + 双形态插件换文件即换内容
剧情 JSON(11 种节点 + 条件表达式) JS 插件(ES Module) Rust WASM 插件(wasm32 · 裸 C ABI) 文本剧本(编辑器编译为 JSON)

静态权限清单是「许诺」,运行期 AuditLog 是「证据」—— 插件拿不到引擎内部对象,每一次能力调用都被宿主校验。

03 · NODE TYPES

十一种内置节点

内置节点不可被插件覆盖;插件还能注册自己的节点类型,与内置节点平权分派。

  • say显示一句对白,等待玩家继续;支持 speaker、auto、delayMs
  • choice显示选项等玩家选;选项可带条件与效果,支持限时
  • jump显式跳转到指定节点
  • set写入状态:var / flag / item / actor 四个命名空间
  • if条件分支,条件即安全表达式(认中文标识符)
  • wait等待若干毫秒;可标记为可跳过
  • emit广播事件给所有监听者(插件 / 宿主)
  • plugin调用插件命令,结果可渲染、可落地效果
  • ui挂载插件注册的 UI 组件,可选等待玩家确认
  • observe被动听世界事件,事件到达时跳转 —— 世界模拟器的核心
  • end结束剧情,带一句结束摘要
  • ……你的插件用 ctx.nodes.register() 注册自定义节点类型
条件就是表达式: item.gold >= 10 && flag.门开了 || var.单价 * var.数量 < 100  // 手写解析器,无 eval,标识符认中文
04 · QUICK START

五分钟跑起来

Node.js ≥ 18、npm ≥ 9 即可;只有构建 WASM 插件才需要 Rust。

Install npm install git+https://gitcode.com/rincjiyu/rne.git#main

从源码跑起来

# 克隆仓库(含构建好的库产物,离线也能用)
git clone https://gitcode.com/rincjiyu/rne.git
cd rne && npm install

npm run dev        # http://localhost:5173,开箱即玩
npm test           # 439 个用例全绿
npm run create-plugin -- com.example.demo
                   # 生成能跑的「插件+剧情+测试」四件套
  • 五个页面入口:引擎演示、剧本编辑器、嵌入演示、展示页…
  • 推送前自动跑类型检查 + 全量测试(pre-push 钩子)

当库引入(免构建)

# dist-lib 随仓库提交:克隆即用,无需构建、离线也行
npm install git+https://gitcode.com/rincjiyu/rne.git#main

# 或打成 tarball 离线分发
npm run prepack && npm pack   # → plugin-story-engine-1.5.0.tgz

// ── 然后 ──────────────────────────────
import { mount } from 'plugin-story-engine'

const app = mount('#app', { story: '/stories/demo.json' })
  • ESM 产物 145 KB(gzip 约 50 KB)+ 43 份类型声明
  • 构建产物已在 node 里实测跑通整条剧情链路
05 · FOR WRITERS

无需编程,
也能创作剧情

编辑器把引擎概念翻译成编剧的词汇:场、对白、选择、条件分支、跳转、结局。写坏了也不会丢东西 —— 解析不通过只报错(带行号),不动你写的内容。

  • 一
    结构体检当场点名

    断头路、没人进的场、一个结局都没有、选项点了莫名结束、条件写错 —— 顶栏一行直接报出来。

  • 二
    一键试读

    复用引擎本身试读全剧,还能「从这一场开始」;改编完马上能看到玩家眼中的样子。

  • 三
    两种交付稿

    可打印剧本 HTML(A4、人物名居中、对白缩进 → Ctrl+P 存 PDF 给导演)与增强版 Markdown(进仓库、能 diff)。

  • 四
    三种模式看同一份剧情

    表单 / 文本 / JSON 能看能改引擎真正加载的那份;含插件的剧情往返保真,工程节点原样保留。

剧本编辑器 · 文本模式Ctrl+S 存草稿
锈蚀带
作者:编剧甲
第一幕
门厅(内景 · 锈蚀的门厅 · 夜)
铁门被推开,灰尘在灯影里浮沉。
铁颚(低声):雨大……炉火还温着,进来吧。
旁白:风从缝里灌进来。
选择:怎么办?
拿钥匙 -> 里间
空手进去 (如果 flag.brave == true)-> 里间
就此离开
结局:好结局
你活着出来了。
06 · SHOWCASE

示例故事

内置示例剧情风格完全不同 —— 它们的作用不是好看,而是证明引擎抽象。全部在 node 里被确定性跑通。

文学向

未寄出的信

纯对白与分歧:三封信、四个结局,只用内置节点。文本插值 {{ var.readCount }} 直接写在台词里。

涌现向

潮池

剧情不主动推进 —— 插件在 world:tick 上演化生态,潮池自己长起来或塌掉,剧情用 observe 被动接住。

实时向

轨道

固定步长物理 + 输入认领 + 受管画布。引擎侧复杂(物理/帧/输入/画布),玩家侧只有两个键。

叙事插件

归途

结束时告诉你「你没走的路」与「因为你」;NPC 跨周目记得你。两个插件都不需要引擎改任何东西。

A/B 变体

两种开场

同一场两个版本按玩家分流(一个玩家只分一次、可复现)—— 用来测「哪一版读者走得更远」。

世界模拟

世界模拟器演示

WASM 模拟物种、JS 弹警示、JSON observe 转向 —— 三种形态的插件在同一个世界里平权协作。

09 · FAQ

现状与限制

先说清楚这个引擎不擅长什么、边界在哪里 —— 合脚,再上路。

  • 没有可视化节点编辑器 —— 剧情目前就是手写 JSON,工作台的编辑器只做格式校验和试读,不拖拽。
  • 节点图只支持内置示例 —— 还不能渲染任意剧情的分支结构。
  • WASM 插件文档比 JS 侧薄 —— 构建工具链要自己搭,示例少。
  • 移动端能玩,但没做专属交互 —— 没有虚拟按键,触屏只是能用。
  • 存档格式还在演进 —— 大版本升级可能要求跑一次迁移。

几个高频问题

接入前被问得最多的几个;更细的排错(错误码、加载失败、observe 不触发)在文档 FAQ。

一定要写插件才能用吗?
不。引擎可以完全脱离插件运行:minimal.json 只用内置节点(say / choice / jump / set / if / wait / emit / plugin / ui / observe / end)。什么时候想要新玩法,什么时候再装插件。
表达式安全吗?支持中文吗?
条件表达式是手写的词法 + 递归下降解析器,绝不使用 eval / new Function。标识符规则允许 Unicode 字母,所以 var.单价 * var.数量、flag.门开了 都能直接写 —— 这是面向中文的引擎应有的样子。
存档会不会丢?
存档在 localStorage 多槽位 + 节点级自动存档。但浏览器会清本地存储(Safari ITP、无痕模式、手动清数据),客户端拦不住 —— 所以引擎提供带信封的存档文件「导出 / 导入」:老版本文件能走迁移器读回,拿错文件会明确报错而不是「导入成功但进度乱了」。
为什么 WASM 插件只支持 Rust?
Rust 编译到 wasm32-unknown-unknown,直接走 C ABI(alloc / dealloc),不使用 wasm-bindgen —— 依赖面更小、ABI 更可测(UTF-8 JSON 跨边界通信)。精确约定见 docs/ABI.md,宿主能力(wasm / simd / 线程)可用 ctx.capabilities 显式探测降级。
怎么接入我自己的 UI / 项目?
三种方式:① mount() 一行挂载用默认界面;② 实现 RuntimeUi 接口 + new Runtime({ ui })(见 docs/FRONTEND.md);③ iframe + postMessage 嵌入,父页完全不 import 引擎。安装走 git 直装或 tarball:npm install git+https://gitcode.com/rincjiyu/rne.git#main。
能部署到哪里?要注意什么?
任意静态托管都可以(Cloudflare Pages / Vercel / GitHub Pages 等)。启用多线程 WASM 或跨域隔离(COOP/COEP)时有静默代价,部署前建议读一遍 docs/CROSS_ORIGIN.md。
COMMUNITY

开源,且活得认真

代码、剧本、测试、文档全部公开在 GitCode;问题与建议走 Issues,48 小时内必有回应。

Apache-2.0开源协议,商用无顾虑
439测试用例全绿,回归自动化
11种内置节点,覆盖叙事全需求
0运行时依赖,克隆即用
觉得不错的话,去点个 Star 呗

接下来,写你自己的剧情。

Apache-2.0 开源。未涵盖的问题,欢迎在仓库提交 issue。