0

我正在尝试将 Phoenix Channels 用作极其简单的游戏服务器。对于原型,其唯一目的是同步玩家位置。但是,在此之前,每个客户端都必须知道所有其他客户端的存在。

当一个新客户加入房间时,我将其广播给所有在场的客户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

这些现有的客户端对消息做出反应并产生一个新的玩家对象。但是,新用户不知道房间里还有谁,因为他们是最后加入的,并且没有收到任何此类信息。我正在努力纠正这一点。

我的计划是将我的代码修改为如下内容:

@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

换句话说,我想维护一个加入用户的列表,并在加入时将其发送给新用户(并将它们添加到其中)。我是否使用代理、GenServer 或其他东西都无关紧要。

问题是,我不知道在哪里可以初始化那个代理,因为我RoomChannel没有init()回调;也不如何将它传递给join()andhandle_info()回调。什么是有效的——或者更好的,惯用的——方法来做到这一点?


PS值得注意的是,我希望将其存储在内存中,而不是数据库中。

4

1 回答 1

1

您可能可以使用Phoenix.Presence来处理所有这些头痛。

当用户加入 websocket 时,您可以将用户的状态数据存储在 socket 连接本身中。

例子:

@impl true
def join("room:main", _payload, socket) do
  user_info = %{
    something1: "something1....",
    something2: "something2....",
    ...
  }

  send(self(), :after_join)

  {:ok, assign(socket, user_info: user_info)}
end

然后确保Phoenix.Presence记录这些数据。你需要做这样的事情

例子:

@impl true
def handle_info(:after_join, socket) do
  %{user_info: user_info} = socket.assigns

  {:ok, _} = Presence.track(socket, "room:main", %{user_info: user_info})

  push(socket, "presence_state", Presence.list(socket))
  {:noreply, socket}
end

"presence_state"频道将广播订阅该频道的用户列表。

但是假设你想做一些更具体的事情,比如只显示频道中的人数。所以可能你也可以在handle_info(:after_join..

例子:

@impl true
def handle_info(:after_join, socket) do
  %{user_info: user_info} = socket.assigns

  {:ok, _} = Presence.track(socket, "room:main", %{user_info: user_info})
  
  total_users = 
    Presence.list(socket)["room:main"].metas
    |> Enum.count()

  broadcast!(socket, "room:main:total_users", %{total_users: total_users})
  push(socket, "presence_state", Presence.list(socket))
  {:noreply, socket}
end

您需要确保您的客户端也在收听"room:main:total_users"频道,以便获取广播数据。

希望它有所帮助。干杯!

于 2022-02-03T13:47:05.323 回答