Start repo, claude & kimi still vibing tho
This commit is contained in:
178
lib/odinsea/channel/client.ex
Normal file
178
lib/odinsea/channel/client.ex
Normal file
@@ -0,0 +1,178 @@
|
||||
defmodule Odinsea.Channel.Client do
|
||||
@moduledoc """
|
||||
Client connection handler for game channel servers.
|
||||
Manages the game session state.
|
||||
"""
|
||||
|
||||
use GenServer, restart: :temporary
|
||||
|
||||
require Logger
|
||||
|
||||
alias Odinsea.Net.Packet.In
|
||||
alias Odinsea.Net.Opcodes
|
||||
alias Odinsea.Channel.Handler
|
||||
|
||||
defstruct [:socket, :ip, :channel_id, :state, :character_id]
|
||||
|
||||
def start_link({socket, channel_id}) do
|
||||
GenServer.start_link(__MODULE__, {socket, channel_id})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init({socket, channel_id}) do
|
||||
{:ok, {ip, _port}} = :inet.peername(socket)
|
||||
ip_string = format_ip(ip)
|
||||
|
||||
Logger.info("Channel #{channel_id} client connected from #{ip_string}")
|
||||
|
||||
state = %__MODULE__{
|
||||
socket: socket,
|
||||
ip: ip_string,
|
||||
channel_id: channel_id,
|
||||
state: :connected,
|
||||
character_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("Channel #{state.channel_id} client disconnected: #{state.ip}")
|
||||
{:stop, :normal, state}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("Channel 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("Channel #{state.channel_id} 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
|
||||
# Define opcodes for matching
|
||||
cp_general_chat = Opcodes.cp_general_chat()
|
||||
cp_party_chat = Opcodes.cp_party_chat()
|
||||
cp_whisper = Opcodes.cp_whisper()
|
||||
cp_move_player = Opcodes.cp_move_player()
|
||||
cp_change_map = Opcodes.cp_change_map()
|
||||
cp_change_keymap = Opcodes.cp_change_keymap()
|
||||
cp_skill_macro = Opcodes.cp_skill_macro()
|
||||
cp_close_range_attack = Opcodes.cp_close_range_attack()
|
||||
cp_ranged_attack = Opcodes.cp_ranged_attack()
|
||||
cp_magic_attack = Opcodes.cp_magic_attack()
|
||||
cp_take_damage = Opcodes.cp_take_damage()
|
||||
|
||||
case opcode do
|
||||
# Chat handlers
|
||||
^cp_general_chat ->
|
||||
case Handler.Chat.handle_general_chat(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_party_chat ->
|
||||
case Handler.Chat.handle_party_chat(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_whisper ->
|
||||
case Handler.Chat.handle_whisper(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
# Player movement and actions
|
||||
^cp_move_player ->
|
||||
case Handler.Player.handle_move_player(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_change_map ->
|
||||
case Handler.Player.handle_change_map(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_change_keymap ->
|
||||
case Handler.Player.handle_change_keymap(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_skill_macro ->
|
||||
case Handler.Player.handle_change_skill_macro(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
# Combat handlers (stubs for now)
|
||||
^cp_close_range_attack ->
|
||||
case Handler.Player.handle_close_range_attack(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_ranged_attack ->
|
||||
case Handler.Player.handle_ranged_attack(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_magic_attack ->
|
||||
case Handler.Player.handle_magic_attack(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
^cp_take_damage ->
|
||||
case Handler.Player.handle_take_damage(packet, state) do
|
||||
{:ok, new_state} -> new_state
|
||||
_ -> state
|
||||
end
|
||||
|
||||
_ ->
|
||||
Logger.debug("Unhandled channel opcode: 0x#{Integer.to_string(opcode, 16)}")
|
||||
state
|
||||
end
|
||||
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
|
||||
Reference in New Issue
Block a user