Porting Couter Strike to Fiber network

最近我在尝试移植一个简单的 1v1 射击游戏(霓虹地图版的 counter-strike),不同的地方在于:每次命中造成的伤害,都通过 Fiber 支付通道去做真实的结算。

  • 匹配前双方需要开通好 fiber 的通道
  • 匹配好之后,双方各创建 4 张 hold 发票(带 SHA-256 哈希)
  • 通过我的服务器判定谁命中,然后释放对应 preimage
  • 输家为每一发命中真实付费给赢家
  • 玩家无法拒绝为自己吃到的伤害付钱

服务器 64 tick/秒,伤害只由服务器裁决。目前跑在 testnet 上。



demo 地址

https://arena.retric.uk

源代码:GitHub - RetricSu/openstrike-fiber-arena · GitHub

限制

需要说明,这是个展示"Fiber 支付 + 游戏事件联动"的技术 demo,并不是完整的防作弊的竞技平台。这个 demo 想要提供的能力很简单:愿意信任中心化服务器来运行游戏 + 让玩家使用 fiber 支付伤害,适合快速把一个游戏跑起来,又能最快接入 fiber 通道。

但他的限制也很明显:需要信任服务器裁决实时对战的结果。伤害是否打中、谁需要向谁付款,这都是服务器说了算。 同时,服务端是简单的"输入可信"模型,客户端发送输入帧,服务端裁决基于的是客户端自愿上报的瞄准和开火。服务器目前并没有对客户端的输入帧做进一步的校验和反作弊,所以不会判断是不是外挂、是否合理等等。这些工作对于要做生产级的真实对战游戏平台项目来说,是必须要做好的细活。

如何试玩

这是一个偏技术向的 demo,需要一点动手能力:

  1. Rust 工具链,然后构建客户端:
git clone --recursive https://github.com/RetricSu/openstrike-fiber-arena
cd openstrike-fiber-arena
cargo build --release --features desktop --bin arena-desktop

嫌麻烦的也可以直接下载 pre-release里已经编译好的二进制:Release v0.1.0 · RetricSu/openstrike-fiber-arena · GitHub

  1. 一个你自己的 Fiber 测试网节点(FNN),最简单用 fiber-pay:
npm install -g @fiber-pay/cli
fiber-pay node start
  1. 去 testnet 水龙头领点 CKB:https://faucet.nervos.org
  2. 和对手的 FNN 之间开一条支付通道(双方各存一点钱进去)

开始对战

双方各自运行客户端(把 --fiber-rpc 指向你自己节点的 RPC 端口,默认 127.0.0.1:8227):

玩家 A 建房:

./target/release/arena-desktop --name <你的名字> \
  --matchmaker https://arena.retric.uk \
  --fiber-rpc http://127.0.0.1:8227 --dev-arena

会打印一个 8 位房间码。

玩家 B 加入(用 A 的房间码):

./target/release/arena-desktop --name <你的名字> \
  --matchmaker https://arena.retric.uk --room <CODE> \
  --fiber-rpc http://127.0.0.1:8227 --dev-arena

两人都就位后,hold 发票握手完成,比赛自动开始,打起来吧!

小提示

  • 如果你本机有系统级 TUN 代理(如 Clash/mihomo)会吞 UDP,加参数 --local-bind <你的物理网卡IP>:0
  • 单人想先试试的话,可以在一台机器上起两个 Fiber 节点 + 两个客户端,自己打自己
  • 这是 testnet 演示,开放房间,来去自由

有任何问题欢迎回帖反馈!:rocket:

20 Likes

Overwhelming. For me, CS started out as a Half-Life mod, and at some point, waypoint files for bots became available, which gradually got better and better. Here, the future—in the form of NervosNetwork Fiber—meets a video game that has made history. It’s certainly one of the most popular multiplayer network shooters. I’m a little speechless.

7 Likes

我觉得这是一个很棒的Fiber integration demo.

在我的理解这应该是一个 权威性/中心化的 1v1 session-based game,外接 Fiber 作为 事件驱动结算侧车。

简单梳理一下:

  • 实时对战仍然是传统 UDP/Renet, Renet 仍然要负责延迟敏感的输入和快照;
  • Fiber 负责把预先授权的支付条件,在服务器判定伤害后兑现。且 Fiber 恒仅处理 pre-authorised value transfer;
  • 热路径 和 结算路径 分离。
  • 托管边界是服务器和 matchmaker 不接触玩家的 wallet key,也不代理玩家的 FNN RPC
  • 伤害累计每跨过一个 25点伤害的 bucket,就释放一张对应 invoice 的 preimage
  • 服务器在伤害发生后释放 preimage,payer 到这个时候也没有了拒付点,那么既然对应 preimage 被释放,付款就无法被输家撤回。

这已经是一个相当自洽的 MVP 模型和优秀的 base line 了. 也对我有一些模型设计上的启发。

最近两个月,我也在频繁思考 gaming、通道网络,以及 Xuejie-style 的 CKB-VM-isomorphic off-chain session runtime 应该如何更自然地结合。期间有一些相似的问题同样困扰着我,所以我想冒昧在这里分享一些思考:

0. 如何尽可能降低 channel onboarding 的存在感?

因为在这个相对简单的模型中,双方仍需要:

  • 各自运行本地 FNN;
  • 准备测试币;
  • 找到对方身份;
  • 事先建立直接通道;
  • 确保双向的 outbound 流动性;
  • 启动游戏客户端。

对于普通玩家可能仍然不可接受。

我好奇在未来我们是否可能把这些步骤压缩为无感的流程?:

Join Room
→ embedded non-custodial wallet/FNN
→ automatic route and liquidity check
→ just-in-time channel or LSP provisioning
→ authorise a bounded session cap
→ start game

1. 服务端的oracle风险怎么处理?

在类似方案中,host server 可能掌握所有 preimages, 那么服务器本身、管理员或 签名设施 被攻破的时候,攻击者可以在没有合法伤害的情况下释放全部 preimages,将每位玩家的整个 pre-authorised cap 结算出去。当然 Cap 的设计使损失有限度,但没有移除信任。

我考虑过一些方案, 一些可能的 hardening 方向包括:

  • 游戏服务器结算signer 分离;
  • 使用 短生命周期的、match-based keys (当然单纯增加签名者数量并不等于直接证明游戏结果正确。只是所有危机状态下的爆炸半径。加密通信上用的各种session key算法可能是一种参考。)
  • 持久化 append-only settlement log作证;
  • 扩展字段,让每次 release 绑定 sequence、match state commitment 等;
  • 为 争议审计 设法预先保留 确定性的输入转译

2.多方可扩展性的问题

如果多人模式继续沿用成对的直接通道,那么随着参与者数量增加,最终很容易逼近 O(n^2) 的 通道关系。那么产品化的阶段可能就要继续扩展前面说的无感化流程,引入 LSP/hub、嵌入式 wallet/FNN、自动流动性供应 和 round-level 或者match-level 的信用抽象。

在CS2的 5v5 场景里,严格来说可能未必需要十名玩家之间全部互连,但每位玩家至少可能与五名对手发生经济关系,仍然会产生明显的 对间流动性(pairwise liquidity)负荷 和 通道管理负荷。

LSP 或 hub 可以轻松把 通道拓扑 从近似 O(n^2) 降到接近 O(n)),但它不一定自动解决这个 shared bounded liability across multiple possible payees 的问题。目前我暂时还没有想到完美的方案。

3. Per-event invoice 怎么处理长时间、大量事件的游戏?

我之前做了一点调研, Counter Strike 2 每一个match的平均 kill 数量 还挺大的

当前汇总约:

37 million matches
748.6 million rounds
5.4 billion kills

用这些经过取整的总数计算​ ≈ 20.2 rounds/match​ 和 146 kills/match

因此,一场普通 match 的 kill 数量大约是 146

我们假设 CS2 玩家每 round 从 100 HP 开始,且 round 内通常不回血。若粗略把每次 kill 对应 100 HP depletion 切成四个 25-damage buckets,触发全局总致命伤害 可以非常粗略得视为 584 个 25-HP threshold-crossing 事件,

换句话说以FPS为例的话, 真实生产环境中的 FPS 一局可能产生数百次可结算伤害的事件。同样的估算方法在Valorant中适用的话, 那么 5V5 的 match 会很轻松突破上千次threshold-crossing 事件。

这在小规模的demo/局域网游戏session中完全没有问题,但是如果有发行方/运营方想要商业运营的时候,

为每次事件预建一张 hold invoice 会很显著地增加:

  • TLC 槽位占用压力
  • 流动性被长期锁定的问题
  • 赛前初始化延迟
  • 取消、超时与故障恢复复杂度
  • 恶意占用资源与 griefing 攻击面

4. 是否可以采用 round-level 或 short-epoch netting?

在规模化场景里,一个可能的取舍是采用 round-level netting,或者更短的 epoch-based netting,而不是为每个 damage bucket 都立即建立独立的最终支付。

例如,玩家双方先各自授权一个最大 exposure:

Player A max liability: 4 units
Player B max liability: 4 units

在 round 或 epoch 内,系统只维护一份签名的 cumulative economic ledger:

A inflicted 75 damage on B → B owes A 3 units
B inflicted 50 damage on A → A owes B 2 units

Gross settlement:
B → A: 3
A → B: 2

Net settlement:
B → A: 1

这样,五次 gross damage-bucket settlements 最终可以压缩成一次 net settlement。

整体结构可以变为:

Renet hot path
→ authoritative game simulation
→ signed cumulative economic checkpoint
→ bounded net obligation
→ Fiber settlement

玩家仍然可以在 session start 时预授权最大 exposure,但过程中不必为每一个细粒度事件立刻消费一张独立 invoice。

如果最终净额必须支持 0 ~ N 之间的任意值,可以考虑 binary-denominated reservations做为一个一个trick,例如:

1, 2, 4, 8, 16, ...

从而用 O(log N) 张 hold invoices 表示较大的金额范围。

5. Liquid Cells 给我的另一个启发:checkpoint 是可重建的

我之前参与了这篇讨论:

这其中对我最有启发的,不只是 netting 本身,而是一个更一般的原则:

一个 state commitment 最好不仅仅是 opaque hash,还应当存在足够的 underlying data,使其他参与者可以重建并验证它。

如果把这个原则应用到这个模型,一个 economic checkpoint 也许可以包含:

struct EconomicCheckpoint {
    match_id,
    epoch_id,
    sequence,
    previous_checkpoint_hash,

    gross_debits,
    gross_credits,
    net_balances,

    consumed_reservations_bitmap,
    game_transcript_root,
    expires_at,

    server_signature
}

这样,客户端、watchtower 或 replay service 至少可以验证:

  • checkpoint sequence 是否连续;
  • debit/credit 是否守恒;
  • net balance 是否计算正确;
  • 某张 invoice 是否已经被消费;
  • 某次 release 是否重复;
  • settlement amount 是否超过 session cap;
  • server restart 后是否可以从最后一个 checkpoint 恢复。

这仍然不能直接无信任地证明某一枪是否真正命中,也不能自动移除 authoritative server,但至少可以让历史变得可审计、可重建。

6.当然Netting 本身也有代价

Per-event 结算天然的优点是,每个 damage bucket 一旦释放,就获得独立finality。Netting 会把 finality 推迟到 round 结束 或 epoch checkpoint交界。这样一来,如果服务器在 checkpoint 前崩溃或消失,玩家之间可能存在尚未结算的经济状态。

因此如果 match netting 可能过于粗糙,一个合理的折衷也许是:

  • round-level checkpoint?
  • 或者每 10–30 秒一个 short epoch;
  • 持久化最新 checkpoint 和 reservation state;
  • server restart 后从最后一个有效 checkpoint 恢复;
  • 对未完成 epoch 使用明确的 timeout、forfeit 或 rollback rule。
6 Likes

I can already imagine games where, as a police gamer, you earn 1 SAT BTC (RGB++) for defusing the bomb within the round time limit, or 1 SAT BTC for planting a bomb on the terrorist gamer side that detonates during the round. Or for a knife kill either side. These situations present particularly demanding challenges in CS.

5 Likes

感谢你这么细致的回复。你对这个 demo 技术上用到的东西的理解完全正确。我感觉游戏确实是个不错的用例,至少我能看到在 CKB 上对游戏讨论的热情是很高的。

针对 CS 这个 kill/match 数的调研很有意思,给了我很多的启发。反过来想,要移植这样的游戏,做full on-chain game几乎是不可能的,offchain显然是更合适的路线。

对于这种实时游戏,确实5 vs 5 player 之间的流动性会是通道网络上一个不小的挑战。我还没有开始往这个方向去做一些尝试,这个 demo 或许会是个好的起点。往这个方向去深入做一做试验应该会很有意思。

另外对服务器的信任的可验证性和可审计性,我觉得也是很合理的需求。借着这个机会,我也整理了下目前有的一些资料提到了 fiber 的文档站上 Game Payment Patterns 。显然现在我们才刚刚开始做这些探索,我把之前 quake 做过的一个 demo 展示 Locked stakes, provable oracle 这样的模式也加了进去,希望对后面的探索者能有些帮助。这个 cs 的例子还没有走到 provable oracle 这一步,我觉得大概率 fiber 上的游戏会有对不同信任级别的需求。有些游戏需要更好的体验,有些需要更好的信任,有些需要牺牲体验换取免信任,有些需要适度牺牲完全的免信任去提升游戏体验——大家可以自由选择不同的技术方案、做 trade-off 取舍。但是一些共同的挑战,比如流动性管理、通道网络的复杂度,这些也需要在考虑提供不同解决方案的同时,有一些共通的技术能够解决。另外值得注意的是,现在我们显然还没有完全 trustless 的方案,对 fiber game 的探索这还是个空白,它也很硬核。

还有一个方向我觉得是 gaming 比较有意思的方向,是做给 agent 用的游戏网络。 agent 的速度比人更快,它们在游戏内的资金上的交互需求会比人类玩家高出一个量级,通道网络在这种并发下容易展示出更多的低延迟、微支付上的技术优势,也许是个不错的方向。我已经能看到 https://agentank.ai/ 这样的非常有趣的例子,我相信我们离指挥 agent 去打比赛、赢赏金的赛博朋克的未来已经近在咫尺了。crypto 应该要能在其中发挥自己的作用。

6 Likes