Multiple broadcast from GenServer to Phoenix Channel seem to not work

Hey guys first post here!
It seems that I’m missing something because I can’t get this problem to work.
I’m building a game matchmaking were for example two people want to play and I generate one ID for them to join a channel.
I made all clients join with differents tokens, and a worker sends it down the rabbithole

def join("search:" <> token, message, socket) do
    pid = Worker.search(%{:size => 2, :client => token})
    {:ok, %{:pid => :wait}, socket}
end

In the end i have a list of tokens in a list called clients and I iterate over it to broadcast to their channels

def handle_call({:join, client}, _from, %{size: size, clients: clients} = state) do
  state = %{state | clients: [client|clients]}
  if length(clients) == (size - 1) do
    lobby_token = :crypto.strong_rand_bytes(40) |> Base.url_encode64 |> binary_part(0, 40)
    %{size: size, clients: clients} = state
    broadcast(clients, lobby_token)
    {:reply, {:ok, self()}, state}
  else
    {:reply, {:error, :full}, state}
  end
end

defp broadcast([client | clients], lobby_token, count) do
  BackendWeb.Endpoint.broadcast("search:#{client}", client, %{token: lobby_token})
  broadcast(clients, lobby_token, count)
end

defp broadcast([], lobby_token, count), do: nil

The objective is to call again the channel for the client and send them the lobby_token that is goin to be another phoenix channel.
This handle_out lives inside search:* channel

def handle_out(event, payload, socket) do
    push socket, event, payload
    {:noreply, socket}
end

Finally the Js part

this.channel = this.socketService.connectTo(`search:${this.token}`);
this.channel.on(this.token, payload => {
  console.log(payload);
})
this.channel.join().receive("ok", response => {
  console.log(response);
  //this.channel.off();
  //this.connectLobby(response);
});

HERE is the problem, only the client who joins first gets the token, the first broadcast seems to work, but the second one does nothing. In my browser in one client I get the token and the other is still waiting…

I wasted like 10 Hours trying to figurate this out doing different things but I dont know how to continue…

help?¿?¿

Welcome! :wave:

You mention the second broadcast not working, does this mean that the broadcast function is not called at all for the second client? or that the push function is doing nothing?

Where does broadcast/3 live - in what module? What exactly does Worker.search/1 do? Where do you put together the clients list?

After all, what’s the logic of your application from start to end like? I feel like we’re missing a bit of information in here. :slightly_smiling_face:

2 Likes

Welcome.

Could You clarify why You would use handle_out in this situation? It is normally used to intercept outgoing call…

There might be better way to authenticate user in a channel. Usually it is set in user_socket, some sort of authentication, or simply generate a token, and put this in conn.assigns.

For example…

  def connect(%{"token" => token}, socket) do
    with {:ok, user_id} <- verify_token(token) do
      {:ok, assign(socket, :user_id, user_id)}
    else
      {:error, _reason} -> :error
    end
  end
1 Like

Yes sorry.

Yes, phoenix log does not provide a thing like it is not beign called

Okay, first of all Worker.search/1 crates a new GenServer and pass to this new created one a token, this is the GenServer where broadcast/3 lives. Also the list called clients is inside the GenServer. When the clients list reaches certain length it calls broadcast/3 to send the token to different channels.

The broadcast/4 is beign called from this bit in the GenServer

if length(clients) == (size - 1) do
    lobby_token = :crypto.strong_rand_bytes(40) |> Base.url_encode64 |> binary_part(0, 40)
    %{size: size, clients: clients} = state
    broadcast(clients, lobby_token, 0)
    {:reply, {:ok, self()}, state}
  else
    {:reply, {:error, :full}, state}
  end

In broadcast/4 I forgot to delete count so it basically does nothing

Basically I have a Queue of Lobbies, when the max capacity of a lobby is reached I want the clients stored in clients list to get the token to connect to a new channel.

In user_token I already have this

def connect(%{"token" => token}, socket) do
case sign_in(socket, token) do
  {:ok, authed_socket, _guardian_params} ->
    {:ok, authed_socket}
  {:error, _} -> :error
end

end

I’m using handle_out because I’m using broadcast/3 in the GenServer