defmodule Odinsea.Shop.Client do @moduledoc """ Client connection handler for the cash shop server. """ use GenServer, restart: :temporary require Logger alias Odinsea.Net.Packet.In defstruct [:socket, :ip, :state, :character_id, :account_id] def start_link(socket) do GenServer.start_link(__MODULE__, socket) end @impl true def init(socket) do {:ok, {ip, _port}} = :inet.peername(socket) ip_string = format_ip(ip) Logger.info("Cash shop client connected from #{ip_string}") state = %__MODULE__{ socket: socket, ip: ip_string, state: :connected, character_id: nil, account_id: nil } send(self(), :receive) {:ok, state} end @impl true def handle_info(:receive, %{socket: socket} = state) do case :gen_tcp.recv(socket, 0, 30_000) do {:ok, data} -> new_state = handle_packet(data, state) send(self(), :receive) {:noreply, new_state} {:error, :closed} -> Logger.info("Cash shop client disconnected: #{state.ip}") {:stop, :normal, state} {:error, reason} -> Logger.warning("Cash shop client error: #{inspect(reason)}") {:stop, :normal, state} end end @impl true def terminate(_reason, state) do if state.socket do :gen_tcp.close(state.socket) end :ok end defp handle_packet(data, state) do packet = In.new(data) case In.decode_short(packet) do {opcode, packet} -> Logger.debug("Cash shop packet: opcode=0x#{Integer.to_string(opcode, 16)}") dispatch_packet(opcode, packet, state) :error -> Logger.warning("Failed to read packet opcode") state end end defp dispatch_packet(_opcode, _packet, state) do # TODO: Implement cash shop packet handlers state end defp format_ip({a, b, c, d}) do "#{a}.#{b}.#{c}.#{d}" end defp format_ip({a, b, c, d, e, f, g, h}) do "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}" end end