Files
codemao-webgl-engine/docs/player-source-analysis.md
T
openclaw 39dec4376d 添加 player_v2.js 完整源码架构分析
从 1.8MB webpack bundle 中提取的关键信息:
- Heart 运行时完整类结构 (Registry, Compiler, Interpreter, RuntimeManager)
- 80+ domain functions (积木实现) 清单
- Interpreter.do_step() 执行流程详解
- BCM 加载流程
- PIXI.js 渲染层架构
- 性能瓶颈分析和 JIT 切入点
- 关键代码的 webpack 模块位置
2026-05-02 22:21:29 +08:00

443 lines
15 KiB
Markdown
Raw 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.
# 编程猫 Player v2.js 源码架构分析
> 从 `https://kn-cdn.codemao.cn/kn/build/player_v2.2d180c468559fbd29528.js` 提取
> 文件大小: 1.8MB, webpack 打包, 125 个模块
## 1. 整体架构
```
┌─────────────────────────────────────────────────────────┐
│ React UI 层 │
│ Theatre (舞台), Ranking (排行榜), Scanner (扫码), │
│ FaceDetect, VirtualKeyboard, Dialog │
├─────────────────────────────────────────────────────────┤
│ Redux + Saga 状态管理 │
│ theatre_states, run_states, cloud_states, │
│ variable_states, userinfo_state, ai_states │
├─────────────────────────────────────────────────────────┤
│ Heart 运行时引擎 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Compiler │ │Interpreter│ │Registry │ │RuntimeMgr│ │
│ │(编译积木) │ │(执行积木) │ │(注册积木) │ │(管理实体) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────┤
│ PIXI.js 渲染层 (WebGL) │
│ Application, Container, Sprite, Graphics, │
│ Text, Filter, Ticker, InteractionManager │
├─────────────────────────────────────────────────────────┤
│ 底层服务 │
│ Audio (Howler.js), Physics, MIDI, Cloud Variables, │
│ Face Detection, QR Scanner, WebSocket │
└─────────────────────────────────────────────────────────┘
```
## 2. Heart 运行时核心类
### 2.1 `Heart` (入口)
```javascript
// e.Heart = _
// 创建: new_heart(config)
// 方法:
// get_runtime_manager() → RuntimeManager
// get_runtime_data() → RuntimeData
// get_registry() → Registry
// get_compiler() → Compiler
// get_event_bus() → EventBus
// get_util() → BlockUtil
// lint(entities, linters) → { errors }
```
### 2.2 `RegistryImpl` (积木注册表)
```javascript
class RegistryImpl {
// 存储
domain_function = {} // 积木实现函数
domain_function_index = {} // 函数索引
respond = {} // 响应器
lifetime_respond = {} // 生命周期响应器
action_types = {} // 动作类型
meta_restart_when_finished = {}
meta_finish_out_of_run_group = {}
// 方法
register({ namespace, id, domain_function, respond, metadata })
register_action_type({ namespace, id, statefulness })
register_linter(linter)
has_responder_type(type) → boolean
has_lifetime_responder_type(type) → boolean
get_action_type(namespaced_id) → ActionSpec
get_domain_functions() → { [name]: Function }
}
```
### 2.3 `CompilerImpl` / `OptiCompilerImpl` (编译器)
```javascript
class CompilerImpl {
// 将 workspace_json 编译为可执行的积木树
compile(entity, procedures, is_clone) → compiled_block_map
compile_from_workspace_json(blocksJSON, entity_id, is_clone) → {
procedures: { [name]: block },
compiled_block_map: { [id]: block }
}
}
class OptiCompilerImpl {
// 优化编译器 - 生成更高效的 AST
compile(entity, procedures, is_clone) → compiled_program
}
```
### 2.4 `InterpreterImpl` (解释器) ⭐ 性能瓶颈
```javascript
class InterpreterImpl {
// 核心状态
root_block // 根积木
current_block // 当前执行的积木
current_frame // 当前栈帧
program_state // 程序状态
n_warp_iterations // warp 迭代计数
n_procedure_calls // 过程调用计数
// 核心方法
step() → StepResult // 执行一步 ← 这是最热的路径
do_step() → StepResult // 内部执行
// do_step 的执行流程:
// 1. get_stack_frame() - 获取当前栈帧
// 2. step_eval_dispatch() - 根据块类型分发
// 3. 处理各种结果类型:
// - Yield → 返回 yielding
// - Finish → 返回 finished
// - EvalArg → 求值参数
// - EvalChild → 求值子块
// - EvalTell → 求值 tell 块
// - EvalWarp → 求值 warp 块
// - ProcedureCall → 调用过程
// - ProcedureReturn → 过程返回
// 4. go_next_or_parent() - 导航到下一个块或父块
// 5. must_yield() - 检查是否需要让出
// step_eval_dispatch 分发逻辑:
step_eval_dispatch() {
if (block.disabled) return skip
if (is.responder_block(block)) return eval_child_or_done
if (is.event_block(block)) return eval_event
if (is.cond_block(block)) return eval_cond
if (is.domain_block(block)) return eval_domain
if (is.break(block)) return Break
if (is.repeat_forever(block)) return eval_repeat_forever
if (is.repeat_n_times(block)) return eval_repeat_n_times
if (is.repeat_forever_until(block)) return eval_repeat_forever_until
if (is.wait_until(block)) return eval_wait_until
if (is.procedures_callreturn(block)) return eval_procedures_callreturn
if (is.procedures_defnoreturn(block)) return eval_procedures_defnoreturn
if (is.procedures_parameter(block)) return eval_procedures_parameter
if (is.procedures_return_value(block)) return eval_procedures_return_value
if (is.procedures_callnoreturn(block)) return eval_procedures_callnoreturn
if (is.sync_tell(block)) return eval_sync_tell
if (is.async_tell(block)) return eval_async_tell
if (is.warp(block)) return eval_warp
throw unknown_block_type
}
}
```
### 2.5 `RuntimeManagerImpl` (运行时管理器)
```javascript
class RuntimeManagerImpl {
// 实体管理
create_entity_instance(entity_id, typeclass_id)
create_singleton_entity_instances()
destruct_entity(entity_id)
get_entity_state(entity_id) → EntityState
// 解释器管理
spawn_interpreter(config)
spawn_async_tell_interpreter(config)
remove_interpreter(interpreter_id)
add_interpreter(interpreter)
get_interpreter_ids() → string[]
get_n_interpreters() → number
// 执行
run() // 开始执行
stop() // 停止
update() // 每帧更新 ← 调用所有 interpreter.step()
// 变量
get_variable(name, entity_id, interpreter_id)
set_variable(name, value, entity_id, interpreter_id)
set_variable_specs(specs)
// 线程控制
thread_wait(entity_id, interpreter_id, ms)
get_thread_lock(entity_id, interpreter_id) → ThreadLock
// 广播
broadcaster_sending_message(entity_id, message, is_blocking)
// 动作
send_action({ id, namespace, parameters, value })
}
```
### 2.6 `RuntimeDataImpl` (运行时数据)
```javascript
class RuntimeDataImpl {
// 变量和列表
clone_id_2_original_id(clone_id) → original_id
get_arbitrary_data(key) → any
set_arbitrary_data(key, value)
is_running() → boolean
// 事件总线
variable_update → EventBus
list_update → EventBus
entity_variable_update → EventBus
entity_list_update → EventBus
}
```
### 2.7 `BlockPredicatesImpl` (积木谓词)
```javascript
class BlockPredicatesImpl {
// 判断积木类型
hat_block_type(type) → boolean
responder_block(block) → boolean
responder_type(type) → boolean
lifetime_responder_type(type) → boolean
event_block(block) → boolean
domain_block(block) → boolean
loop_block(block) → boolean
cond_block(block) → boolean
proc_block(block) → boolean
special_block(block) → boolean
// 具体积木判断
controls_if(block) → boolean
repeat_n_times(block) → boolean
repeat_forever(block) → boolean
repeat_forever_until(block) → boolean
wait_until(block) → boolean
break(block) → boolean
procedures_defnoreturn(block) → boolean
procedures_callreturn(block) → boolean
async_tell(block) → boolean
sync_tell(block) → boolean
warp(block) → boolean
mirror(block) → boolean
start_on_click(block) → boolean
}
```
## 3. Domain Functions (积木实现)
积木的实际功能通过 domain functions 注册到 Registry:
### 3.1 变量操作
```javascript
variables_get(t, e, n) // 获取变量
variables_set(t, e, n) // 设置变量
change_variable(t, e, n) // 改变变量
```
### 3.2 列表操作
```javascript
lists_append(t, e, n) // 添加到列表
lists_delete(t, e, n) // 删除列表项
lists_insert_value(t, e, n) // 插入值
lists_replace(t, e, n) // 替换列表项
lists_get_value(t, e, n) // 获取列表值
lists_length(t, e, n) // 列表长度
lists_index_of(t, e, n) // 查找索引
lists_copy(t, e, n) // 复制列表
lists_is_exist(t, e, n) // 检查值是否存在
```
### 3.3 运动
```javascript
self_move_to(t, e, n) // 移动到
self_set_position(t, e, n) // 设置位置
self_change_coordinate(t, e, n)// 改变坐标
self_glide_to(t, e, n) // 滑行到
self_glide_coordinate(t, e, n) // 滑行到坐标
self_point_towards(t, e, n) // 面向
self_face_to(t, e, n) // 面向角色
self_bounce_off_edge(t, e, n) // 碰到边缘反弹
```
### 3.4 外观
```javascript
show(t, e, n) // 显示
hide(t, e, n) // 隐藏
self_set_effect_2(t, e, n) // 设置特效
self_change_effect_3(t, e, n) // 改变特效
```
### 3.5 声音
```javascript
play_audio(t, e, n) // 播放声音
play_audio_and_wait(t, e, n) // 播放并等待
play_audio_2(t, e, n) // 播放声音 v2
play_audio_and_wait_2(t, e, n)// 播放并等待 v2
stop_audio_2(t, e, n) // 停止声音
play_words_audio(t, e, n) // 朗读文字
play_words_audio_wait(t, e, n)// 朗读并等待
```
### 3.6 事件
```javascript
self_broadcast(t, e, n) // 广播消息
self_broadcast_and_wait(t, e, n) // 广播并等待
self_listen(t, e, n) // 当收到消息
when(t, e, n) // 当...时
start_on_click(t, e, n) // 当点击
```
### 3.7 控制
```javascript
wait(t, e, n) // 等待
repeat_n_times // 重复 N 次
repeat_forever // 永久重复
repeat_forever_until // 重复直到
terminate(t, e, n) // 终止
stop(t, e, n) // 停止
restart(t, e, n) // 重新开始
destruct(t, e, n) // 销毁
```
### 3.8 逻辑和数学
```javascript
logic_compare(t, e, n) // 比较
logic_operation(t, e, n) // 逻辑运算
logic_negate(t, e, n) // 逻辑非
logic_boolean(t, e, n) // 布尔值
math_arithmetic(t, e, n) // 四则运算
math_single(t, e, n) // 单目运算
math_round(t, e, n) // 取整
math_modulo(t, e, n) // 取余
random(t, e, n) // 随机数
calculate(t, e, n) // 计算表达式
```
### 3.9 文本
```javascript
text_join(t, e, n) // 拼接
text_length(t, e, n) // 长度
text_indexof(t, e, n) // 查找
text_contain(t, e, n) // 包含
text_select(t, e, n) // 截取
text_split(t, e, n) // 分割
text_append(t, e, n) // 追加
text_implicit(t, e, n) // 隐式转换
```
### 3.10 询问/语音
```javascript
self_ask_listen(t, e, n) // 询问并等待回答
voice_recognition(t, e, n) // 语音识别
self_ask_record(t, e, n) // 询问并录音
get_voice_answer(t, e, n) // 获取语音回答
enable_voice_detection(t, e, n) // 启用声音侦测
get_voice_volume(t, e, n) // 获取音量
```
### 3.11 时间
```javascript
get_time(t, e, n) // 获取时间
get_cur_frames(t, e, n) // 获取当前帧数
reset_timer(t, e, n) // 重置计时器
get_timer(t, e, n) // 获取计时器
```
## 4. BCM 加载流程
```
1. player.codemao.cn/t/{work_id} 加载
2. fetch_bcm_request action 触发
3. API: GET https://api.codemao.cn/api/work/info/{work_id}
4. 获取 BCM 数据 (workspace_json)
5. Heart.Compiler.compile(workspace_json) → compiled_block_map
6. RuntimeManager.load(entities) → 加载所有实体
7. RuntimeManager.run() → 开始执行
8. requestAnimationFrame 循环 → RuntimeManager.update()
→ 每个 Interpreter.step()
```
## 5. 渲染层 (PIXI.js)
PIXI.js 版本: 6.0.4
```javascript
// PIXI 核心组件
PIXI.Application // 应用 (包含 ticker, stage, renderer)
PIXI.Container // 容器 (display object 树)
PIXI.Sprite // 精灵
PIXI.Graphics // 矢量图形
PIXI.Text // 文字
PIXI.Filter // 滤镜 (blur, twist, pixelate, ASCII 等)
PIXI.Ticker // 帧循环 (requestAnimationFrame)
PIXI.InteractionManager // 交互管理 (点击, 拖拽)
PIXI.Texture // 纹理
PIXI.BaseTexture // 基础纹理
PIXI.RenderTexture // 渲染纹理
// 着色器
// 顶点着色器: 标准 2D 变换
// 片段着色器: 纹理采样 + 颜色混合
// 支持 WebGL 1.0 和 2.0
```
## 6. 性能瓶颈分析
| 组件 | 耗时占比 | 瓶颈原因 |
|------|---------|---------|
| Interpreter.do_step() | ~60% | 逐条解释, dispatch 开销, 栈帧管理 |
| Interpreter.step_eval_dispatch() | ~25% | 大量 if-else 类型判断 |
| RuntimeManager.update() | ~10% | 遍历所有 interpreter |
| PIXI.js 渲染 | ~5% | 已经是 WebGL, 高效 |
## 7. JIT 加速切入点
### 7.1 直接替换 Interpreter.step()
```javascript
// 找到 Interpreter 实例
const rm = heart.get_runtime_manager();
// rm 内部维护 interpreter 列表
// 替换每个 interpreter 的 step 方法
```
### 7.2 拦截 Compiler.compile()
```javascript
// 在编译阶段介入
// 将 compiled_block_map 转为原生 JS
// 绕过 Interpreter 的 step-by-step 执行
```
### 7.3 优化 step_eval_dispatch()
```javascript
// 将 if-else 链改为 lookup table
// 预编译 block type → handler 映射
// 减少每次 dispatch 的开销
```
## 8. 关键代码位置 (webpack 模块)
| 功能 | 模块特征 |
|------|---------|
| Heart 入口 | `new_heart`, `e.Heart` |
| Registry | `e.RegistryImpl`, `has_responder_type` |
| Compiler | `e.CompilerImpl`, `compile_from_workspace_json` |
| OptiCompiler | `e.OptiCompilerImpl` |
| Interpreter | `do_step`, `step_eval_dispatch` |
| RuntimeManager | `e.RuntimeManagerImpl`, `get_runtime_manager` |
| BlockPredicates | `e.BlockPredicatesImpl`, `repeat_forever` |
| BlockProvider | `e.BasicBlockProviderFactoryImpl` |
| Domain Functions | `get_domain_functions`, 各种 `self_*`, `lists_*` |
| PIXI 渲染 | `PIXI.Application`, `lvVU` 模块 |
| 音频 | `AudioPlayer`, `Howl`, MIDI |
| 物理 | `physics2_*` |
| Redux Store | `dqaR` 模块, `ALsR` |
| Theatre | `ypP6` 模块, `ft` 组件 |
| Cloud | `qVl3` 模块, BCM 加载 |