74 lines
1.7 KiB
Elixir
74 lines
1.7 KiB
Elixir
defmodule Odinsea.Channel.Server do
|
|
@moduledoc """
|
|
A single game channel server.
|
|
"""
|
|
|
|
use GenServer
|
|
|
|
require Logger
|
|
|
|
defstruct [:channel_id, :socket, :port, :clients]
|
|
|
|
def start_link(channel_id) do
|
|
GenServer.start_link(__MODULE__, channel_id, name: via_tuple(channel_id))
|
|
end
|
|
|
|
def via_tuple(channel_id) do
|
|
{:via, Registry, {Odinsea.Channel.Registry, channel_id}}
|
|
end
|
|
|
|
@impl true
|
|
def init(channel_id) do
|
|
ports = Application.get_env(:odinsea, :game, [])[:channel_ports] || %{}
|
|
port = Map.get(ports, channel_id, 8584 + channel_id)
|
|
|
|
case :gen_tcp.listen(port, tcp_options()) do
|
|
{:ok, socket} ->
|
|
Logger.info("Channel #{channel_id} listening on port #{port}")
|
|
send(self(), :accept)
|
|
|
|
{:ok,
|
|
%__MODULE__{
|
|
channel_id: channel_id,
|
|
socket: socket,
|
|
port: port,
|
|
clients: %{}
|
|
}}
|
|
|
|
{:error, reason} ->
|
|
Logger.error("Failed to start channel #{channel_id} on port #{port}: #{inspect(reason)}")
|
|
{:stop, reason}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_info(:accept, %{socket: socket, channel_id: channel_id} = state) do
|
|
case :gen_tcp.accept(socket) do
|
|
{:ok, client_socket} ->
|
|
{:ok, _pid} =
|
|
DynamicSupervisor.start_child(
|
|
Odinsea.ClientSupervisor,
|
|
{Odinsea.Channel.Client, {client_socket, channel_id}}
|
|
)
|
|
|
|
send(self(), :accept)
|
|
{:noreply, state}
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("Channel #{channel_id} accept error: #{inspect(reason)}")
|
|
send(self(), :accept)
|
|
{:noreply, state}
|
|
end
|
|
end
|
|
|
|
defp tcp_options do
|
|
[
|
|
:binary,
|
|
packet: :raw,
|
|
active: false,
|
|
reuseaddr: true,
|
|
backlog: 100
|
|
]
|
|
end
|
|
end
|