Protocol/Server: implement STATE_DELTA + PART partitioning and per-tick minimal changes
- Protocol: SnakeDelta structure; build_state_delta(_body); build_part - Server: compute per-snake changes (move/grow/blocked-shrink), apples diffs - Partition large deltas by snake changes with apples in first part; use update_id - Send STATE_DELTA when under MTU; else PART packets referencing STATE_DELTA
This commit is contained in:
@@ -297,3 +297,80 @@ def build_state_full(
|
||||
body.append(ax & 0xFF)
|
||||
body.append(ay & 0xFF)
|
||||
return header + bytes(body)
|
||||
|
||||
|
||||
# --- State Delta ---
|
||||
|
||||
@dataclass
|
||||
class SnakeDelta:
|
||||
snake_id: int
|
||||
head_moved: bool
|
||||
tail_removed: bool
|
||||
grew: bool
|
||||
blocked: bool
|
||||
new_head_x: int = 0
|
||||
new_head_y: int = 0
|
||||
direction: Direction = Direction.RIGHT
|
||||
|
||||
|
||||
def build_state_delta_body(
|
||||
*, update_id: int, changes: Sequence[SnakeDelta], apples_added: Sequence[Tuple[int, int]], apples_removed: Sequence[Tuple[int, int]]
|
||||
) -> bytes:
|
||||
body = bytearray()
|
||||
body.extend(int(update_id & 0xFFFF).to_bytes(2, "big"))
|
||||
# snakes
|
||||
body.extend(quic_varint_encode(len(changes)))
|
||||
for ch in changes:
|
||||
body.append(ch.snake_id & 0xFF)
|
||||
flags = (
|
||||
(1 if ch.head_moved else 0)
|
||||
| ((1 if ch.tail_removed else 0) << 1)
|
||||
| ((1 if ch.grew else 0) << 2)
|
||||
| ((1 if ch.blocked else 0) << 3)
|
||||
)
|
||||
body.append(flags & 0xFF)
|
||||
body.append(int(ch.direction) & 0x03)
|
||||
if ch.head_moved:
|
||||
body.append(ch.new_head_x & 0xFF)
|
||||
body.append(ch.new_head_y & 0xFF)
|
||||
# apples added
|
||||
body.extend(quic_varint_encode(len(apples_added)))
|
||||
for ax, ay in apples_added:
|
||||
body.append(ax & 0xFF)
|
||||
body.append(ay & 0xFF)
|
||||
# apples removed
|
||||
body.extend(quic_varint_encode(len(apples_removed)))
|
||||
for rx, ry in apples_removed:
|
||||
body.append(rx & 0xFF)
|
||||
body.append(ry & 0xFF)
|
||||
return bytes(body)
|
||||
|
||||
|
||||
def build_state_delta(
|
||||
*, version: int, seq: int, tick: int, update_id: int, changes: Sequence[SnakeDelta], apples_added: Sequence[Tuple[int, int]], apples_removed: Sequence[Tuple[int, int]]
|
||||
) -> bytes:
|
||||
header = pack_header(version, PacketType.STATE_DELTA, 0, seq, tick & 0xFFFF)
|
||||
body = build_state_delta_body(update_id=update_id, changes=changes, apples_added=apples_added, apples_removed=apples_removed)
|
||||
return header + body
|
||||
|
||||
|
||||
def build_part(
|
||||
*,
|
||||
version: int,
|
||||
seq: int,
|
||||
tick: int,
|
||||
update_id: int,
|
||||
part_index: int,
|
||||
parts_total: int,
|
||||
inner_type: PacketType,
|
||||
chunk_payload: bytes,
|
||||
) -> bytes:
|
||||
header = pack_header(version, PacketType.PART, 0, seq, tick & 0xFFFF)
|
||||
body = bytearray()
|
||||
body.extend(int(update_id & 0xFFFF).to_bytes(2, "big"))
|
||||
body.append(part_index & 0xFF)
|
||||
body.append(parts_total & 0xFF)
|
||||
body.append(int(inner_type) & 0xFF)
|
||||
# include the chunk payload bytes
|
||||
body.extend(chunk_payload)
|
||||
return header + bytes(body)
|
||||
|
||||
Reference in New Issue
Block a user