40 lines
1.7 KiB
Python
40 lines
1.7 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> initial personal state, once per connection
|
|
(next = epoch ms cooldown ends, or empty if free now)
|
|
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
|
|
e|<width>|<height> an admin resized the grid for this event -
|
|
broadcast to everyone. A client that sees this
|
|
should re-fetch /api/.../place/canvas rather than
|
|
try to remap its local pixels.
|
|
|
|
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)
|