77 lines
4.7 KiB
Markdown
77 lines
4.7 KiB
Markdown
---
|
||
name: runtime
|
||
description: 元景 W-10 运行时执行引擎模块(前端 JS 引擎 + 可选服务端辅助)。世界运行时初始化、实体状态、脚本执行调度、事件分发、定时器、碰撞检测、暂停恢复、错误降级、独立世界本地引擎。开发/修改 runtime 相关功能前必读。
|
||
---
|
||
|
||
# runtime 模块(W-10 运行时执行引擎)
|
||
|
||
## 架构
|
||
|
||
```
|
||
wwwroot/runtime.js 前端核心引擎(RuntimeEngine 类,事件循环 tick 驱动)
|
||
wwwroot/play.html 游戏运行页(画布 + 日志 + 控件,验收入口)
|
||
wwwroot/index.ui 模块入口页(bricks)
|
||
wwwroot/api/*.dspy 可选服务端辅助(load_world / runtime_status / runtime_event / runtime_control)
|
||
runtime/runtime_service.py 服务端辅助实现(ServerEnv 注册,无自有数据表,只读复用 world 模块)
|
||
scripts/load_path.py RBAC 路径注册(any/logined 分层,禁通配符)
|
||
```
|
||
|
||
- 运行时引擎核心在**前端 JS**(浏览器本地执行,事件循环 + 实体状态 + 脚本执行)。
|
||
- 服务端辅助是**可选**的:`/runtime/api/load_world.dspy` 从 world 模块读取世界与实体;
|
||
后端不可用/查无数据时前端自动降级**独立模式**(W-10i),本地构建演示世界继续运行。
|
||
- 本模块为交互层模块,**无自有数据表**(无 models/、json/、init/)。
|
||
|
||
## 功能点映射(W-10a ~ W-10i)
|
||
|
||
| 功能点 | 实现位置 | 验收口径 |
|
||
|--------|----------|----------|
|
||
| W-10a 世界运行时初始化(构建运行态实体树) | runtime.js `initWorld/buildTree` + api/load_world.dspy | 开始玩即初始化实体树,实体可点击 |
|
||
| W-10b 实体运行时状态管理 | `makeEntity` / state 机(idle/active/done/failed) | 点击后实体 state 变化并绘制 |
|
||
| W-10c 脚本运行时执行调度 | `executeScript/executeAction`(与 script_engine 编译产物 actions 对接) | 点击触发脚本动作执行 |
|
||
| W-10d 事件分发(路由到对应脚本) | `dispatchEvent/processEvents/_routeEvent` | click/timer/collide 事件路由到实体绑定脚本 |
|
||
| W-10e 定时器管理 | `addTimer/processTimers`(tick 驱动,非 setInterval) | 定时器按间隔触发脚本 |
|
||
| W-10f 碰撞检测 | `checkCollisions`(AABB 两两检测) | 移动后实体碰撞触发 onCollide |
|
||
| W-10g 运行时暂停/恢复 | `pause/resume/stop` + api/runtime_control.dspy | 暂停后 tick 挂起,恢复继续 |
|
||
| W-10h 错误处理与降级 | `handleError`(try/catch 捕获,单脚本失败不整页崩溃) | 故意抛错脚本 → 日志记录错误,引擎继续运行 |
|
||
| W-10i 独立世界本地引擎 | `_localWorldEntities` + initWorld 降级分支 | 后端不可用仍可本地构建世界运行 |
|
||
|
||
## 脚本对接契约(script_engine 编译产物格式)
|
||
|
||
```json
|
||
{ "id": "script_click", "name": "点击响应",
|
||
"actions": [
|
||
{"op": "log", "message": "..."},
|
||
{"op": "set_state", "state": "active"},
|
||
{"op": "move", "dx": 12, "dy": 0},
|
||
{"op": "emit", "event": "active_changed", "entity_id": "hero", "payload": {}},
|
||
{"op": "delay", "ms": 1000},
|
||
{"op": "fail", "message": "故意抛错"}
|
||
]}
|
||
```
|
||
|
||
实体绑定脚本:`props.scripts = ["script_click", ...]`;碰撞回调:`props.onCollide = "script_id"`。
|
||
|
||
## DSPY 端点
|
||
|
||
- `GET /runtime/api/load_world.dspy?world_id=xxx` → `{ok, degraded, mode, world, entities}`
|
||
- `GET /runtime/api/runtime_status.dspy?runtime_id=xxx` → `{runtime_id, running, paused, mode, entities, events, errors}`
|
||
- `POST /runtime/api/runtime_event.dspy`(event_type/entity_id/payload)→ `{ok, queued}`
|
||
- `POST /runtime/api/runtime_control.dspy`(action=start/pause/resume)→ `{ok, action, running, paused}`
|
||
|
||
## RBAC 路径(scripts/load_path.py 已注册)
|
||
|
||
- `any`:`/runtime/runtime.js`
|
||
- `logined`:`/runtime`、`/runtime/index.ui`、`/runtime/play.html`、`/runtime/api/*.dspy`(4 个显式)
|
||
|
||
## 陷阱
|
||
|
||
1. **dspy 禁项**:dspy 内禁 import / f-string / print / uuid —— 全部用 ServerEnv 注入全局
|
||
(`load_world_runtime` 等),`return` 显式返回。
|
||
2. **三处同步注册**:新增服务端函数必须同步 ① runtime_service.py ② runtime/__init__.py
|
||
③ runtime/init.py `env.xxx = xxx`,漏一处 → NameError。
|
||
3. **取库名禁硬编码**:服务端辅助用 `ServerEnv().get_module_dbname('world')`,禁止写死 DBNAME。
|
||
4. **DictObject 不可 dict()**:读 world 记录用 `_row_to_dict` 逐字段 getattr。
|
||
5. **前端引擎不依赖 setInterval**:定时器/帧循环用 `setTimeout` 自调度 + tick 驱动,
|
||
bricks script actiontype 禁止 fetch/setInterval(play.html 是独立 HTML,不受此限)。
|
||
6. **降级优先**:服务端任何异常都返回 `ok=True, degraded=True`,绝不抛 500 —— 前端据此切独立模式。
|