2026-08-29 21:10:24 +08:00

77 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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/setIntervalplay.html 是独立 HTML不受此限
6. **降级优先**:服务端任何异常都返回 `ok=True, degraded=True`,绝不抛 500 —— 前端据此切独立模式。