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.
You can probably use
Phoenix.Presenceto handle all of those headache.When a user joined the websocket, you can store the user's state data in the socket connection itself.
Example:
Then to make sure that
Phoenix.Presencerecord those data. You need to do something like thisExample:
The
"presence_state"channel will broadcast the list of users that are subscribed to the channel.But let say you wanna do something more specific like only displaying the number of people in the channel. So probably you can also do something like this in the
handle_info(:after_join..Example:
You need to make sure that your client is listening to the
"room:main:total_users"channel aswell in order to get the broadcasted data.Hopefully it helps. Cheers!