Files
polylan-submitter/polylan_submitter/place/wire.py
T

48 lines
2.1 KiB
Python

"""Compact wire format for the place websocket.
Same convention as clock/wire.py (kept separate on purpose - the two games
don't share code, mirroring how the clock and place apps don't share code
either). Each message is a short string: a one-letter type tag followed by
'|'-separated fields, in a fixed order the client already knows from this
table:
s|<next>|<width>|<height>|<end>
initial personal state, once per connection
(next = epoch ms cooldown ends, or empty if free now;
end = epoch ms placement ends, or empty if never)
p|<x>|<y>|<c> a pixel was placed - broadcast to everyone,
including the placer (no separate ack needed)
k|<next> your own placement was accepted; carries your
new cooldown end
r|c|<next> rejected: still on cooldown
r|a rejected: not logged in
r|i rejected: invalid x/y/color
r|d rejected: placement is over (past <end>)
e|<width>|<height>|<end>
an admin edited this event's settings (grid size
and/or end time) - broadcast to everyone.
If the size changed, a client should re-fetch
/api/.../place/canvas rather than try to remap its
local pixels.
w placement just ended - broadcast to everyone at
<end> (see tasks.py). Clients play the wipe wave
and blank their board.
Client -> server has exactly one message shape, the same as the broadcast:
p|<x>|<y>|<c>
Whether the game is enabled at all for this event (EventGame.enabled) is
checked once, at connect time - a connection is refused outright rather than
accepted-but-disabled, so there's no "disabled" case in this wire format.
"""
DELIMITER = "|"
def encode(*parts) -> str:
return DELIMITER.join("" if p is None else str(p) for p in parts)
def to_epoch_ms(dt) -> int:
return int(dt.timestamp() * 1000)