@@ -8,15 +8,30 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
88
99 alias Aesir.Commons.StatusParams
1010 alias Aesir.Commons.Utils.ServerTick
11+ alias Aesir.ZoneServer.Packets.CzRequestChat
1112 alias Aesir.ZoneServer.Packets.ZcAckReqname
1213 alias Aesir.ZoneServer.Packets.ZcAckReqnameall
1314 alias Aesir.ZoneServer.Packets.ZcEquipitemList
1415 alias Aesir.ZoneServer.Packets.ZcLongparChange
1516 alias Aesir.ZoneServer.Packets.ZcNormalItemlist
17+ alias Aesir.ZoneServer.Packets.ZcNotifyChat
1618 alias Aesir.ZoneServer.Packets.ZcNotifyTime
1719 alias Aesir.ZoneServer.Packets.ZcParChange
20+ alias Aesir.ZoneServer.Unit.Broadcast
1821 alias Aesir.ZoneServer.Unit.UnitRegistry
1922
23+ # Chat constants
24+ @ chat_max_size 255
25+
26+ # Packet IDs
27+ @ cz_notify_actorinit 0x007D
28+ @ cz_request_time 0x007E
29+ @ cz_request_time2 0x0360
30+ @ cz_reqname2 0x0368
31+ @ cz_request_move 0x035F
32+ @ cz_request_act 0x0437
33+ @ cz_request_chat 0x008C
34+
2035 @ doc """
2136 Processes an incoming packet for a player session.
2237
@@ -33,7 +48,7 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
3348
3449 # CZ_NOTIFY_ACTORINIT - Player finished loading map
3550 def handle_packet (
36- 0x007D ,
51+ @ cz_notify_actorinit ,
3752 _packet_data ,
3853 % { character: character , connection_pid: connection_pid , game_state: game_state } = state
3954 ) do
@@ -75,7 +90,7 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
7590 end
7691
7792 # CZ_REQUEST_TIME - Client requesting server time
78- def handle_packet ( 0x007E , _packet_data , % { connection_pid: connection_pid } = state ) do
93+ def handle_packet ( @ cz_request_time , _packet_data , % { connection_pid: connection_pid } = state ) do
7994 server_tick = ServerTick . now ( )
8095
8196 packet = % ZcNotifyTime {
@@ -87,7 +102,7 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
87102 end
88103
89104 # CZ_REQUEST_TIME2 - Alternative client time request
90- def handle_packet ( 0x0360 , _packet_data , % { connection_pid: connection_pid } = state ) do
105+ def handle_packet ( @ cz_request_time2 , _packet_data , % { connection_pid: connection_pid } = state ) do
91106 server_tick = ServerTick . now ( )
92107
93108 packet = % ZcNotifyTime {
@@ -100,7 +115,7 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
100115
101116 # CZ_REQNAME2 - Client requesting entity name
102117 def handle_packet (
103- 0x0368 ,
118+ @ cz_reqname2 ,
104119 packet_data ,
105120 % {
106121 character: character ,
@@ -165,13 +180,13 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
165180 end
166181
167182 # CZ_REQUEST_MOVE - Player movement request
168- def handle_packet ( 0x035F , packet_data , state ) do
183+ def handle_packet ( @ cz_request_move , packet_data , state ) do
169184 GenServer . cast ( self ( ) , { :request_move , packet_data . dest_x , packet_data . dest_y } )
170185 { :noreply , state }
171186 end
172187
173188 # CZ_REQUEST_ACT - Player action request (attack, sit, stand, etc.)
174- def handle_packet ( 0x0437 , packet_data , state ) do
189+ def handle_packet ( @ cz_request_act , packet_data , state ) do
175190 case packet_data . action do
176191 action when action in [ 0 , 7 ] ->
177192 # Attack actions (0 = single attack, 7 = continuous attack)
@@ -192,6 +207,48 @@ defmodule Aesir.ZoneServer.Unit.Player.Handlers.PacketHandler do
192207 { :noreply , state }
193208 end
194209
210+ # CZ_REQUEST_CHAT - Player sending an area chat message
211+ def handle_packet (
212+ @ cz_request_chat ,
213+ % CzRequestChat { message: raw_message } ,
214+ % { character: character , game_state: game_state , connection_pid: connection_pid } = state
215+ ) do
216+ # 1. Message Validation
217+ # Max chat size from rAthena is 256 bytes (including null terminator)
218+ if byte_size ( raw_message ) > @ chat_max_size do
219+ Logger . warning ( "Player #{ character . id } sent a message exceeding maximum length." )
220+ # Optionally send an error message to the client
221+ { :noreply , state }
222+ else
223+ # rAthena expects "CharName : Message"
224+ # We need to extract the actual message and validate the prefix
225+ name_prefix = character . name <> " : "
226+
227+ if String . starts_with? ( raw_message , name_prefix ) do
228+ chat_message = raw_message
229+
230+ # 2. Construct ZcNotifyChat packet
231+ packet = % ZcNotifyChat {
232+ gid: character . id ,
233+ message: chat_message
234+ }
235+
236+ # 3. Broadcasting
237+ # To self
238+ send ( connection_pid , { :send_packet , packet } )
239+
240+ # To visible players (excluding self)
241+ Broadcast . to_visible_players ( game_state , packet , exclude_id: character . id )
242+ else
243+ Logger . warning (
244+ "Player #{ character . id } sent a malformed chat message (expected '#{ name_prefix } '). Message: '#{ raw_message } '"
245+ )
246+ end
247+
248+ { :noreply , state }
249+ end
250+ end
251+
195252 # Fallback for unknown packets
196253 def handle_packet ( packet_id , _packet_data , state ) do
197254 Logger . warning ( "Unhandled packet in PacketHandler: 0x#{ Integer . to_string ( packet_id , 16 ) } " )
0 commit comments