Start repo, claude & kimi still vibing tho

This commit is contained in:
ra
2026-02-14 17:04:21 -07:00
commit f5b8aeb39d
54 changed files with 9466 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
defmodule Odinsea.Net.Packet.In do
@moduledoc """
Incoming packet reader ported from Java InPacket.
Handles little-endian decoding of MapleStory packet data.
"""
defstruct data: <<>>, index: 0, length: 0
alias Odinsea.Constants.Server
@type t :: %__MODULE__{
data: binary(),
index: non_neg_integer(),
length: non_neg_integer()
}
@doc """
Creates a new incoming packet from binary data.
"""
@spec new(binary()) :: t()
def new(data) when is_binary(data) do
%__MODULE__{
data: data,
index: 0,
length: byte_size(data)
}
end
@doc """
Returns the remaining bytes in the packet.
"""
@spec remaining(t()) :: non_neg_integer()
def remaining(%__MODULE__{length: length, index: index}), do: length - index
@doc """
Returns true if the packet has been fully read.
"""
@spec empty?(t()) :: boolean()
def empty?(%__MODULE__{length: length, index: index}), do: index >= length
@doc """
Returns the current read position.
"""
@spec get_index(t()) :: non_neg_integer()
def get_index(%__MODULE__{index: index}), do: index
@doc """
Sets the read position.
"""
@spec set_index(t(), non_neg_integer()) :: t()
def set_index(packet, index) when index >= 0 do
%{packet | index: min(index, packet.length)}
end
@doc """
Skips the specified number of bytes.
"""
@spec skip(t(), non_neg_integer()) :: t()
def skip(packet, count) when count >= 0 do
%{packet | index: min(packet.index + count, packet.length)}
end
@doc """
Reads a byte and returns {value, updated_packet}.
"""
@spec decode_byte(t()) :: {integer(), t()} | :error
def decode_byte(%__MODULE__{data: data, index: index, length: length}) do
if index + 1 <= length do
<<_::binary-size(index), value::signed-integer-little-8, _::binary>> = data
{value, %__MODULE__{data: data, index: index + 1, length: length}}
else
:error
end
end
@doc """
Reads a byte and returns only the value, raising on error.
"""
@spec decode_byte!(t()) :: integer()
def decode_byte!(packet) do
case decode_byte(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading byte"
end
end
@doc """
Reads a short (2 bytes) and returns {value, updated_packet}.
"""
@spec decode_short(t()) :: {integer(), t()} | :error
def decode_short(%__MODULE__{data: data, index: index, length: length}) do
if index + 2 <= length do
<<_::binary-size(index), value::signed-integer-little-16, _::binary>> = data
{value, %__MODULE__{data: data, index: index + 2, length: length}}
else
:error
end
end
@doc """
Reads a short and returns only the value, raising on error.
"""
@spec decode_short!(t()) :: integer()
def decode_short!(packet) do
case decode_short(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading short"
end
end
@doc """
Reads an int (4 bytes) and returns {value, updated_packet}.
"""
@spec decode_int(t()) :: {integer(), t()} | :error
def decode_int(%__MODULE__{data: data, index: index, length: length}) do
if index + 4 <= length do
<<_::binary-size(index), value::signed-integer-little-32, _::binary>> = data
{value, %__MODULE__{data: data, index: index + 4, length: length}}
else
:error
end
end
@doc """
Reads an int and returns only the value, raising on error.
"""
@spec decode_int!(t()) :: integer()
def decode_int!(packet) do
case decode_int(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading int"
end
end
@doc """
Reads a long (8 bytes) and returns {value, updated_packet}.
"""
@spec decode_long(t()) :: {integer(), t()} | :error
def decode_long(%__MODULE__{data: data, index: index, length: length}) do
if index + 8 <= length do
<<_::binary-size(index), value::signed-integer-little-64, _::binary>> = data
{value, %__MODULE__{data: data, index: index + 8, length: length}}
else
:error
end
end
@doc """
Reads a long and returns only the value, raising on error.
"""
@spec decode_long!(t()) :: integer()
def decode_long!(packet) do
case decode_long(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading long"
end
end
@doc """
Reads a MapleStory ASCII string (length-prefixed).
Format: [2-byte length][ASCII bytes]
"""
@spec decode_string(t()) :: {String.t(), t()} | :error
def decode_string(packet) do
case decode_short(packet) do
{length, packet} when length >= 0 ->
decode_buffer(packet, length) |> decode_string_result()
_ ->
:error
end
end
defp decode_string_result({bytes, packet}) do
{bytes, packet}
end
@doc """
Reads a string and returns only the value, raising on error.
"""
@spec decode_string!(t()) :: String.t()
def decode_string!(packet) do
case decode_string(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading string"
end
end
@doc """
Reads a specified number of bytes and returns {bytes, updated_packet}.
"""
@spec decode_buffer(t(), non_neg_integer()) :: {binary(), t()} | :error
def decode_buffer(%__MODULE__{data: data, index: index, length: total_length}, count)
when count >= 0 do
if index + count <= total_length do
<<_::binary-size(index), buffer::binary-size(count), _::binary>> = data
{buffer, %__MODULE__{data: data, index: index + count, length: total_length}}
else
:error
end
end
@doc """
Reads a buffer and returns only the bytes, raising on error.
"""
@spec decode_buffer!(t(), non_neg_integer()) :: binary()
def decode_buffer!(packet, count) do
case decode_buffer(packet, count) do
{value, _} -> value
:error -> raise "Packet underrun reading buffer"
end
end
@doc """
Reads a boolean (1 byte, 0 = false, 1 = true).
"""
@spec decode_bool(t()) :: {boolean(), t()} | :error
def decode_bool(packet) do
case decode_byte(packet) do
{0, packet} -> {false, packet}
{_, packet} -> {true, packet}
:error -> :error
end
end
@doc """
Reads a boolean, raising on error.
"""
@spec decode_bool!(t()) :: boolean()
def decode_bool!(packet) do
case decode_bool(packet) do
{value, _} -> value
:error -> raise "Packet underrun reading bool"
end
end
@doc """
Reads the remaining bytes in the packet.
"""
@spec read_remaining(t()) :: {binary(), t()}
def read_remaining(%__MODULE__{data: data, index: index, length: length}) do
remaining = length - index
<<_::binary-size(index), buffer::binary-size(remaining), _::binary>> = data
{buffer, %__MODULE__{data: data, index: length, length: length}}
end
@doc """
Converts the packet to a hex string for debugging.
"""
@spec to_hex_string(t()) :: String.t()
def to_hex_string(%__MODULE__{data: data}), do: Odinsea.Net.Hex.encode(data)
@doc """
Converts the packet to a hex string with position markers.
"""
@spec to_hex_string(t(), boolean()) :: String.t()
def to_hex_string(%__MODULE__{data: data, index: index}, true) do
hex_str = Odinsea.Net.Hex.encode(data)
"[pos=#{index}] " <> hex_str
end
def to_hex_string(packet, false), do: to_hex_string(packet)
@doc """
Returns the raw packet data.
"""
@spec to_buffer(t()) :: binary()
def to_buffer(%__MODULE__{data: data}), do: data
@doc """
Returns a slice of the packet data.
"""
@spec slice(t(), non_neg_integer(), non_neg_integer()) :: binary()
def slice(%__MODULE__{data: data}, start, length) do
binary_part(data, start, length)
end
@doc """
Returns the packet length.
"""
@spec length(t()) :: non_neg_integer()
def length(%__MODULE__{length: length}), do: length
end