381 lines
10 KiB
Elixir
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
|