I am implementing a chess server in elixir, I am implementing the client in React and I am using phoenix npm framework in react for handling socket and channel logic. This is my User Socket Code:
defmodule VortexPubSub.Cygnus.UserSocket do
use Phoenix.Socket
require Logger
alias VortexPubSub.KafkaProducer
alias VortexPubSub.Constants
alias Holmberg.Mutation.User, as: UserMutation
channel "game:chess:*", VortexPubSub.Cygnus.ChessGameChannel
@impl true
def connect(%{"token" => token , "user_id" => user_id , "username" => username}, socket) do
user_connection_event_payload = %{user_id: user_id , username: username}
case UserMutation.set_user_online(user_id, true) do
{:ok , _} -> {:ok , assign(socket, :user_id, user_id)}
{:error, _} ->:error
end
end
def connect(_params) do
Logger.info("THIS IS different connect fn")
:error
end
def id(socket), do: nil
end
The Chess Channel is defined like this:
defmodule VortexPubSub.Cygnus.ChessGameChannel do
alias MaelStorm.ChessServer
alias VortexPubSub.Presence
alias VortexPubSub.KafkaProducer
alias VortexPubSub.Constants
def join("game:chess:" <> game_id, _params, socket) do
IO.puts("Received join message from user ")
case ChessServer.game_pid(game_id) do
pid when is_pid(pid) ->
IO.puts("Channel Joinin Successful")
{:ok, socket}
nil ->
IO.puts("ERROR WHILE JOINING CHANNEL")
{:error, %{reason: "Game does not exist"}}
_ -> IO.puts("Some other error ")
{:error, %{reason: "Game does not exist"}}
end
end
def handle_in("joined-room", %{"user_id" => user_id, "username" => username}, socket) do
#Add logic to prevent user from joining if the game is in progress
"game:chess:" <> game_id = socket.topic
Phoenix.PubSub.broadcast!(socket, "new-user-joined", %{user_id: user_id, username: username, game_id: game_id})
#Send Current turn mappings of game to joined user
KafkaProducer.send_message(Constants.kafka_user_topic(), %{user_id: user_id, username: username, game_id: game_id} ,
Constants.kafka_user_joined_event_key())
{:noreply, socket}
end
def handle_in("leaved-room", %{"user_id" => user_id, "username" => username, "game_id" => game_id, "player_type" => player_type}, socket) do
if player_type == "host" do
Phoenix.PubSub.broadcast!(socket, "remove-all-users", %{user_id: user_id, username: username, game_id: game_id, player_type: player_type})
KafkaProducer.send_message(Constants.kafka_game_topic(), %{message: "host-left", game_id: game_id}, Constants.kafka_game_general_event_key())
else
Phoenix.PubSub.broadcast!(socket, "user-left-room", %{user_id: user_id, username: username, game_id: game_id, player_type: player_type})
end
KafkaProducer.send_message(Constants.kafka_user_topic(), %{user_id: user_id, username: username, game_id: game_id, player_type: player_type}, Constants.kafka_user_left_room_event_key())
{:noreply,socket}
end
def handle_in("update-user-status-in-room", %{user_id: user_id, username: username, game_id: game_id, status: status} ,socket) do
Phoenix.PubSub.broadcast!(socket, "user-status-update", %{user_id: user_id, username: username, game_id: game_id, status: status} )
KafkaProducer.send_message(Constants.kafka_user_topic(), %{user_id: user_id, username: username, game_id: game_id, status: status}, Constants.kafka_user_status_event_key())
{:noreply,socket}
end
def handle_in("game-event", %{user_id: user_id, game_id: game_id, game_event: game_event, event_type: event_type} , socket) do
Phoenix.PubSub.broadcast!(socket, "send-user-game-event", %{user_id: user_id, game_id: game_id, game_event: game_event, event_type: event_type} )
user_game_move_event = %{
game_id: game_id,
user_id: user_id,
user_move: game_event,
move_type: event_type,
}
KafkaProducer.send_message(Constants.kafka_user_game_events_topic(), %{user_id: user_id, game_id: game_id, game_event: game_event, event_type: event_type}, Constants.kafka_user_game_move_event_key())
{:noreply,socket}
end
def handle_in("verifying-game-status", %{user_id: user_id, game_id: game_id} , socket) do
Phoenix.PubSub.broadcast!(socket, "verifying-game", %{user_id: user_id , game_id: game_id} )
KafkaProducer.send_message(Constants.kafka_user_topic(), %{user_id: user_id, game_id: game_id}, Constants.kafka_verifying_game_status_event_key())
{:noreply,socket}
end
def handle_in("start-game-event", %{admin_id: admin_id, game_id: game_id, game_name: game_name} , socket) do
Phoenix.PubSub.broadcast!(socket, "start-game-for-all", %{admin_id: admin_id , game_id: game_id, game_name: game_name} )
# KafkaProducer.send_message(Constants.kafka_user_topic(), %{admin_id: admin_id, game_id: game_id}, Constants.kafka_verifying_game_status_event_key())
{:noreply,socket}
end
defp current_player(socket) do
socket.assigns.current_player
end
def terminate(reason, socket) do
# Add Logic to rearrange the state if user disconnects in arbitary way
# Handle cleanup or logging when a client leaves
IO.puts "Client left channel: #{inspect(reason)}"
:ok
end
end
This is my react code to create a socket connection and connect to channel
useEffect( () => {
if(!gameStore.isSpectator) {
console.log("Channel join loop. Current status of channel is")
console.log(chann)
if(chann === null) {
let new_socket = new Socket("ws://localhost:4001/socket", {params: {token: "token" , user_id: user_details.id , username: user_details.username }})
new_socket.connect()
console.log("setting chann this time")
let chann_new = new_socket.channel("game:" + gameType + ":" + game_id , {})
console.log("new channel is")
console.log(chann_new)
chann_new.join().receive("ok", response => {
console.log(`Joined game successfully 😊`)
})
.receive("error", response => {
console.log("Error while joining game")
console.log(response)
})
chann_new.onError((response) => {
console.log("SOme error occured on the channel")
console.log(response)
})
setChannel(chann_new);
}
// chann_new?.push("joined-room", {game_id: game_id, user_id: user_details.id , username: user_details.username})
}
return () => {
chann?.off("joined-room")
}
} , [chann] )
The Socket connection is successful as I get this message in logs after successful connection
21:54:56.733 [info] CONNECTED TO VortexPubSub.Cygnus.UserSocket in 3ms
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"token" => "token", "user_id" => "9fc09f31-35ce-4b1f-8839-9d1ac48282db", "username" => "necromorph23", "vsn" => "2.0.0"}
But after joining the channel I keep getting this error:
Ranch listener VortexPubSub.Endpoint.HTTP had connection process started with :cowboy_clear:start_link/4 at #PID<0.624.0> exit with reason: {:undef, [{VortexPubSub.Cygnus.ChessGameChannel, :child_spec, [{VortexPubSub.Endpoint, {#PID<0.624.0>, #Reference<0.3611881942.3265527814.183130>}}], []}, {Phoenix.Channel.Server, :join, 4, [file: ~c"lib/phoenix/channel/server.ex", line: 26]}, {Phoenix.Socket, :handle_in, 4, [file: ~c"lib/phoenix/socket.ex", line: 665]}, {WebSockAdapter.CowboyAdapter, :websocket_handle, 2, [file: ~c"lib/websock_adapter/cowboy_adapter.ex", line: 24]}, {:cowboy_websocket, :handler_call, 6, [file: ~c"/mnt/e/proj/vortex_pub_sub/deps/cowboy/src/cowboy_websocket.erl", line: 528]}, {:cowboy_http, :loop, 1, [file: ~c"/mnt/e/proj/vortex_pub_sub/deps/cowboy/src/cowboy_http.erl", line: 253]}, {:proc_lib, :init_p_do_apply, 3, [file: ~c"proc_lib.erl", line: 241]}]}
The socket keeps on getting disconnecting and it connects again to the server and tries to join the channel but this loop keeps on happening.
This is the channel states I have observed in the Inspect Logs
Can anyone help me to fix this issue.
Thanks