房主小世界 · 星型拓扑 · 双通道 · 客户端判定 + 房主校验
| 通道 | 创建方式 | 承载 | 语义后果 |
|---|---|---|---|
| s 状态 | createDataChannel('s', {ordered:false, maxRetransmits:0}) |
upd(上报位姿 20Hz)、sync(房主快照 20Hz)、voice(语音帧) |
丢了就等下一个。位姿用 lerp 平滑,画面约滞后 1 RTT;语音有 80ms 重排窗,缺包跳过不阻塞。 |
| e 事件 | createDataChannel('e')(默认参数) |
hello / team / roster / fire / blt / fireRocket / rkt / hitRep / hit / hitReject / kill / kstat / died / crash / envDmg / round / rEnd / matchEnd / pickList / go / ping / pong / kick / modeMismatch |
可靠有序:不会丢,但一个包丢了后面全排队等重传(队头阻塞 → 几十到几百 ms 的延迟尖峰)。 |
子弹与命中全部走 e,所以「hitRep 丢包」在网络层不会发生;会失效的只有逻辑丢弃(回合已结束、目标已死、去重表命中)和 40m 位置校验拒绝。 反过来,位姿走 s,所以「开火快照」与「对方看到的机体位置」天然对不齐。
fireGun 生成本地子弹(src='me'),放枪口火光;随后广播 fire{o,d,fid,s,dm,sl}o/d/s 重建一颗 foeVis 子弹,再广播 blt;各端按同样参数再建一颗纯视觉弹src==='me' 的子弹做 segSphere(prev→pos) 扫掠;命中即 active=false,子弹当帧消失foeVis 子弹永不与单位碰撞,只跟地形/建筑交互;它只是曳光命中驾驶舱 ×3)—— 不等网络,但不改血量hitRep{fid, id, part, p, splash?, dmg?} 发给房主fid/id 非空 → hitSettled 去重(键 rid:fid:id,5s)→ 目标存活 → |p − tgtPos| ≤ 40mpeerDie → 记台账 → 广播 killhit{s,v,part,pos,dmg,hp,fatal,fid}netBroadcast 给所有人me.hp = d.hp;旁观者:火花/爆炸 + 目标血条。所有端同时 killBulletFx(s, fid) 移除那发弹道| 事项 | 判定方 | 依据 |
|---|---|---|
| 这一发打没打中 | 开火者 | 本地 segSphere 扫掠(用自己看到的画面) |
| 伤害是多少 | 房主 | 基础伤害 × 房主按目标弱点表复核的倍率 |
| 命中位置是否合理 | 房主 | HIT_TOLERANCE = 40m,超出回 hitReject |
| 血量 / 死亡 | 房主 | applyPeerDamage → peerDie |
| 谁杀了谁 | 房主 | lastDmgFrom + 击杀台账,3s 一次 kstat 校正 |
| 回合 / 比分 / 终局 | 房主 | rEnd / matchEnd;非房主用 s0/s1 按阵营换算 |
| 撞地 / 出海 | 房主 | upd 的 y 值 / 弹道位置 |
| 弹道长什么样 | 各端自己 | fire/blt 参数重建,纯视觉,不参与判定 |
| 玩法 / 武器 | 本地实体 | 网络 |
|---|---|---|
| 空战 · 机炮 / 高爆 | fireGun → bullets | fire{o,d,fid,s,dm,sl} |
| 空战 · 机载火箭 | firePlaneRocket → rockets | fireRocket → rkt |
| 防空车 · 飞机机炮 | 同上 | 同上 |
| 防空车 · 车载机枪 | fireGunAAGun(520 m/s) | fire{o,d,fid,s,dm,sl} |
| 防空车 · 火箭炮 | fireRocket | fireRocket → rkt |
| 直升机 · 机枪 | fireGun(speed+520) | fire v3.11 补齐 s/dm/sl |
| 直升机 · 航空炸弹 | spawnRocketFx + 炸弹模型 | fireRocket/rkt v3.11 新增 bomb:1 + v/g/br/lr/life |
炸弹的初速是「投弹瞬间的载机速度 − 弹射速度」,所以广播必须带 v;只发方向会让接收端按 260 m/s 直线飞,弹道完全不同。
接收端用模式包钩子 spawnRemoteBomb 重建炸弹模型与物理,结算仍由投弹者的 hitRep 驱动。
≥ 80ms 直接 kick 并延迟重载页面。通过检测才算「就绪」,人齐判定用的是就绪人数 —— 所以不合格的玩家会在进入选图/选阵营之前被剔除,不会把房主从选人界面打回大厅;主动踢出也不缩编(3v3 不会悄悄变成 2v2)。onClose → 删除连接与 roster 条目 → 该玩家按阵亡结算;回合内剩余玩家继续,房主推进 rEnd。gm,加入方在生成应答码前就比对;不一致直接拒绝,连 PeerConnection 都不建。连接后 hello 再带一次 gm 兜底(兼容旧版客户端),不一致由房主踢出并提示双方模式名。round 走可靠通道正常先到;丢了由 sync 的 rn 超前兜底。rn 落后的旧快照整段跳过,防局号回退与二次重置。location.reload(),而是按「停输入 → 关连接 → 清实体 → 清状态 → 清 UI → 重建菜单 3D」复位。连接关闭走 Net.close()(先 DataChannel 后 PeerConnection),对端通过 onClose 各自软重置。foeVis 子弹不与飞机/防空车碰撞。命中特效出现前,它已经从目标身上穿过去了 —— 移除时机是 hit 到达的那一帧(killBulletFx 按「射手 + fid」精确匹配)。hit(e 通道)可能先于一条更早生成的 sync(s 通道)到达,旧快照会把血量短暂覆盖回去(约 50~150ms),随后被下一个快照纠正。p,接收端的子弹按自己的积分位置渲染。延迟 100ms、弹速 600 m/s 就是 60m 的视觉差。hitRep,房主按 rid:fid:id 分别去重结算,互不压制。