Putting it together¶
Let's trace what happens when the client sends a message:
- Client calls
self.send({"action": "message", "text": "Hello!"}). - repod serializes the dict with msgpack, adds a 4-byte length header, and sends it over TCP.
- On the server, the channel's read loop receives the bytes, parses the frame, and puts the dict in a queue.
- The server's process loop takes the dict from the queue and calls
channel._dispatch(data). _dispatchlooks atdata["action"](which is"message"), findsNetwork_messageon the channel, and calls it.- Your
Network_messagemethod runs with the full dict as argument.
The same flow works in reverse: a channel calls self.send(...), and the client's pump() dispatches it to the matching Network_{action} on the ConnectionListener.
Fallback handler¶
If a message arrives with an action that has no matching Network_{action} method, repod calls network_received() instead. Override it to catch unhandled messages:
class GameChannel(Channel):
def Network_move(self, data: dict) -> None:
...
def network_received(self, data: dict) -> None:
print(f"Unhandled action: {data.get('action')}")
This works on both Channel and ConnectionListener.
Adding state to channels¶
Channels are regular Python objects. Add any attributes you need in __init__:
class PlayerChannel(Channel):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.username = "anonymous"
self.score = 0
self.position = (0, 0)
def Network_join(self, data: dict) -> None:
self.username = data["username"]
def Network_move(self, data: dict) -> None:
self.position = (data["x"], data["y"])