Files
odinsea-elixir/lib/odinsea/net/packet_logger.ex
2026-02-25 12:26:26 -07:00

381 lines
10 KiB
Elixir

defmodule Odinsea.Net.PacketLogger do
@moduledoc """
Comprehensive packet logging system for debugging MapleStory protocol.
Provides detailed packet logging similar to the Java version with:
- Direction (client/loopback)
- Opcode name and value (decimal/hex)
- Raw hex data
- ASCII text representation
- Context information (session, thread)
"""
require Logger
alias Odinsea.Net.{Hex, Opcodes}
@doc """
Logs an incoming client packet with full details.
## Parameters
- `opcode` - The packet opcode (integer)
- `data` - The packet data (binary, excluding opcode bytes)
- `context` - Map with :ip, :server_type, etc.
"""
def log_client_packet(opcode, data, context \\ %{}) do
if packet_logging_enabled?() do
opcode_name = get_client_opcode_name(opcode)
ip = Map.get(context, :ip, "unknown")
server_type = Map.get(context, :server_type, :unknown)
# Format opcode header
opcode_header = format_opcode_header("client", opcode_name, opcode)
# Full packet data includes opcode
full_data = <<opcode::little-16>> <> data
# Format hex and text
hex_str = Hex.encode(full_data)
text_str = Hex.to_ascii(full_data)
# Log the packet
Logger.info("""
#{opcode_header}
[Data] #{hex_str}
[Text] #{text_str}
[Context] IP=#{ip} Server=#{server_type} Size=#{byte_size(full_data)} bytes
""")
:ok
else
:ok
end
end
@doc """
Logs an outgoing server packet with full details.
## Parameters
- `opcode` - The packet opcode (integer)
- `data` - The packet data (binary, excluding opcode bytes)
- `context` - Map with :ip, :server_type, etc.
"""
def log_server_packet(opcode, data, context \\ %{}) do
if packet_logging_enabled?() do
opcode_name = get_server_opcode_name(opcode)
ip = Map.get(context, :ip, "unknown")
server_type = Map.get(context, :server_type, :unknown)
# Format opcode header
opcode_header = format_opcode_header("loopback", opcode_name, opcode)
# Full packet data includes opcode
full_data = <<opcode::little-16>> <> data
# Format hex and text
hex_str = Hex.encode(full_data)
text_str = Hex.to_ascii(full_data)
# Log the packet
Logger.info("""
#{opcode_header}
[Data] #{hex_str}
[Text] #{text_str}
[Context] IP=#{ip} Server=#{server_type} Size=#{byte_size(full_data)} bytes
""")
:ok
else
:ok
end
end
@doc """
Logs raw packet data (used for handshake/hello packets that don't follow normal format).
"""
def log_raw_packet(direction, label, data, context \\ %{}) do
if packet_logging_enabled?() do
ip = Map.get(context, :ip, "unknown")
data = IO.iodata_to_binary(data)
hex_str = Hex.encode(data)
text_str = Hex.to_ascii(data)
Logger.info("""
[#{direction}] [#{label}]
[Data] #{hex_str}
[Text] #{text_str}
[Context] IP=#{ip} Size=#{byte_size(data)} bytes
""")
:ok
else
:ok
end
end
# ==================================================================================================
# Private Helper Functions
# ==================================================================================================
defp packet_logging_enabled? do
features = Application.get_env(:odinsea, :features, [])
Keyword.get(features, :log_packet, false)
end
defp format_opcode_header(direction, opcode_name, opcode) do
opcode_hex = Integer.to_string(opcode, 16) |> String.upcase() |> String.pad_leading(2, "0")
"[#{direction}] [#{opcode_name}] #{opcode} / 0x#{opcode_hex}"
end
# ==================================================================================================
# Opcode Name Resolution
# ==================================================================================================
defp get_client_opcode_name(opcode) do
case opcode do
# Connection/Security
0x16 -> "CP_AliveAck"
# Login/Account
0x01 -> "CP_PermissionRequest"
0x02 -> "CP_CheckPassword"
0x04 -> "CP_ServerlistRequest"
0x05 -> "CP_SelectWorld"
0x06 -> "CP_CheckUserLimit"
0x0E -> "CP_CheckCharName"
0x12 -> "CP_CreateChar"
0x14 -> "CP_CreateUltimate"
0x15 -> "CP_DeleteChar"
0x17 -> "CP_ExceptionLog"
0x18 -> "CP_SecurityPacket"
0x19 -> "CP_CharSelect"
0x1A -> "CP_AuthSecondPassword"
0x1D -> "CP_ClientDumpLog"
0x1E -> "CP_CreateSecurityHandle"
0x20 -> "RSA_KEY"
0x5001 -> "CP_HardwareInfo"
0x5004 -> "CP_WindowFocus"
# Migration/Channel
0x0D -> "CP_PlayerLoggedIn"
0x23 -> "CP_ChangeMap"
0x24 -> "CP_ChangeChannel"
0x25 -> "CP_EnterCashShop"
0x26 -> "CP_EnterPvp"
0x27 -> "CP_EnterPvpParty"
0x29 -> "CP_LeavePvp"
0xB4 -> "CP_EnterMts"
# Player Movement/Actions
0x2A -> "CP_MovePlayer"
0x2C -> "CP_CancelChair"
0x2D -> "CP_UseChair"
0x2F -> "CP_CloseRangeAttack"
0x30 -> "CP_RangedAttack"
0x31 -> "CP_MagicAttack"
0x32 -> "CP_PassiveEnergy"
0x34 -> "CP_TakeDamage"
0x35 -> "CP_PvpAttack"
0x36 -> "CP_GeneralChat"
0x37 -> "CP_CloseChalkboard"
0x38 -> "CP_FaceExpression"
0x75 -> "CP_CharInfoRequest"
0x76 -> "CP_SpawnPet"
0x78 -> "CP_CancelDebuff"
# NPC Interaction
0x40 -> "CP_NpcTalk"
0x41 -> "CP_NpcMove"
0x42 -> "CP_NpcTalkMore"
0x43 -> "CP_NpcShop"
0x44 -> "CP_Storage"
0x45 -> "CP_UseHiredMerchant"
0x47 -> "CP_MerchItemStore"
# Inventory/Items
0x4D -> "CP_ItemSort"
0x4E -> "CP_ItemGather"
0x4F -> "CP_ItemMove"
0x53 -> "CP_UseItem"
0x10C -> "CP_ItemPickup"
# Stats/Skills
0x6A -> "CP_DistributeAp"
0x6B -> "CP_AutoAssignAp"
0x6E -> "CP_DistributeSp"
0x6F -> "CP_SpecialMove"
# Social
0xA0 -> "CP_PartyChat"
0xA1 -> "CP_Whisper"
0xA4 -> "CP_PartyOperation"
0xA8 -> "CP_GuildOperation"
# Cash Shop
0x135 -> "CP_CsUpdate"
0x136 -> "CP_BuyCsItem"
0x137 -> "CP_CouponCode"
# Custom
0x5002 -> "CP_InjectPacket"
0x5003 -> "CP_SetCodePage"
_ -> "UNKNOWN"
end
end
defp get_server_opcode_name(opcode) do
case opcode do
# General
0x0D -> "LP_AliveReq"
0x0C -> "LP_ChangeChannel"
0x15 -> "LP_LatestConnectedWorld"
0x16 -> "LP_RecommendWorldMessage"
# Login
0x00 -> "HELLO"
0x01 -> "LOGIN_STATUS"
0x03 -> "LP_ServerStatus"
0x06 -> "SERVERLIST"
0x07 -> "LP_CharList"
0x08 -> "LP_ServerIp"
0x09 -> "LP_CharNameResponse"
0x0A -> "LP_AddNewCharEntry"
0x0B -> "LP_DeleteCharResponse"
0x10 -> "LP_ChannelSelected"
0x12 -> "LP_RelogResponse"
0x13 -> "RSA_KEY"
0x17 -> "LOGIN_AUTH"
0x18 -> "LP_SecondPwError"
# Inventory/Stats
0x19 -> "LP_ModifyInventoryItem"
0x1A -> "LP_UpdateInventorySlot"
0x1B -> "LP_UpdateStats"
0x1C -> "LP_GiveBuff"
0x1D -> "LP_CancelBuff"
0x20 -> "LP_UpdateSkills"
0x22 -> "LP_FameResponse"
0x23 -> "LP_ShowStatusInfo"
0x25 -> "LP_TrockLocations"
# Social/Party/Guild
0x38 -> "LP_PartyOperation"
0x3A -> "LP_ExpeditionOperation"
0x3B -> "LP_BuddyList"
0x3D -> "LP_GuildOperation"
0x3E -> "LP_AllianceOperation"
# Map Effects/Environment
0x3F -> "LP_SpawnPortal"
0x40 -> "LP_MechPortal"
0x41 -> "LP_ServerMessage"
0x4A -> "LP_YellowChat"
# Family
0x6D -> "LP_SendPedigree"
0x6E -> "LP_OpenFamily"
0x73 -> "LP_Family"
# Misc UI/Messages
0x7E -> "LP_TopMsg"
0x7F -> "LP_MidMsg"
0x80 -> "LP_ClearMidMsg"
# Warps/Shops
0x90 -> "LP_WarpToMap"
0x91 -> "LP_MtsOpen"
0x92 -> "LP_CsOpen"
# Effects
0x99 -> "LP_ShowEquipEffect"
0x9A -> "LP_MultiChat"
0x9B -> "LP_Whisper"
0xA1 -> "LP_MapEffect"
0xA6 -> "LP_Clock"
# Players
0xB8 -> "LP_SpawnPlayer"
0xB9 -> "LP_RemovePlayerFromMap"
0xBA -> "LP_ChatText"
0xBC -> "LP_Chalkboard"
# Pets
0xD1 -> "LP_SpawnPet"
0xD4 -> "LP_MovePet"
0xD5 -> "LP_PetChat"
# Player Actions
0xE2 -> "LP_MovePlayer"
0xE4 -> "LP_CloseRangeAttack"
0xE5 -> "LP_RangedAttack"
0xE6 -> "LP_MagicAttack"
0xE8 -> "LP_SkillEffect"
0xEB -> "LP_DamagePlayer"
0xEC -> "LP_FacialExpression"
0xF0 -> "LP_ShowChair"
0xF1 -> "LP_UpdateCharLook"
# Summons
0x131 -> "LP_SpawnSummon"
0x132 -> "LP_RemoveSummon"
0x133 -> "LP_MoveSummon"
0x134 -> "LP_SummonAttack"
# Monsters
0x13A -> "LP_SpawnMonster"
0x13B -> "LP_KillMonster"
0x13C -> "LP_SpawnMonsterControl"
0x13D -> "LP_MoveMonster"
0x144 -> "LP_DamageMonster"
# NPCs
0x156 -> "LP_SpawnNpc"
0x157 -> "LP_RemoveNpc"
0x158 -> "LP_SpawnNpcRequestController"
0x159 -> "LP_NpcAction"
0x1A3 -> "LP_NpcTalk"
0x1A5 -> "LP_OpenNpcShop"
# Merchants
0x161 -> "LP_SpawnHiredMerchant"
0x162 -> "LP_DestroyHiredMerchant"
# Map Objects
0x165 -> "LP_DropItemFromMapObject"
0x167 -> "LP_RemoveItemFromMap"
0x16B -> "LP_SpawnMist"
0x16C -> "LP_RemoveMist"
0x16D -> "LP_SpawnDoor"
0x16E -> "LP_RemoveDoor"
# Reactors
0x171 -> "LP_ReactorHit"
0x173 -> "LP_ReactorSpawn"
0x174 -> "LP_ReactorDestroy"
# NPC/Shop Interactions
0x1A6 -> "LP_ConfirmShopTransaction"
0x1A9 -> "LP_OpenStorage"
0x1AB -> "LP_MerchItemStore"
# Cash Shop
0x1B8 -> "LP_CsUpdate"
0x1B9 -> "LP_CsOperation"
# Input
0x1C5 -> "LP_Keymap"
# Custom
0x5000 -> "LP_DamageSkin"
0x5001 -> "LP_OpenWebsite"
0x15 -> "LP_LatestConnectedWorld"
0x16 -> "LP_RecommendWorldMessage"
_ -> "UNKNOWN"
end
end
end