weopqrst/moonroll/protocol does not have a README file

    ActiveFrame

    pub(all) struct ActiveFrame {
    player : Int
    active : Bool
    } derive(Eq)

    某个玩家上线 / 掉线的声明。active = 1 是上线,0 是掉线。

    掉线本身不是协议层的判断(网络断没断只有上层知道),这里只负责把 上层的决定可靠地同步给其他对等体,让所有人用同一个在线集合推进确认帧。

    ActiveFrame::equal

    fn ActiveFrame::equal(ActiveFrame, ActiveFrame) -> Bool

    ActiveFrame::new

    fn ActiveFrame::new(player : Int, active : Bool) -> ActiveFrame

    ActiveFrame::not_equal

    fn ActiveFrame::not_equal(x : ActiveFrame, y : ActiveFrame) -> Bool

    ChecksumFrame

    pub(all) struct ChecksumFrame {
    frame : Int
    hash : UInt64
    } derive(Eq)

    ChecksumFrame::equal

    ChecksumFrame::new

    fn ChecksumFrame::new(frame : Int, hash : UInt64) -> ChecksumFrame

    ChecksumFrame::not_equal

    fn ChecksumFrame::not_equal(x : ChecksumFrame, y : ChecksumFrame) -> Bool

    ConfirmFrame

    pub(all) struct ConfirmFrame {
    frame : Int
    } derive(Eq)

    ConfirmFrame::equal

    ConfirmFrame::new

    fn ConfirmFrame::new(frame : Int) -> ConfirmFrame

    ConfirmFrame::not_equal

    fn ConfirmFrame::not_equal(x : ConfirmFrame, y : ConfirmFrame) -> Bool

    InputFrame

    pub(all) struct InputFrame {
    player : Int
    frame : Int
    inputs : Array[UInt64]
    } derive(Eq)

    紧凑字节协议:输入帧 / 输入区间帧 / 确认帧 / 校验和帧 / 在线状态帧 / 全量状态帧 / 状态请求帧。

    线上格式(小端,varint 为 LEB128):
    Input : [0x01][varint player][varint frame][varint count][varint bits] * count Confirm : [0x02][varint frame] Checksum : [0x03][varint frame][8 bytes hash] Active : [0x04][varint player][varint active] InputSpan: [0x05][varint player][varint start][varint count][varint mask][varint bits] * popcount(mask) Snap : [0x06][varint frame][varint len][byte] * len SnapAsk : [0x07][varint frame]

    Input 里的 inputs[i] 是玩家 player + i 在同一帧的输入位图。 常见情况是「自己一个人的一帧」,即 count = 1;多人同帧批量补齐时 count 才会大于 1。

    InputSpan 是同一个玩家的一段连续帧:count 是帧数,mask 的第 i 位 表示 start + i 这一帧的输入是否随包发出,未置位的帧沿用包里前一个值。 重发冗余输入时,一个人一帧一条 Input 要 4 字节,一段 8 帧且输入只变两次 时 InputSpan 只要 7 字节。

    InputFrame::equal

    fn InputFrame::equal(InputFrame, InputFrame) -> Bool

    显式声明 derive(Eq) 生成的方法提升为普通方法(消除 W0079)。

    InputFrame::new

    fn InputFrame::new(frame : Int, inputs : Array[UInt64]) -> InputFrame

    默认把 inputs[0] 当作玩家 0 的输入。

    InputFrame::new_from

    fn InputFrame::new_from(player : Int, frame : Int, inputs : Array[UInt64]) -> InputFrame

    inputs[i] 是玩家 player + i 的输入。

    InputFrame::not_equal

    fn InputFrame::not_equal(x : InputFrame, y : InputFrame) -> Bool

    显式声明 derive(Eq) 生成的方法提升为普通方法(消除 W0079)。

    InputSpanFrame

    pub(all) struct InputSpanFrame {
    player : Int
    start : Int
    count : Int
    mask : UInt64
    values : Array[UInt64]
    } derive(Eq)

    同一个玩家的一段连续帧输入。

    count 是帧数,mask 的第 i 位表示 start + i 帧的输入是否在 values 里显式给出;未置位表示「与包里前一个显式值相同」。首帧必须置位, 否则这段就没有起点值。

    它是 Input 的补充而不是替代:Input 适合「同一帧、多个玩家」, InputSpan 适合「同一个玩家、多帧重发」。

    InputSpanFrame::equal

    InputSpanFrame::expand

    fn InputSpanFrame::expand(self : InputSpanFrame) -> Array[UInt64]

    展开成逐帧输入,长度等于 count。

    未置位的帧沿用包里前一个显式值;因此解码合法的段必定以显式帧开头。

    InputSpanFrame::not_equal

    fn InputSpanFrame::not_equal(x : InputSpanFrame, y : InputSpanFrame) -> Bool

    InputSpanFrame::of

    fn InputSpanFrame::of(player : Int, start : Int, inputs : Array[UInt64]) -> InputSpanFrame

    由「玩家 player 从 start 起的连续帧输入」构造。

    首帧总是显式,之后只有与前一帧不同才显式——所以「按住同一个键 8 帧」 在线上只是一个值加一个掩码。超过 MAX_SPAN 帧的部分会被截断。

    InputSpanFrame::raw

    fn InputSpanFrame::raw(player : Int, start : Int, inputs : Array[UInt64]) -> InputSpanFrame

    逐帧全给的版本:掩码全 1,不做任何折叠。

    用来把「区间帧」和「变化掩码」两个优化的收益分开测—— raw 与 of 的差就是掩码省下的那部分。

    Message

    pub(all) enum Message {
    Input(InputFrame)
    Confirm(ConfirmFrame)
    Checksum(ChecksumFrame)
    Active(ActiveFrame)
    Span(InputSpanFrame)
    Snap(SnapFrame)
    SnapRequest(SnapRequestFrame)
    } derive(Eq)

    Message::equal

    fn Message::equal(Message, Message) -> Bool

    Message::not_equal

    fn Message::not_equal(x : Message, y : Message) -> Bool

    SnapFrame

    pub(all) struct SnapFrame {
    frame : Int
    state : Bytes
    } derive(Eq)

    某一帧的完整状态,用于分叉之后的全量重同步。

    state 是 Sim::save 的原始字节,协议层不解释它——只有跑同一个 Sim 的对等体才读得懂。所以它是「可信对端之间」的帧,不是公开数据: 接收方要么把它原样交给 Session::resync_from,要么丢掉。

    SnapFrame::equal

    fn SnapFrame::equal(SnapFrame, SnapFrame) -> Bool

    SnapFrame::new

    fn SnapFrame::new(frame : Int, state : Bytes) -> SnapFrame

    SnapFrame::not_equal

    fn SnapFrame::not_equal(x : SnapFrame, y : SnapFrame) -> Bool

    SnapRequestFrame

    pub(all) struct SnapRequestFrame {
    frame : Int
    } derive(Eq)

    索取某一帧的完整状态。分叉被检测到之后,落后的一端发这个包, 由持有权威状态的那一端回一条 Snap。

    单独一个请求帧而不是把请求塞进 Checksum,是因为「报告不一致」和 「请求重同步」是两件事:前者可以每帧发,后者只该在对端明确落后时发一次。

    SnapRequestFrame::equal

    SnapRequestFrame::new

    fn SnapRequestFrame::new(frame : Int) -> SnapRequestFrame

    SnapRequestFrame::not_equal

    fn SnapRequestFrame::not_equal(x : SnapRequestFrame, y : SnapRequestFrame) -> Bool

    MAX_SNAPSHOT

    let MAX_SNAPSHOT : Int

    一条 Snap 帧能携带的状态字节上限(64 KiB)。

    全量状态是「一整帧的世界」,正常只有几 KB;60fps 下再大的快照也不该 塞进单包。给一个明确上限,解码端就能在读到 len 的瞬间拒绝畸形包, 而不是为它分配一块任意大的内存。

    MAX_SPAN

    let MAX_SPAN : Int

    一段帧的输入上限:掩码是 64 位,再多就用两个包。

    decode

    fn decode(buf : Bytes) -> Message?

    decode_batch

    fn decode_batch(buf : Bytes) -> Array[Message]

    encode

    fn encode(msg : Message) -> Bytes

    encode_batch

    fn encode_batch(msgs : Array[Message]) -> Bytes

    Source Files