I'm trying to use Phoenix Channels as a dead-simple game server. For the prototype, its only purpose is to sync player positions. However, before that happens, every client must be aware of all other clients' existence.
When a new client joins the room, I broadcast that to all present clients from my RoomChannel
:
@impl true
def join("room:main", _payload, socket) do
send(self(), :after_join)
{:ok, socket}
end
@impl true
def handle_info(:after_join, socket) do
broadcast_from!(socket, "user_join", %{user_id: socket.assigns.user_id})
{:noreply, socket}
end
These existing clients react to the message and spawn a new player object. However, the new user has no idea who else is in the room with them, as they joined last and didn't get sent any such information. I'm trying to rectify that.
My plan is to modify my code to something like this:
@impl true
def join("room:main", _payload, socket) do
Agent.update(active_users_agent, fn(list) -> [list | socket.assigns.user_id] end, 5000)
send(self(), :after_join)
{:ok, socket}
end
@impl true
def handle_info(:after_join, socket) do
push(socket, "room_data", Agent.get(active_users_agent, fn(list) -> list end))
broadcast_from!(socket, "user_join", %{user_id: socket.assigns.user_id})
{:noreply, socket}
end
In other words, I want to maintain a list of joined users and send it to the new user upon joining (and add them to it). Whether I use an Agent, a GenServer, or something else doesn't matter overly much.
The problem is, I don't know where I can initialize that Agent, as my RoomChannel
doesn't have an init()
callback; nor how to pass it to the join()
and handle_info()
callbacks. What's a working—or better yet, idiomatic—way to do this?
P.S. It's worth noting I want this stored in memory, not in a database.