For the complete documentation index, see llms.txt. This page is also available as Markdown.

网络基础

All Out 以多人为先,但 CSL 的设计使你几乎可以像在单人模式下一样编写游戏逻辑。

CSL 会在客户端运行这段游戏逻辑以提供即时反馈,并在服务器上运行以保证权威性。客户端会定期与服务器的权威场景状态进行一致化。你不需要编写 RPC,也不需要单独的网络生成代码。

编写一条游戏逻辑路径

伤害、奖励、生成、移动、随机选择和声音调用都应在共享的游戏逻辑路径中运行。

将每个玩家的状态保存在 Player 或该玩家拥有的其他对象上。只有在确实是共享数据时,才使用全局场景状态。

玩家执行检查

Player_Base 有两个执行检查:

检查
使用

is_local_or_server()

玩家 UI 以及它产生的输入。仅在该玩家的 ao_late_update.

is_local()

仅对特定玩家生效的视觉覆盖,例如只对其拥有者隐藏某个对象。

所有玩家 UI,包括装饰性 UI,都应放在 is_local_or_server()下。切勿仅将游戏逻辑或 UI 状态存放在 is_local();下一次服务器同步会将其替换。

Player :: class : Player_Base {
    inventory_open: bool;

    ao_late_update :: method(dt: float) {
        if is_local_or_server() {
            rect := UI.get_safe_screen_rect()
                .top_right_rect()
                .grow(35, 90, 35, 90)
                .offset(-100, -45);
            bs := UI.default_button_settings();
            ts := UI.default_text_settings();

            if UI.button(rect, bs, ts, "Items").clicked {
                inventory_open = !inventory_open;
            }
        }

        if is_local() {
            // 在这里应用仅对玩家生效的可见性,但不要更改字段。
        }
    }
}

如果本地视觉覆盖因一致化而发生变化,请在 ao_on_state_sync() 中根据同步状态重新计算,或者每帧都应用它。

音效

调用 SFX.play 应与事件一起在同一条共享游戏逻辑路径中运行。服务器结果到达时,引擎会去重已预测的播放。要只为某个玩家播放声音,请设置 SFX_Desc.specific_to_player;不要将该调用包裹在 is_local().

避免每帧的状态抖动

每个会变化的游戏字段都可能参与网络状态差异。不要通过每帧减去 dt 来存储倒计时;如果同样的状态可以用固定时间表示,就不要这样做。

只存储一次转换时间,并派生剩余时长:

这会在截止时间变化时同步它,而不是每帧同步一个新的计时器值。同样的模式也适用于冷却时间、临时效果以及计划中的状态变化。

排查不一致

检查:

  • 被隐藏在 is_local()Game.is_server().

  • 存放在全局变量中的按玩家值。

  • 用于播种游戏随机性的仅本地数据。

  • 本可用固定时间戳表示,却每帧都在变化的状态。

相关文档

最后更新于