# cc383script2d-host **Repository Path**: moho721/cc383script2d-host ## Basic Information - **Project Name**: cc383script2d-host - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-10 - **Last Updated**: 2026-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # cc383script2d-host > **cc383script2d 的验证宿主(Verification Host)** > > 一个独立的轻量 "Cocos Creator 3.8.3 运行时模拟" 项目,用于**在不安装 Cocos Creator 编辑器的情况下**,在浏览器里把 `cc383script2d` 纯脚本基座跑起来、可视化试玩,从而验证该脚本项目的可行性、加速开发进度。 > > **铁律**:本仓库是唯一可写的目录。**严禁改动 `cc383script2d`(及本仓库以外任何文件)**。发现游戏项目的问题 → 只报告,不代改,由项目方自行解决。 --- ## 1. 背景 - **被测项目**:`cc383script2d`([Gitee: moho721/cc383script2d](https://gitee.com/moho721/cc383script2d))—— Cocos Creator 3.8.3 的 2D 生存/沙盒"工厂型"脚本基座。15 个 TS 文件、约 2400 行,纯脚本层:无场景、无预制体、无资源、无 `.meta`。 - **痛点**:作者出差中,无法安装/使用 Cocos Creator 编辑器,官方验证路径(导入编辑器 → 建场景 → 运行)走不通,但项目进度不能停。 - **目标**:用一个"验证宿主"在浏览器里把脚本跑起来并**可视化试玩**,证明这套脚本架构(生成/移动/索敌/攻击/掉落/拾取/背包/丢弃/热键/死亡复活)能跑通,从而推进 Phase 1 → Phase 2。 ## 2. 需求 | # | 需求 | 说明 | |---|------|------| | R1 | 不依赖 Cocos Creator | 纯浏览器运行,无需编辑器、无需安装 Cocos 引擎包 | | R2 | 只改宿主 | 对 `cc383script2d` **只读引用、零写入**。仓库内以 git submodule 指向 `vendor/` 作为可复现基线;**当前构建源 = submodule 云基线 `vendor/cc383script2d/_BaseClassScript`(`185fc04`,项目方已推送全部修复,见 §3)** | | R3 | 可视化试玩 | Canvas 渲染实体(色块+名字+血条),键盘完整操作,可玩 | | R4 | 日志可观测 | 面板显示 EventBus 事件流,便于核对脚本行为是否符合设计 | | R5 | 国内可访问 | 部署在腾讯云 80 端口,`http://43.139.172.126` 直接打开 | | R6 | 云端同步 | 宿主独立仓库,云上可复现;构建源通过 `GAME_SRC` 环境变量切换(submodule 基线 / 本地工作树) | ## 3. 铁律与协作约定 1. **本仓库以外一律只读**:`cc383script2d`、工作区其它目录,均不得修改/新增/删除任何文件。 2. **问题只报告**:宿主在运行中发现的 cc383script2d 缺陷、行为异常、与 Cocos 语义冲突,统一记录到本仓库 `docs/verification-report.md`(或 README §7),由作者修复。 3. **构建源与 submodule 基线**:云端基线已升级到 `origin/master` = `4420453`(含 P1-1/2/3、P2-1、P3-1、Phase 4.1 联机核心、P4-1/2/3、P4-4、**P5-1 采集物无限掉落修复**、**`extensions/runtime-assets` 插件**与 **`release/` 发布内容层**——发布内容/保密规则/产品标识在工厂仓库,见 §12)。`vendor/cc383script2d` submodule 已升级到 `4420453`;宿主默认 `GAME_SRC=vendor/cc383script2d/_BaseClassScript`(**云基线**,可复现、纯只读,默认 `npm run build`/`npm run test`/`npm run publish` 全绿)。若临时复验本地未推送改动,用 `GAME_SRC=../cc383script2d/_BaseClassScript npm run build` 覆盖。 ## 4. 验证范围与边界(诚实声明) - **验证的是「脚本行为逻辑」**:移动、多方向帧切换(四/八方向,`directionCount` 可配)、有限状态机(Idle/Patrol/Chase/Attack)、伤害计算、掉落表概率、背包/堆叠/热键、死亡复活、EventBus 事件流、**联机协议往返(Phase 4.2 服务器中继:JOIN/WELCOME、STATE/SNAPSHOT、EVENT/EVENT2C、LEAVE/LEAVED)**。 - **不验证「引擎渲染效果」**:shim 默认用色块/文字代替真实 Sprite 贴图与动画帧。动画逻辑(帧索引、方向、一次性动画)照常执行,视觉默认是示意性的;**填图到 `public/assets/` 后由资源管线自动替换为真实贴图**(见 §11,所见即所得)。 - `@property` 装饰器按「编辑器默认参数」语义运行(宿主记录属性元数据,全部使用代码默认值)。 - Prefab 引用(playerPrefab/enemyPrefab/itemDropPrefab)在宿主中走代码内建的「无预制体 → 动态建节点」分支,等价于验证动态创建路径。 ## 5. 架构 ``` cc383script2d-host/ ├── src/ │ ├── shim/ # cc API 的浏览器实现(本仓库写,唯一改动源) │ │ ├─ decorator.ts # @ccclass / @property(兼容 legacy 与标准装饰器转换) │ │ ├─ component.ts # Component 基类 + 生命周期(onLoad/start/update/onDestroy) + schedule/scheduleOnce │ │ ├─ node.ts # Node 树: addComponent/getComponent/setPosition/setParent/getChildByName/on/off/destroy/active │ │ ├─ math.ts # Vec2 / Vec3 / Rect │ │ ├─ ui.ts # Label / ProgressBar / Sprite / UITransform │ │ ├─ prefab.ts # Prefab / instantiate(宿主走动态建节点分支) │ │ ├─ input.ts # Input / EventKeyboard / KeyCode / input(键盘) │ │ ├─ game.ts # game.totalTime 等 │ │ └─ index.ts # 'cc' 模块出口(esbuild 解析 `from 'cc'`) │ ├── runtime/ │ │ ├─ scene.ts # 场景管理器:生命周期推进 / 游戏循环 / 延迟销毁 │ │ ├─ renderer.ts # Canvas 渲染:实体绘制(色块/资源图二选一)、摄像机跟随、血条 │ │ ├─ hud.ts # 玩家三围 HUD / 背包面板 / 事件日志面板(HP 条/格子可用资源图) │ │ ├─ touch.ts # 移动端触屏控制:虚拟摇杆 + 动作按钮(合成键盘事件) │ │ ├─ netui.ts # 联机 UI(Phase 4.2):连接面板 + 幽灵玩家/物品渲染 + 远程事件反馈 │ │ ├─ server-enemy.ts# 服务器权威共享敌人(Phase 4.3):代理敌人(BaseLife 子类)+ 客户端层(真实掉落/隐藏私有敌人;服务器攻击玩家的血量覆盖由游戏侧 resolveEntity 处理,P4-4) │ │ ├─ assets.ts # 资源管线(P6):assets/img 帧动画加载/方向回退/音乐音效(无头环境空操作) │ │ └─ sprites.ts # 精灵状态:实体→资源槽位(元素/时机/方向),位移推断朝向 + 事件驱动攻击/受击/死亡 │ └── main.ts # 入口:场景装配(等价编辑器)+ 事件追踪 + 渲染循环 + __host 测试API + 资源预热/音效接线 ├── vendor/cc383script2d # git submodule,只读引用云端基线(185fc04,P5-1 修复后升级) ├── public/ │ ├─ index.html # 页面(canvas + 操作提示) │ └─ assets/ # 资源目录(所见即所得填图区):img/(玩家/敌人/采集物/物品/特效/UI/背景 帧动画)+ music/ + sfx/,见其 README ├── server/ │ ├─ serve.mjs # 静态服务 + /ws WebSocket 中继(同端口,Phase 4.2/4.3)+ 每 IP 连接上限;导出 createServer/startServer(CLI 与 Electron 桌面共用) │ ├─ net-relay.mjs # 联机中继逻辑(纯 JS 零依赖:JOIN/WELCOME、STATE/SNAPSHOT、EVENT/EVENT2C、LEAVE/LEAVED;Phase 4.3 共享敌人权威结算;maxClients/消息大小上限) │ ├─ enemy-world.mjs # 服务器权威共享敌人(Phase 4.3):敌人模拟/AI/掉落/重生(零依赖纯逻辑) │ └─ wsconn.mjs # 极简 RFC6455 WebSocket 服务器(零依赖,Node http + crypto;1MB 消息上限) ├── desktop/ │ ├─ main.js # P5 Electron 主进程:内置 server/serve.mjs(127.0.0.1,默认端口 18983)+ 浏览器窗口;双实例开两窗口即本地联机 │ └─ icon.ico # 桌面图标(scripts/make-icon.py 生成) ├── scripts/ │ ├─ make-icon.py # 生成 desktop/icon.ico(纯 Python,零依赖 PNG→ICO) │ └─ package-desktop.mjs# P5 打包:下载 Electron win32 发行 zip(npmmirror,校验 SHA256)→ 改名 exe → 写入 resources/app → 打便携版 zip(零 Wine;assets/ 整目录打进 exe) ├── tests/ │ ├─ headless.mjs # 无头逻辑验证(Node 驱动 __host,确定性断言,含联机中继+真 WS 端到端) │ ├─ assets.mjs # 资源管线单测(Node 里跑浏览器逻辑:帧发现/方向回退/状态→槽位) │ └─ plugins.mjs # 插件层单测(crafting/saving/buff 自检 + ×真实基座集成,需 GAME_SRC 指向本地工作树) ├── docs/verification-report.md # 验证报告(含问题 P1-1/P1-2/P1-3/P2-1/P3-1/P4-1/2/3/P4-4/P5-1 及修复复验) └── build.mjs # esbuild 打包;GAME_SRC 决定游戏源码位置(默认 submodule 基线,可切本地工作树);extensions/* 从 GAME_ROOT/extensions 第二源码根解析(L3 插件) ``` **依赖的 cc API 全集**(shim 需要覆盖的清单,来自对 cc383script2d 的静态分析): `_decorator`(ccclass/property)、`Component`、`Node`、`Vec2`、`Vec3`、`Rect`、`Label`、`ProgressBar`、`Sprite`、`UITransform`、`Prefab`、`instantiate`、`Input`、`EventKeyboard`、`KeyCode`、`input`、`game`(totalTime) **场景装配**(宿主代为完成,等价于作者在编辑器中手动创建的节点):`GameManager` 节点(启动脚本)/ `WorldManager` 节点(世界单例)/ `Camera` 节点(摄像机跟随)。游戏脚本本身零修改。 ## 6. 计划任务 | 阶段 | 任务 | 状态 | |------|------|------| | P0 | 创建宿主项目、README、git init、submodule | ✅ 本地完成(推送 Gitee 待项目方建远程仓库) | | P1 | 实现 cc shim 运行时(上表 API 全集) | ✅ 完成 | | P2 | 模拟场景 + Canvas 渲染 + 键盘交互 + 日志面板 | ✅ 完成 | | P3 | 构建验证:跑通全流程,产出验证报告 + 问题清单(只报告) | ✅ 完成(见 `docs/verification-report.md`) | | P4 | 部署 80 端口,国内浏览器可访问试玩 | ✅ 完成(`http://43.139.172.126`,systemd 保活) | | P4.5 | 移动端触屏控制(虚拟摇杆 + 动作按钮),手机可直接游玩 | ✅ 完成(2026-08-24) | | P4.2 | 联机 Phase 4.2:WebSocket 中继服务器(同端口 /ws)+ 浏览器双窗口联机 demo + 无头测试 | ✅ 完成(2026-09-03) | | P4.3 | 联机 Phase 4.3:服务器权威共享敌人(服务器模拟敌人/权威血量/死亡/掉落,全服同屏一致 + 敌人攻击玩家 + 掉落可拾取) | ✅ 完成(2026-09-03) | | P5 | Electron 桌面 exe(Windows 便携版,shim 演示版,非 Cocos 真产物) | ✅ 完成(2026-09-03,见 §7/§10;产物 `dist/survival-sandbox-demo-win32-x64.zip`,产品标识见游戏仓库 `release/game-info.md`) | | P5-1 | 追查「采集物(箱子)原地无限掉落」归属 → 定位为游戏侧 bug(`onHarvest` 未注销/未清血),已在游戏侧修复并复验,云基线已推送、submodule 已升级 | ✅ 完成(2026-09-03,见 §7 第九轮;默认构建源已含修复) | | P6 | 资源目录(所见即所得):`public/assets/` 填图管线(玩家/敌人/采集物/物品/特效/UI/背景 帧动画 + 音乐/音效)自动替换色块 | ✅ 完成(2026-09-03,见 §11) | | P7 | itch.io 发布方案 / 加密结论 / 服务器防攻击(含中继限流加固) | ✅ 方案完成(2026-09-03,见 §12) | | P8 | itch.io 上传(web `:html5` + exe `:windows`,产物已就绪) | ⏳ 待账号登录(2026-09-03,见 §13) | | P9 | 素材填充(assets/ 首批图/音乐/音效 → 所见即所得) | ⏳ 计划(P6 收尾) | > 当前优先级:**web 版(含手机触屏 + Phase 4.2 联机双窗口)、Windows 便携版桌面(P5)、资源填图管线(P6)均已跑通**,itch.io 发布方案(P7)已就绪。web、桌面 exe、手机完全同源(同一套构建产物 + 同一套 server/serve.mjs),exe 仅多一层桌面包装 + 内置本地联机中继。 ## 7. 已知问题 / 待同步(报告区,由项目方处理) > 本节内容由宿主维护者如实记录,**不修改游戏项目**。 - **✅ 基线已闭环(2026-09-03)**:项目方已推送**全部**修复与完整 demo(云端 `origin/master` = `09454ad`,含 GameManager、P1-1/2/3、P2-1、P3-1、Phase 4.1 联机核心、P4-1/2/3 修复与 **P4-4**)。`vendor/cc383script2d` submodule 已升级到 `09454ad`,宿主默认构建源切换为 submodule 云基线(`GAME_SRC=vendor/cc383script2d/_BaseClassScript`),无头 107 项断言 + 网络核心 47 项自检全绿。本地工作树与基线 diff 为空(内容一致),仍遵守「只读、零写入」铁律。 ### 验证发现的问题(详细见 `docs/verification-report.md`;P1-1/P1-2/P1-3/P2-1/P3-1/P4-1/P4-2/P4-3 均已由项目方修复并复验通过) | 编号 | 问题 | 实测现象 | 状态 | |------|------|---------|------| | **P1-1** | `WorldManager.registerEntity` 用 `entityId \|\| node.name` 作 key | 5 个敌人同名 "Enemy" → 注册表只留 1 个,其余成孤儿;同模板掉落物互相覆盖,掉 2 个面包只能拾 1 个 | ✅ 已修复(`uid` 唯一键),复验通过 | | **P1-2** | `GameManager.createEnemyNode()` 未 `addComponent(EnemyAI)` | 无 Prefab 时敌人完全静止,不巡逻/不索敌/不攻击 | ✅ 已修复(补挂 `EnemyAI`+`BaseAnimation`),复验通过 | | **P1-3** | `BaseItem.onUse` 为空实现 | 使用面包/绷带只扣数量,不恢复生命/饥饿/口渴 | ✅ 已修复(消费模板 `effects`),复验通过 | | **P2-1** | `BaseAnimation` 一次性动画被 `setMoving(false)` 每帧重置 | 站立攻击动画永远播不完,命中结算永不触发 → 站立攻击 100% 挥空 | ✅ 已修复(one-shot 期间 `setMoving/setDirection` 早退),复验通过 | | **P3-1** | Q 键"无限丢弃":热键栏幽灵条目 + 无条件 emit | 背包空后按 Q 仍每按一次凭空生成 1 个地面物品,物品无限复制 | ✅ 已修复(`getItemInHand` 跳幽灵 / `removeItem.useItem` 清热键栏幽灵 / `dropItem` 仅扣减成功才 emit / `moveToHotbar` 去交换改覆盖),复验通过 + 回归断言 | | **P4-1** | 真实 WebSocket 下 JOIN 握手被丢弃(CONNECTING 时 `send()` 静默丢弃、无 onopen 补发) | 服务器收不到 JOIN → 无 WELCOME、`_connected` 恒 false,真实联机完全不可用(Loopback 同步 open 掩盖) | ✅ 已修复(`NetTransport.onOpen` + CONNECTING 排队 flush + `onTransportOpen` 补发 JOIN),复验通过 | | **P4-2** | `serializeEntity` 只对 hp 取整,`pos` 浮点微变 → STATE 去抖失效 | 移动时每帧全速上行 STATE,违背"数值取整→去抖"承诺 | ✅ 已修复(`pos.x/y` 取整),复验通过 | | **P4-3** | 自检断言计数与 README 对不上(38 vs 39) | 纯计数脱节 | ✅ 已澄清(自检汇总行改为自报断言总数,47 项),复验通过 | | **P4-4** | 网络 uid 与本地实体 uid 不统一:本地玩家注册用 `Entity_N`、网络身份是服务器 `U_N`,`resolveEntity` 无法用 `U_N` 命中本地玩家 | 服务器权威 `entity:damaged`(targetUid=本机 `U_N`)经 `applyRemoteEvent→resolveEntity` 静默落空,玩家不掉血(宿主此前用 `server-enemy.ts` 自同步补丁绕行) | ✅ 已修复(项目方 `09454ad`):`resolveEntity` 按 `_myUid` 命中本地玩家 + `spawnGhost` 防御跳过本机回显;**宿主自同步补丁已删**,血量覆盖走游戏自身管线,复验通过(真 WS 端到端新增断言) | | **P5-1** | **采集物(宿主色块“箱子”)原地无限掉落**:`BaseDestructible.onHarvest` 掉落+销毁节点后**未注销 WorldManager、未把 `currentHp` 置 0**,`GameManager.tryHarvest` 的 `currentHp>0` 守卫形同虚设 | 站在同一采集物旁反复按 E:每次 E 都再掉一坨物品,**无限刷**(与 P3-1 同症状但根因不同) | ✅ **游戏侧 bug**(宿主仅复现,根因在 cc383script2d)。修复:`onHarvest` 先 `currentHp=0`(守卫生效)+ `tryHarvest` 先 `unregisterEntity`(防残留实例被重复命中)。已在游戏侧修复并复验(采集→拾空→再按 E 不再掉落;注册表无残留);回归断言已固化进 `tests/headless.mjs` 第 8 节(**在未修复基线会失败**:物品 9→7、Harvested 1→2,锁定根因)。云基线已推送 `12ef258`,submodule 已升级,默认生效 | > 修复均由项目方完成,宿主只报告 + 复验,不代改。遗留非阻塞项:`directionCount=8` 美术图集配置注意点、**共享敌人掉落的拾取为"各端实例化"模型**(装备系统已接通,2026-09-03,见 §7 第三轮回调)。`BaseLife.useItem` 死代码已由项目方删除(P4-4 清理)。 > **宿主新增功能(2026-09-03,纯宿主渲染/交互,游戏脚本零改动)**:① 攻击范围/时机示意(`attackfx.ts`:范围圆环 + 挥击扇形蓄力 + 受击闪光/飘伤);② 背包交互(点选格→再点=使用(仅消耗品)/点热键栏或按数字键=放入快捷栏);③ 物品图标系统(`itemshape.ts`:项目方扩充到 22 种模板后,宿主已为**全部 22 种配齐专用图标**,地图掉落物与背包图标一致;未收录模板按 itemId/类型兜底);④ 背包信息行(`hud.ts`:选中格显示 名称·类别·加成/效果)。 > > **2026-09-03 第二轮回调(掉落/采集闭环)复验**:项目方新增 `Harvestable`(树/岩石/草丛/鱼点可采集)与敌人 `tier` 分层(约 20% 精英),22 种物品全部可获取。宿主无头测试新增第 8/9 节固化回归断言(采集树→木材必掉+树销毁、4 种采集物类型齐全、精英生成血量 100、精英死亡必掉牛排、精英注销),**32 项断言全绿**。 > > **2026-09-03 第三轮回调(装备加成)复验**:项目方接通装备系统——`Inventory.equipItem/unequipItem` 将物品实例推入 `BaseLife.equipmentItems`(注册表优先/模板兜底、同类型 1 件替换),`useItem` 对武器/防具自动装备,`getAttackDamage/getDefense` 加成生效;宿主背包 UI 双点武器/防具 = 装备/卸下(替换原「未接通仅示意」),新增无头测试第 10 节断言(铁剑+5/皮甲+3/战斧替换/卸下/带装备战斗扣血)全绿。 > > **2026-09-03 第四轮回调(联机客户端核心 Phase 4.1)复验**:项目方提交 `Network/` 五文件(NetProtocol/NetState/NetTransport/NetSyncCore 纯 TS + NetworkSync cc 薄接线,已推送 e4a1e87)。宿主侧:① 补 shim `Node.isValid`(NetworkSync 幽灵清理依赖的 Cocos 语义);② 新增无头测试第 11 节(pairLoopback 双端连接 / EVENT2C 伤害血量权威化 / SNAPSHOT 幽灵生成·位置·销毁 / 攻击挥击上行 EVENT / STATE 去抖),**58 项断言全绿**。**发现 1 个需项目方修复的问题(P4-1)**:`NetworkSync.connect()` 在 `transport.connect()` 后立即发 JOIN,而真实 WebSocket 此刻处于 CONNECTING、`send()` 被丢弃且无 onopen 补发 → 真实联机收不到 WELCOME、_connected 恒 false,客户端完全不可用(Loopback 因同步 open 掩盖了此问题)。修复建议:`NetTransport` 增加 `onOpen` 回调,onopen 时补发 JOIN。详见报告 §四/§六。 > > **2026-09-03 第五轮回调(Phase 4.1 修复复验 + Phase 4.2 服务器/联机 demo)复验**:项目方已推送 P4-1/2/3 修复(`185fc04`),宿主:① submodule 升级到 `185fc04`,默认构建源切换为云基线(与本地工作树 diff 为空);② **实现 Phase 4.2 联机服务器**——`server/net-relay.mjs`(纯中继逻辑:JOIN→WELCOME 分配 uid / STATE→SNAPSHOT 组帧去抖中继 / EVENT→EVENT2C 转发 / LEAVE→LEAVED,零依赖可测试)+ `server/wsconn.mjs`(极简 RFC6455 WebSocket 服务器,与静态服务同端口 `/ws`,满足 CSP `connect-src 'self'`);③ **浏览器联机 UI**——`src/runtime/netui.ts` 右上角连接面板(地址输入/连接/断开/状态/在线列表)+ 幽灵玩家渲染(紫色,与本地绿色玩家区分)+ 幽灵物品 + 远程事件飘字/挥击闪光,游戏脚本零改动;④ **无头测试第 12 节**(中继单元 7 项 + 真 WebSocket 端到端 8 项:NetworkSync 经真实 RFC6455 服务器 ↔ 协议级第二客户端,覆盖 JOIN/WELCOME、幽灵生成/位置、STATE 移动中继、EVENT2C 表现、LEAVED 销毁),**106 项断言全绿**。双窗口联机入口:两个页面/两台机器各自点右上角「联机」(默认 `ws://<当前域名>/ws`),即可看到对方紫色幽灵玩家实时移动/攻击/掉落。 > > **2026-09-03 第六轮(Phase 4.3 服务器权威共享敌人,宿主实现,游戏脚本零改动)**:解决 §Network 已知限制"共享敌人血量各端独立演算可能分叉、敌人击杀掉落物不跨端同步"。① **服务器模拟共享敌人**(`server/enemy-world.mjs`,语义对齐 GameManager:~20% 精英 hp100/攻12/速130,普通 hp50/攻5/速100,环绕原点半径 150–400;AI 巡逻/追击最近玩家/近战攻击;死亡掷掉落表 + 延迟重生;`random` 可注入保证测试确定性);② **`net-relay.mjs`**:SNAPSHOT 并入敌人状态(各端收到同一批、血量一致)、拦截玩家命中 SE_* 的 `entity:damaged` 应用到权威 hp 后广播(双端串行收敛、分叉消除)、死亡广播 `entity:died` + `world:loot_dropped`(服务器掷掉落表,各端一致)、敌人攻击玩家按记录的玩家 hp 结算并广播权威伤害、WELCOME 增 `serverEnemies` 标志;`enemyCount` 默认 0=纯中继(现有行为/测试不变),`serve.mjs` 默认 5(可 `ENEMY_COUNT` 覆盖);③ **客户端代理敌人层**(`src/runtime/server-enemy.ts`):`ServerEnemyLife`(BaseLife 子类,覆写 `takeDamage` 本地只扣血+上行不本地死亡 → 避免与服务器重复掉落;覆写 `onScheduledTick` 空 → 不挨饿)+ `ServerEnemyLayer`(`snapshot_new`→建代理并注册进 WorldManager(`Object.defineProperty` 覆写 uid=SE_N,`resolveEntity`/`getLivesInRange` 均能命中);服务器攻击→本地玩家血量自同步 + `entity:died`→触发本地复活;服务器掉落→建真实可拾取掉落物 + 清同位置幽灵节点;进入权威模式隐藏本机 5 只私有敌人);④ **无头测试第 13 节**(中继单元 12 项:WELCOME 标志/同批敌人/权威血量/串行收敛/死亡掉落/敌人攻玩家;真 WebSocket 端到端 9 项:抑制私有敌人/代理创建/权威 hp/击杀移除/真实掉落/断开清理),**106 项断言全绿**。**零协议新增、零游戏侧改动**(全部复用 SNAPSHOT / EVENT / EVENT2C)。 > > **2026-09-03 第七轮(P4-4 根因修复复验,游戏侧改动,宿主只删补丁+加断言)**:Phase 4.3 遗留的"服务器攻击→本地玩家血量"依赖宿主自同步补丁(`server-enemy.ts` 手改 `currentHealth`),根因在游戏侧——`NetworkSync.resolveEntity` 无法用网络 uid `U_N` 命中本地玩家(本地注册键是 `Entity_N`)。项目方推送 `09454ad`:`resolveEntity` 按 `_myUid` 映射到本地玩家 + `spawnGhost` 防御跳过本机回显,服务器权威 `entity:damaged`(targetUid=本机 `U_N`) 走 `applyRemoteEvent → applyHp` 自身管线。宿主:① **删除自同步补丁**(`syncLocalPlayerHp` 与 `onRemoteEvent('damage')` 分支);② 无头测试第 13 节新增**真 WS 端到端断言**(停 enemy tick 消除并发干扰 → 复位本地血量 100 → 服务器权威 `entity:damaged` 带 hp=70 → 断言本地玩家血量经游戏管线降至 70;**该断言在无修复的旧基线会失败**,锁定根因);③ submodule 升级到 `09454ad`,**107 项断言全绿**。附带:项目方顺带删除 `BaseLife.useItem` 死代码(P4-4 清理,消耗品统一走 `BaseItem.onUse → CombatManager.applyEffect`)。 > > **2026-09-03 第八轮(P5 Windows 便携版桌面,纯宿主,游戏脚本零改动)**:完成 README §6 的 P5 待办——**Electron 桌面版**(`desktop/main.js` + `desktop/icon.ico`,与 web 完全同源)。① `server/serve.mjs` 重构为可复用(导出 `createServer/startServer`,CLI `node server/serve.mjs` 行为不变;`npm run serve`/无头测试/部署均复验通过);② 桌面主进程**内置同一套服务**(127.0.0.1:18983,可 `CC_HOST_PORT` 覆盖):静态服务 + `/ws` 联机中继 + Phase 4.3 共享敌人,开窗口即玩;**开两个 exe 自动复用同一本地中继**(第二实例探测到本应用已在服务 → 不再起服务器直接加载同 URL),双窗口本地联机无需公网;也可在联机面板填公网地址 `ws://43.139.172.126/ws` 与手机/网页端跨机联机;③ **打包链路零 Wine**:`scripts/package-desktop.mjs` 从 npmmirror 下载 Electron 44.1.1 win32-x64 发行包(SHA256 校验通过 `c574a229…`)→ 改名 `cc383script2d-host.exe` → 写入 `resources/app/`(与 web 同源构建产物,不含 node_modules/vendor,精简)→ 打便携版 zip(`dist/cc383script2d-host-win32-x64.zip`,~150MB);④ **实测**:xvfb 下启动宿主主进程与打包后产物均正常(页面 200、本地中继就绪、第二实例复用端口);无头 107 项断言全绿。遗留:exe 图标/版本元数据需 Wine 写入(`rcedit`),本机未装 Wine,故 exe 为 **Electron 默认图标**、文件名即应用名,属演示版可接受项(若在装有 Wine 的机器上 `npm run desktop:pack` 可换回 electron-packager 自定义图标)。 > > **2026-09-03 第九轮(P5-1 采集物无限掉落追查 + P6 资源管线 + P7 发布方案,宿主侧)**:① **P5-1 追查**:用户报告“拾取箱子可原地无限拾取(拾完再按 E 又出一堆物品)”。宿主用无头脚本复现——目标即**采集物(宿主色块形似箱子)**:E 采集树→掉 3 木→拾空→原地再按 E **又掉 3 木(复现)**。根因在**游戏侧**:`BaseDestructible.onHarvest` 掉落+销毁节点后未注销 WorldManager、未把 `currentHp` 置 0,`GameManager.tryHarvest` 的 `currentHp>0` 守卫失效 → 同一实例被反复采集。宿主在**游戏本地工作树**修复(`onHarvest` 先 `currentHp=0` + `tryHarvest` 先 `unregisterEntity`,双保险),新增第 8 节回归断言(未修复基线必失败,锁定根因),复验:再按 E 不再掉落、注册表无残留、**无头 109 项断言全绿**。② **P6 资源管线(所见即所得)**:`public/assets/` 填图区(163 目录,见 §11)——用户按“元素×时机”目录放 `0.png 1.png…` 帧动画,宿主 `assets.ts`(帧发现/方向回退/左向镜像/音效)+ `sprites.ts`(位移推断朝向 + 攻击/受击/死亡事件驱动槽位)+ renderer/hud/attackfx 接线:实体/物品/背景/血条/格子/面板/攻击挥击/受击/拾取/采集/死亡特效全部**有图自动替换、无图回退色块**;音乐(`music/bgm_*.mp3` 循环)+ 音效(`sfx/*.mp3`,首次交互解锁)同步管线。新增 `tests/assets.mjs`(帧发现/方向回退/状态→槽位单测,Node 跑浏览器逻辑)全绿;serve.mjs 补音频 MIME;桌面打包把 assets/ 整目录打进 exe。③ **P7 itch.io 发布/加密/防攻击**(详见 §12):web 版静态上传可玩(联机需自建中继);无需加密(客户端代码必然可见),建议 release 开 minify + 关 sourcemap;中继服务器已加防滥用(`RELAY_MAX_CLIENTS` 人数上限 / 单消息 64KB 上限 / `WS_MAX_PER_IP` 每 IP 4 连接,均实测生效)。 > > **2026-09-03 第十轮(推送闭环 + itch 发布准备)**:① 游戏侧已推送 `12ef258`(P5-1 修复 + `extensions/runtime-assets` 插件),宿主 submodule 已升级,**默认 `GAME_SRC=vendor` 构建/测试全绿(109 断言 + 资源管线单测),无需再切本地**;② 宿主推送 `3cf9cd5`(P6/P5-1/P7)+ `5c1c99d`(submodule 升级)+ `8106ce5`(`build:release` 脚本);③ **itch.io 发布产物已就绪**:`public-release/`(web,minify 无 sourcemap)+ 重建的 `dist/cc383script2d-host-win32-x64.zip`(含新代码 + assets)+ butler v15.31.0 已装;**待账号登录后上传**(web `:html5` + exe `:windows`,见 §13)。 > > **2026-09-03 第十一轮(联机断线清理 + 发布扫描加固,纯宿主,游戏脚本零改动)**:① **修复断线后幽灵永久残留 bug**——游戏侧 `NetworkSync.disconnect()`/`handleClose()` 只重置状态、不清理幽灵(仅 `onDestroy → destroyGhosts` 才清),而宿主 `netDisconnect` 只调 `disconnect()` 不销毁节点 → 手动断开/异常掉线后旧幽灵玩家与幽灵物品**永远残留在画面上**,且重连 `spawnGhost` 因 uid 已存在而跳过、旧幽灵不刷新。修复:宿主新增 `teardownNet()`(`disconnect()` + 销毁 NetworkSync 节点 → 帧末 `onDestroy` 触发 `destroyGhosts` 清空),接入 手动断开 / 重连前 / `onConnectionChange('closed')` 三条路径(`main.ts`)。② **发布扫描加固**:`publish.mjs` 敏感词扫描从 html/md/txt 扩展为 **html/md/txt/js**——minify 不删字符串字面量,`bundle.js` 属对外内容且可被 grep;实测注入含「宿主」的 probe.js 被拦截、移除后恢复绿。③ **无头测试新增第 12c 节**(手动断开幽灵清空 + getNet() 为 null;异常掉线清空 + 可干净重连,真 WS 端到端 6 项);**该节在未修复基线上失败(ghosts=1 残留),锁定根因**。**无头 115 项断言全绿**;build:release + publish 扫描(22 条规则)0 命中。线上部署已同步(本机即发布机,serve 直接读工作树)。 > > **2026-09-03 第十二轮(插件层 L3:crafting/saving/buff,游戏侧新增插件,宿主只加验证)**:游戏 `extensions/` 新增三个自包含玩法插件——`crafting`(合成:`Recipes` 纯数据 10 配方 + 新模板烤鱼;`CraftingManager` 纯 TS 核心,鸭子类型背包、**先证容量后扣料**、失败零改动原子性;`CraftingTable` 薄 cc 合成台)、`saving`(存档:`SaveFormat`/`SaveManager` 版本化快照 + 迁移链 + `StorageBackend`/读写器注入;`GameSaveController` 薄 cc 接 player/采集物/地面物品,`loadInventory` 失败快照回滚)、`buff`(状态效果:`BuffSystem` 三种堆叠策略 refresh/accumulate/strongest + DOT/再生,补齐基座 `CombatManager.applyEffect` 的 speed/damage/defense 占位;`BuffController` update 驱动、幂等覆写/还原)。全部遵循插件契约:只依赖基座、不改基座、可选加载、自带纯 TS selfcheck。宿主新增 `tests/plugins.mjs`(仿 assets.mjs 打包跑 3 自检 + **真实基座集成**:shim Inventory 合成/背包满零改动、存档 round-trip、BuffController 驱动中毒/移速)——**插件自检 59 项 + 集成 13 项 = 72 项全绿**(`GAME_SRC=../cc383script2d/_BaseClassScript node tests/plugins.mjs`)。游戏主干零 diff;云基线 `npm run build`/无头 115 项不受影响(main.ts 不 import 插件)。 ## 8. 开发环境 - 服务器:腾讯云 `43.139.172.126`(Ubuntu,国内网络直连快) - Node v22 / Python 3.12 / sudo 可用 - 构建:esbuild(TS → 浏览器单文件,无框架依赖) - 部署:Node 静态服务 + systemd,绑定 `0.0.0.0:80`(安全组仅放行 80/22;实测 80 外网可达) - 桌面(P5):Electron 44.1.1(本机二进制从 npmmirror 镜像拉取,GitHub 被墙)+ 自写零 Wine 打包脚本(`scripts/package-desktop.mjs`);Electron 运行需 GTK3 库(本机已装 libgtk-3-0 等,`apt-get install libgtk-3-0 libnss3 libasound2t64 libgbm1 libxss1`) ## 9. 部署与访问 - **上线时间**:2026-08-10 - **访问地址**:`http://43.139.172.126`(80 端口,国内直连快;实测德国/法国/瑞典/伊朗等外部节点 HTTP 200) - **服务方式**:systemd 服务 `cc-host`(静态文件服务,禁目录列表/路径规范化/安全响应头/无访问日志),重启自启、崩溃自拉起 - **操作说明(桌面)**:WASD/方向键 移动 · 空格/J 攻击 · E 拾取 · Q 丢弃 · I 背包 · 数字键 1-9 热键 - **操作说明(手机)**:触屏直接游玩 —— 左侧虚拟摇杆移动,右侧按钮 攻击/拾取/丢弃/背包 + 热键栏,建议横屏。触屏操作通过宿主合成键盘事件,复用游戏脚本 PlayerController 的按键绑定,游戏脚本零改动。 - **操作说明(联机,Phase 4.2)**:右上角「联机」面板默认地址 `ws://<当前域名>/ws`(同源),点「联机」即可;**开两个页面(或两台机器)各自联机** → 看到对方**紫色幽灵玩家**实时移动,攻击/伤害/掉落/拾取以飘字同步(对方看到你同理)。断开点「断开」。 - **操作说明(联机,Phase 4.3 服务器权威共享敌人)**:连上后自动进入服务器权威模式——本机 5 只私有敌人隐藏,场上红色敌人是**服务器模拟的共享敌人**,所有玩家同屏打同一批:血量/死亡/掉落全服一致(谁命中谁扣,服务器为唯一权威);敌人会追击并攻击玩家(伤害全服一致,被击时本地血量经游戏自身管线权威覆盖(P4-4 根因已修)、死亡自动复活);击杀后掉落**可拾取**(谁先捡谁得,其它端同步消失)。被击/击杀均可能有 ≤1 tick(≤100ms)的网络往返延迟。 - **操作说明(桌面版,P5,Windows 便携版)**:解压 `survival-sandbox-demo-win32-x64.zip` → 双击 `survival-sandbox-demo.exe` 即玩(**免安装**,与 web 版同源同操作)。exe 内置本地服务与联机中继(`127.0.0.1:18983`):**开两个 exe 自动连上同一个本地中继** → 双窗口本地双人(对方为紫色幽灵);点右上角「联机」改地址为 `ws://43.139.172.126/ws` 可与手机/网页端跨机联机。exe 为演示版,使用 Electron 默认图标(见 §7 第八轮遗留说明)。产品名(exe/zip/窗口标题)由游戏仓库 `release/game-info.md` 的「暂定名/产品标识」自动生成,无工程代号。 - **访问提示(2026-08-24)**:公司出差电脑实测被公司网络拦截无法连接(超时),海外节点与家用/手机网络均可正常访问;请用**手机或非公司网络**打开 `http://43.139.172.126` 游玩(桌面版 exe 不受公司网络拦截影响)。 - **验证报告**:详见 `docs/verification-report.md`(发现的问题 P1-1/P1-2/P1-3/P2-1/P3-1/P4-1/P4-2/P4-3/P4-4/P5-1 均已闭环;P5-1 采集物无限掉落为游戏侧 bug,云基线已推送 `12ef258` 并升级 submodule;物品种类已由项目方扩充至 22 种;联机 Phase 4.2 服务器与双窗口 demo、P5 Windows 便携版桌面、P6 资源管线、P7 发布方案均已完成) - **itch.io(P7)**:发布方案见 §12——web 版静态上传即玩(联机需自建中继),exe 上传 `dist/survival-sandbox-demo-win32-x64.zip`;暂未上传(等资源管线出第一批素材后一起发) ### 本地快速开始 ```bash git clone <你的 Gitee cc383script2d-host 地址> && cd cc383script2d-host npm install # 或直接复用本机 esbuild(见下) npm run build # 构建 bundle.js(默认 GAME_SRC=vendor/cc383script2d/_BaseClassScript 云基线,只读) node tests/headless.mjs # 无头逻辑验证(115 项断言,应全部通过) node tests/assets.mjs # 资源管线单测(帧发现/方向回退/状态→槽位) GAME_SRC=../cc383script2d/_BaseClassScript node tests/plugins.mjs # 插件层单测(游戏本地工作树有新插件;未入 submodule 时需指定 GAME_SRC) sudo node server/serve.mjs # 或 systemctl 方式,本地 http://127.0.0.1:80 预览(含 /ws 联机中继) # --- P5 桌面版(Windows 便携版 exe)--- npm run desktop:dist # 一键:build + 下载 Electron win32(npmmirror,SHA256 校验)→ 打便携版 zip → dist/survival-sandbox-demo-win32-x64.zip(产品名读游戏仓库 release/) npm run desktop:run # 开发预览:electron .(本机需图形环境;无头可用 xvfb-run + --no-sandbox) ``` > 说明:本机因腾讯 npm 镜像偶发装不上 esbuild,采用 `ln -s /home/ubuntu/metabot/node_modules/esbuild node_modules/esbuild` 软链复用;其它机器 `npm install` 即可(esbuild 已在 devDependencies)。桌面打包无需 Wine(`scripts/package-desktop.mjs` 手动组装);若需给 exe 写自定义图标/版本元数据,可在装有 Wine 的机器上用 `npx electron-packager` 重新打包(devDependencies 已含 electron,electron-packager 已移除)。 ## 10. 决策记录 - **2026-08-10(确认)**:新建独立 Gitee 仓库 `cc383script2d-host`;游戏脚本用 **git submodule** 引用(不会改动原仓库,仅宿主内记录指向);exe(Electron)与 web 都做、web 优先;**严禁改动仓库外文件**,问题只报告不代改。 - **2026-08-10(构建源)**:发现 cc383script2d 云基线缺 `GameManager.ts`(demo 全在本地未推送)后,构建源决策为**默认只读引用本地工作树**(`GAME_SRC=../cc383script2d/_BaseClassScript`),submodule 保留作可复现基线,项目方推送后可一键切换复验。 - **2026-08-10(验证+上线)**:无头逻辑验证全流程跑通(3/3 确定性),发现 P1-1/P1-2/P1-3 三个问题(只报告);`http://43.139.172.126` 上线(systemd `cc-host` 保活)。 - **2026-08-24(移动端触屏)**:确认公司出差电脑被公司网络拦截无法访问(超时;海外节点检测 5/5 HTTP 200,服务器无问题),改由手机游玩。宿主新增移动端触屏控制(`src/runtime/touch.ts`:虚拟摇杆 WASD + 攻击/拾取/丢弃/背包/热键栏按钮,合成键盘事件复用游戏按键绑定,仅触摸设备启用);画布改为按实际屏幕自适应分辨率(DPR 感知);触屏模式压缩日志面板。P5 exe 在手机 web 版验证通过后启动。 - **2026-09-02(修复复验)**:作者修复宿主报告的 P1-1(实体 `uid` 唯一键)/P1-2(补挂 `EnemyAI`)/P1-3(消耗品 `effects` 生效),并修复宿主新发现的 P2-1(一次性动画被 `setMoving(false)` 重置 → 站立攻击挥空),配套新增八方向动画 + 触屏 8 方向摇杆、热键栏开局填充、阵营过滤、EventBus `off` 精确匹配。宿主重新编译、无头 + 浏览器复验全部通过;`tests/headless.mjs` 适配新攻击机制(等动画播完再断言)。 - **2026-09-03(基线闭环 + Phase 4.2)**:作者推送全部修复(`origin/master` = `185fc04`,含 P1-1/2/3、P2-1、P3-1、Phase 4.1 联机核心与 P4-1/2/3)。宿主:① submodule 升级到 `185fc04`、默认构建源切换为云基线(无头 106 项 + 自检 47 项全绿);② 实现 Phase 4.2 联机服务器(`net-relay.mjs` 纯中继 + `wsconn.mjs` 极简 RFC6455,与静态服务同端口 `/ws`);③ 浏览器联机 UI(`netui.ts` 连接面板 + 幽灵渲染 + 远程事件飘字);④ 无头测试新增第 12 节(中继单元 7 项 + 真 WebSocket 端到端 8 项)。 - **2026-09-03(Phase 4.3 服务器权威共享敌人)**:宿主实现服务器权威共享敌人,解决"共享敌人血量各端分叉、击杀掉落不跨端同步"的已知限制。① `server/enemy-world.mjs` 服务器模拟 N 只共享敌人(语义对齐 GameManager:~20% 精英 hp100/攻12/速130,普通 hp50/攻5/速100,巡逻/追击/近战 AI,死亡掷掉落表 + 延迟重生,random 可注入保证测试确定性);② `net-relay.mjs` SNAPSHOT 并入敌人、拦截 SE_* 的 `entity:damaged` 应用到权威 hp、敌人攻击玩家广播权威伤害、WELCOME 增 `serverEnemies` 标志(`enemyCount` 默认 0=纯中继,现有行为不变;`serve.mjs` 默认 5,可 `ENEMY_COUNT` 覆盖);③ 客户端 `src/runtime/server-enemy.ts` 代理敌人层(BaseLife 子类覆写 `takeDamage` 本地只扣血+上行不本地死亡 → 避免双重掉落;`Object.defineProperty` 覆写 uid=SE_N 使 `resolveEntity` 能命中;进入权威模式隐藏本机 5 只私有敌人;敌人攻击→本地玩家血量自同步 + 触发本地复活;服务器掉落→建真实可拾取掉落物 + 清同位置幽灵);④ 无头测试新增第 13 节(中继单元 12 项 + 真 WS 端到端 9 项),**106 项断言全绿**。零协议新增、零游戏侧改动。 - **2026-09-03(P4-4 根因修复复验)**:Phase 4.3 的"服务器攻击→本地玩家血量"自同步补丁根因在游戏侧(`resolveEntity` 无法用网络 uid `U_N` 命中本地玩家),项目方推送 `09454ad` 修复(`resolveEntity` 按 `_myUid` 映射 + `spawnGhost` 防自幽灵),宿主删除自同步补丁(`syncLocalPlayerHp` + `onRemoteEvent('damage')` 分支)并新增真 WS 端到端断言(停 enemy tick、复位血量、权威 hp 覆盖,无修复时失败),submodule 升级 `09454ad`,**107 项断言全绿**。项目方顺带删除 `BaseLife.useItem` 死代码(P4-4 清理)。 - **2026-09-03(P5 Windows 便携版桌面)**:完成 P5——Electron 桌面版,与 web 完全同源。决策:① **服务复用**——`serve.mjs` 重构为可导入(导出 `createServer/startServer`),桌面主进程内置同一套 静态+联机中继+共享敌人 服务(127.0.0.1:18983,`CC_HOST_PORT` 可覆盖),CLI/部署/无头测试行为不变;② **双窗口本地联机**——第二实例探测到本应用已在服务则不再起服务器、直接加载同 URL(两个 exe 连同一中继即可本地双人);③ **零 Wine 打包**——GitHub 被墙、npm 走腾讯镜像(`NODE_ENV=production` 会跳过 devDependencies,安装需 `--include=dev`),electron-packager 的 win32 元数据写入需 Wine(~1GB),故自写 `scripts/package-desktop.mjs`:npmmirror 下载 Electron 44.1.1 win32-x64(SHA256 `c574a229…` 校验)→ 改名 `cc383script2d-host.exe` → 写入 `resources/app/` → 打便携版 zip;④ **实测**:xvfb 下宿主与打包产物均正常启动(页面 200、中继就绪、第二实例复用端口)。遗留:exe 图标/版本元数据需 Wine(演示版用 Electron 默认图标);桌面版待项目方/用户在 Windows 上实测。产物 `dist/cc383script2d-host-win32-x64.zip`(~150MB)。 - **2026-09-03(P5-1 采集物无限掉落 + P6 资源管线 + P7 发布方案)**:① P5-1 追查定位为游戏侧 bug 并在游戏本地工作树修复(`onHarvest` 清血 + `tryHarvest` 注销,详见 §7 第九轮);② P6 资源目录(`public/assets/` 所见即所得填图)+ 宿主资源管线(`assets.ts`/`sprites.ts`/renderer/hud/attackfx 接线,`tests/assets.mjs` 单测);③ P7 itch.io 发布/加密/防攻击方案(见 §12),中继服务器加 `RELAY_MAX_CLIENTS`/`RELAY_MAX_MSG_BYTES`/`WS_MAX_PER_IP` 防滥用(实测生效)。 - **2026-09-03(联机断线清理 + 发布扫描加固)**:① 断线清理决策——联机断开(手动/异常)必须彻底清空幽灵,宿主侧用「销毁 NetworkSync 节点 → 帧末 `onDestroy → destroyGhosts`」实现(游戏侧 `disconnect/handleClose` 不清理,属游戏侧语义,宿主以生命周期补足、不代改游戏);接入 `teardownNet()` 三条路径(`netDisconnect` / `netConnect` 重连前 / `onConnectionChange('closed')`);② 发布扫描加固——敏感词扫描扩到 `bundle.js`(minify 保留字符串字面量,属对外内容),保证保密承诺覆盖全部发布产物;③ 新增无头 12c 节 6 项断言,退化基线必失败(锁定根因)。 - **待办**:① **itch.io 上传**(P8,等账号登录后 `butler push`,见 §13);② **素材填充**(P9,assets/ 首批图/音乐/音效);③ 手机 web 版、双窗口联机(含 Phase 4.3 共享敌人)与 **Windows 桌面版(P5)** 由项目方/用户实测确认;④ 若需 exe 自定义图标/元数据,在装有 Wine 的机器上重新打包(或在 Linux 装 Wine)。 --- ## 11. 资源目录(所见即所得填图区,P6) **一句话**:`public/assets/` 是给你贴图/贴音频的“快递柜”——按 **元素 × 时机** 的目录名放 **以索引命名的帧动画**(`0.png 1.png 2.png…`),宿主自动加载并替换原来的色块/图标;**填一张生效一张,无需改代码**。音乐/音效同样按文件名放。 **复用架构(P6 已插件化)**:资源系统的**通用核心已抽到游戏仓库插件 `extensions/runtime-assets`**(L3,纯 TS、零 cc、零宿主依赖)——槽位约定 / 元素映射 / 精灵状态机 / **`AssetProvider` 接口**。本宿主只是该插件的**一个实现方**(`src/runtime/assets.ts` = 浏览器 Image/Audio provider;`src/runtime/sprites.ts` = 薄接线)。将来 host1/host2/…(其它类型游戏的新宿主)实现同一个 `AssetProvider` 接口即可整套复用;换游戏只需传自定义元素映射。详见插件 `extensions/runtime-assets/README.md`。 - 目录结构、命名规则、回退规则、完整槽位清单:**见 `public/assets/README.md`**(权威文档)。 - 当前已接线的槽位:**玩家**(idle/move+八方向/attack/hurt/death)、**敌人**(normal/elite 两套同构)、**可采集物**(tree/rock/bush/fishing_spot 的 idle/breaking/harvest)、**可破坏物/宝箱**(destructible/chest 预留)、**物品**(22 模板 + unknown 的 ground/icon)、**特效**(fx/attack_swing、hit、pickup、harvest、death、dash)、**UI**(hpbar 底/填充、hotbar/inventory 格子、panel、selected)、**背景**(bg/ground、grid、shadow);音乐 `music/bgm_*.mp3`(循环)、音效 `sfx/*.mp3`(攻击/受击/拾取/丢弃/采集/死亡/装备/界面)。 - 使用示例: ```bash # 把玩家的待机图放进去(2 帧待机动画) mkdir -p public/assets/img/player/idle cp 我的待机_0.png public/assets/img/player/idle/0.png cp 我的待机_1.png public/assets/img/player/idle/1.png # 刷新页面(Ctrl+F5)即生效;桌面版需重新 npm run desktop:dist ``` - 回退规则:带方向的槽位优先 `move_down/` 等,缺失回退 `move/`(左向自动镜像);该槽位无任何图 → 回退宿主色块/图标,游戏照常可玩。 - 新增游戏元素:复制 `img/item/unknown/` 或 `img/enemy/normal/` 目录改名即可命中,无需改代码。 ## 12. itch.io 发布 / 加密 / 服务器防攻击(P7 方案) > **发布分工(2026-09-03 调整)**:发布**内容**(游戏名 / 玩法介绍 / 操作 / 特性 / 企划)与**保密规则** > 写在游戏仓库 `cc383script2d/release/`(**内容随工厂走、保证 factory 通用**,见游戏仓库 §如何扩展); > 本宿主只负责**执行发布**:`npm run build:release`(清洗产物:minify、无 sourcemap、中性标题、打包物 > 不含工程说明)→ `npm run publish`(按 `release/confidentiality.md` 敏感词扫描 + 组装 itch 页文案 + > 打印 / 执行上传)。宿主不感知具体游戏身份,换游戏只需换游戏仓库的 `release/`。 ### 12.1 web 版发布 - **上传什么**:`npm run build:release` 产出的 `public-release/`(`index.html` + `bundle.js` + `assets/`), **不含** node_modules/服务器与工程说明文件。用 [butler](https://itch.io/docs/butler) 或网页上传 zip: ```bash npm run build:release # 清洗后的发布产物(minify、无 sourcemap、标题=「暂定名」) npm run publish # 敏感词扫描 + 组装 itch 页文案 + 打印上传命令(确认后加 --push 真正上传) ``` 内容源(标题 / 页面文案)来自游戏仓库 `release/game-info.md` 与 `release/itch-page.md`;默认 `GAME_SRC=vendor` 云基线需已含 `release/`(未含时可用 `GAME_SRC=../cc383script2d/_BaseClassScript` 指向游戏工作树)。 - **itch.io 是纯静态托管,没有后端**:页面上的 `/ws` 联机中继、Phase 4.3 服务器权威共享敌人在 itch 上**无法运行**。发布到 itch 的版本 = **单机完整可玩**(移动/战斗/采集/掉落/拾取/背包/热键/装备全通)。 - **想要 itch 上也能联机**:把 `net-relay`/`enemy-world` 部署到自己的服务器(本机 `43.139.172.126` 已有),并在宿主 CSP `connect-src` 里加上该域名(当前 `connect-src 'self'`),联机面板填 `ws://<你的域名>/ws`。建议先发单机版,联机 demo 留在自建站。 - **嵌入注意**: - 音频自动播放被浏览器拦 → 宿主已在**首次点击/按键**解锁音频并播 BGM(`unlockAudio`),发布前可加一层“点击开始”遮罩更稳; - iframe 内键盘需要焦点 → 页面加载时 `window.focus()`; - 画布已 DPR 自适应 + `viewport-fit=cover`,手机可玩; - itch 页面设置建议:开 **Mobile friendly**、选合适 iframe 尺寸、可开“fullscreen”。 - **发布前建议**:`build.mjs` 开 `minify: true`、去掉 sourcemap(release 构建),详见 12.3。 ### 12.2 exe(Windows)版发布 - 上传 `dist/` 下的 Windows 便携包(~150MB;`npm run publish` 会自动找到最新 zip): ```bash npm run publish # 扫描 + 组装 + 打印上传命令(含 windows 通道) npm run publish -- --push # 检查通过后真正上传(需 ITCH_ACCOUNT/ITCH_SLUG 或替换占位) ``` 或在网页上传器里按“Windows”上传 zip。exe 内置静态服务 + 联机中继(127.0.0.1),**本地双窗口联机开箱即用**,不受 itch 静态托管限制。 - **产品命名(已中性化)**:zip / exe / 窗口标题已统一为中性产品名(`survival-sandbox-demo` / 「2D 生存沙盒 Demo」),由 `package-desktop.mjs` 读游戏仓库 `release/game-info.md` 的「产品标识/暂定名」自动生成,无工程代号;改作品名只需改工厂 `release/`(见游戏仓库 `release/confidentiality.md` §五)。 - **注意**:Electron 便携版未签名,Windows SmartScreen/部分杀软可能提示“未知发布者”——对免费小项目一般可接受(点“仍要运行”);若在意需代码签名证书(收费)。发布页建议注明“Windows x64,解压即玩”。 ### 12.3 是否需要加密 - **结论:不需要加密(DRM 无意义)**。web/exe 游戏的所有代码和资源最终都在玩家机器上(JS 源码必然可见、Electron 的 `resources/app` 可解包),任何“加密”都能被绕过,只增加成本。 - **建议做的是**: 1. release 构建 **esbuild `minify: true`**(压缩混淆,防“一眼看代码”); 2. **去掉 sourcemap**(当前构建带 `.map`,等于把源码送给玩家); 3. **保密清洗**(`build:release` 已内置):页面标题用游戏仓库 `release/` 的「暂定名」、打包物不含工程说明文件(README*);`npm run publish` 再按 `release/confidentiality.md` 敏感词表扫可见文本,命中即拦截; 4. 美术资源若怕被扒,可做拼合图集/重命名,但属“防君子不防小人”,性价比低,按需做。 ### 12.4 服务器防攻击 - **现状(已有基础加固)**:静态服务仅 `GET/HEAD`、路径规范化防目录穿越、禁目录列表、`no-cache`、安全响应头 + CSP、无访问日志;`wsconn.mjs` 消息 1MB 上限。 - **本次新增(中继防滥用,实测生效)**: | 环境变量 | 默认 | 作用 | |---|---|---| | `RELAY_MAX_CLIENTS` | 0(不限) | 联机同时在线上限,满则拒绝新 JOIN | | `RELAY_MAX_MSG_BYTES` | 65536 | 单条客户端消息上限,超限丢弃 | | `WS_MAX_PER_IP` | 4 | 每 IP 的 WebSocket 连接数上限(本地多开/多窗口不受影响) | - **对外公网部署建议**(若把中继开放到公网): 1. **套 Cloudflare**(免费):隐藏源站 IP + 抗 DDoS + 可加 WAF 规则; 2. 服务器侧:`ufw` 只放行必要端口、`fail2ban` 拦扫描、**不以 root 跑**(80 端口可用 caddy/nginx 反代到高位端口,顺带上 HTTPS/WSS); 3. 游戏层:保持 `ENEMY_COUNT` 有上限、快照/STATE 频率有节流(已 10Hz + 去抖);如需防“机器人刷在线”,可在 JOIN 加简单口令(`RELAY_TOKEN`,当前未加,按需)。 - **诚实声明**:本仓库的静态服务与中继是“演示/朋友间联机”级别,未做完整抗 DDoS(那是基础设施层面的事,交给 Cloudflare + 云厂商安全组即可)。 - **跨游戏复用边界**:见 `server/README.md`——`serve.mjs`/`net-relay.mjs`/`wsconn.mjs` 是**游戏无关的通用中继核心**(协议由游戏侧 `NetProtocol` 冻结定义,换游戏不改中继);`enemy-world.mjs` 是唯一游戏相关件(仿照它做“服务器侧插件”注入即可)。 --- ## 13. 当前状态与后续规划(2026-09-03) ### 当前状态 - **云基线**:游戏 `4420453`(P5-1 修复 + `extensions/runtime-assets` 插件 + **`release/` 发布内容层(含产品标识)**),submodule 已升级;默认 `GAME_SRC=vendor` 构建/测试/发布检查全绿(**无头 115 项断言 + 资源管线单测 + publish 敏感词扫描 0 命中**——含 minify 后仍保留字符串字面量的 `bundle.js`,见 §7 第十一轮)。 - **功能闭环**:22 物品 / 采集 / 装备 / 联机 P4.2(中继 + 双窗口)/ P4.3(服务器权威共享敌人)/ P4-4 / P5-1;资源管线**插件化**(P6,宿主 = `AssetProvider` 实现方);发布方案(P7)。 - **插件层(L3,本机本地工作树)**:游戏 `extensions/` 新增 `crafting`(合成)/ `saving`(存档)/ `buff`(状态效果)三个自包含插件(纯 TS 核心 + 薄 cc 层 + selfcheck);`tests/plugins.mjs` 一键验证(**插件自检 59 项 + 真实基座集成 13 项 = 72 项全绿**,`GAME_SRC=../cc383script2d/_BaseClassScript`)。未推送、未入 submodule,云基线 `npm run build` 不受影响(main.ts 不 import 插件 = 可选加载)。 - **产物就绪**:web `public-release/`(minify 无 sourcemap);exe `dist/survival-sandbox-demo-win32-x64.zip`(已重建,含新代码 + assets + 中性产品名);butler v15.31.0 已装。**itch.io 上传待账号登录**(§12)。 - **线上**:`http://43.139.172.126`(联机版:P4.2 双窗口 + P4.3 共享敌人,systemd 保活)。 ### 后续规划(按优先级) 1. **itch.io 发布(P8,等账号登录;内容源 = 游戏仓库 `release/`,本宿主执行发布)**: ```bash npm run build:release # 清洗发布产物(minify、无 sourcemap、中性标题、无工程说明) npm run publish # 敏感词扫描(按游戏 release/confidentiality.md)+ 组装 itch 页 + 打印命令 npm run publish -- --push # 检查通过后真正 butler push(ITCH_ACCOUNT/ITCH_SLUG 或替换占位) ``` 发布页注明:web 为单机版(联机需自建中继);exe 为 Win x64 便携版。 2. **素材填充(P9,P6 收尾)**:往 `public/assets/` 填首批图(玩家/敌人/物品/UI/背景 帧动画)+ 音乐/音效 → 刷新即所见即所得(桌面版需重新 `npm run desktop:dist`)。 3. **联机公网优化**:CSP `connect-src` 加自建中继域名、Cloudflare / ufw / fail2ban、`RELAY_MAX_CLIENTS` 等限流参数按需调(§12.4)。 4. **工厂化落地(复用)**:以 `runtime-assets` 插件 + 本宿主为参考,为下一款游戏建 host2——实现 `AssetProvider` 接口即可整套复用资产系统与服务器通用核心(§11 / server/README.md);`crafting/saving/buff` 插件的「纯 TS 核心 + 鸭子类型接口」同样跨宿主复用(`tests/plugins.mjs` 是 L2 验收入口)。 5. **可选**:exe 自定义图标/版本元数据(Wine)、代码签名降 SmartScreen 提示、联机**自动重连**(断线清理已闭环,见 §7 第十一轮)/平滑插值。