添加 player_v2.js 完整源码架构分析
从 1.8MB webpack bundle 中提取的关键信息: - Heart 运行时完整类结构 (Registry, Compiler, Interpreter, RuntimeManager) - 80+ domain functions (积木实现) 清单 - Interpreter.do_step() 执行流程详解 - BCM 加载流程 - PIXI.js 渲染层架构 - 性能瓶颈分析和 JIT 切入点 - 关键代码的 webpack 模块位置
This commit is contained in:
1 parent
cd50c395b1
commit
39dec4376d
1 file changed
+442
@@ -0,0 +1,442 @@
|
||||
# 编程猫 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 加载 |
|
||||
Reference in new issue
Block a user