# 编程猫 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 加载 |