I've finally had the chance to use channels for a project (hack day
multiplayer game - hope to release and blog about it some time soon), and I
wanted to document some of the rough edges I hit and ask some questions
about them.
Specifically though, I find the mapping of payload.type to a method on the
consumer confusing and somewhat brittle.
The design we went with was to have a
PlayerConsumer(AsyncWebsocketConsumer) and a GameConsumer(SyncConsumer)
running as a worker. The GameConsumer starts the engine in a thread, and
lets the engine fetch the channel layer. The player and engine then
communicate like so:
# PlayerConsumer: receive game state updates
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
# PlayerConsumer: publish player joining event to game
await self.channel_layer.send(
'game_engine',
{
'type': 'player.new',
'player': self.user.email,
'channel': self.channel_name,
}
)
# GameConsumer: publish state update
async_to_sync(self.channel_layer.group_send)(
self.group_name,
{
'type': 'game_update',
'state': state_json,
}
)
This works, provided PlayerConsumer has a method:
async def game_update(self, event):
And the GameConsumer has a method:
def player_new(self, event):
But if these two consumers are in completely different code bases/packages,
there is no real way to know what the interface is between these consumers.
Worse, it's extremely easy for a bad actor to crash a listening consumer.
Either of the following events will crash the consumer receiving the
message:
await self.channel_layer.send(
'game_engine',
{
'type': 'i_am_a_bad_consumer'
}
)
async_to_sync(self.channel_layer.group_send)(
self.group_name,
{
'type': 'gme_update', # typo
'msg': 'hi',
}
)
I'm not so concerned about arbitrary asgi applications gatecrashing my app
as I ultimately have control over what is going to run. But each consumer
participating in a group must support the superset of all message types
that may be sent to that group if it wants to avoid crashing. And it has to
support the superset of message types without being able to discover what
they might be. Oh, and they're strings that **might** match a method I have
if periods are converted to underscores.
This is all very good for adhoc eventing, but I think at some point you're
going to want to publish an explicit Interface. A GroupInterface might
define a collection of message types that are valid for members
participating in that group, and then a consumer could subscribe to
**that** rather than just a name. The interface members might be optional
or required, I haven't thought that far ahead, but at least each member
would be known. Attempting to publish an unknown message to a group would
crash the **sender** rather than the **receiver**.
Has anything like this come up before?
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/939460e7-45da-4dd0-afb7-f386274faea1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.