From de4e344cdbfead458969a370e71b2246b4e16f06 Mon Sep 17 00:00:00 2001 From: qornwh1 Date: Mon, 9 Mar 2026 08:51:50 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=B1=84=ED=8C=85=20=EA=B8=B0?= =?UTF-8?q?=EC=A6=9D=20=EC=B6=94=EA=B0=80,=20=EA=B7=93=EC=86=8D=EB=A7=90?= =?UTF-8?q?=EC=9D=80=20=EB=8C=80=EC=83=81=EC=9D=B4=20id=EB=9D=BC=EB=8A=94?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=EC=9E=88=EC=9D=8C=20<=3D=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=EB=8A=94=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20=EB=91=94?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MMOTestServer/MMOserver/Game/GameServer.cs | 81 ++++++++++++++++++- MMOTestServer/MMOserver/Packet/PacketBody.cs | 59 +++++++++++++- .../MMOserver/Packet/PacketHeader.cs | 3 + 3 files changed, 140 insertions(+), 3 deletions(-) diff --git a/MMOTestServer/MMOserver/Game/GameServer.cs b/MMOTestServer/MMOserver/Game/GameServer.cs index 40ec37f..3d1a9eb 100644 --- a/MMOTestServer/MMOserver/Game/GameServer.cs +++ b/MMOTestServer/MMOserver/Game/GameServer.cs @@ -27,6 +27,7 @@ public class GameServer : ServerBase [(ushort)PacketCode.ACTION_PLAYER] = OnActionPlayer, [(ushort)PacketCode.STATE_PLAYER] = OnStatePlayer, [(ushort)PacketCode.REQUEST_PARTY] = OnRequestParty, + [(ushort)PacketCode.CHAT] = OnChat, }; userUuidGenerator = new UuidGenerator(); } @@ -182,7 +183,14 @@ public class GameServer : ServerBase { if (packetHandlers.TryGetValue(type, out Action? handler)) { - handler(peer, hashKey, payload); + try + { + handler(peer, hashKey, payload); + } + catch (Exception ex) + { + Log.Error(ex, "[GameServer] 패킷 처리 중 예외 Type={Type} HashKey={Key}", type, hashKey); + } } else { @@ -201,7 +209,7 @@ public class GameServer : ServerBase { ChannelInfo info = new ChannelInfo(); info.ChannelId = channel.ChannelId; - info.ChannelUserConut = channel.UserCount; + info.ChannelUserCount = channel.UserCount; info.ChannelUserMax = channel.UserCountMax; loadChannelPacket.Channels.Add(info); } @@ -564,6 +572,75 @@ public class GameServer : ServerBase } } + private void OnChat(NetPeer peer, int hashKey, byte[] payload) + { + ChatPacket req = Serializer.Deserialize(new ReadOnlyMemory(payload)); + + ChannelManager cm = ChannelManager.Instance; + int channelId = cm.HasUser(hashKey); + if (channelId < 0) + { + return; + } + + Player? sender = cm.GetChannel(channelId).GetPlayer(hashKey); + if (sender == null) + { + return; + } + + // 서버에서 발신자 정보 채워줌 (클라 위조 방지) + ChatPacket res = new ChatPacket + { + Type = req.Type, + SenderId = sender.PlayerId, + SenderNickname = sender.Nickname, + TargetId = req.TargetId, + Message = req.Message, + }; + + byte[] data = PacketSerializer.Serialize((ushort)PacketCode.CHAT, res); + + switch (req.Type) + { + case ChatType.GLOBAL: + // 채널 내 모든 유저 (자신 포함) + BroadcastToChannel(channelId, data); + Log.Debug("[Chat] GLOBAL HashKey={Key} Message={Msg}", hashKey, req.Message); + break; + + case ChatType.PARTY: + // 파티 멤버에게만 (자신 포함) + PartyManager pm = cm.GetChannel(channelId).GetPartyManager(); + PartyInfo? party = pm.GetPartyByPlayer(hashKey); + if (party == null) + { + Log.Warning("[Chat] PARTY 파티 없음 HashKey={Key}", hashKey); + return; + } + + BroadcastToUsers(party.PartyMemberIds, data); + Log.Debug("[Chat] PARTY HashKey={Key} PartyId={PartyId} Message={Msg}", hashKey, party.PartyId, req.Message); + break; + + case ChatType.WHISPER: + // 대상 + 발신자에게만 + if (sessions.TryGetValue(req.TargetId, out NetPeer? targetPeer)) + { + SendTo(targetPeer, data); + } + else + { + Log.Warning("[Chat] WHISPER 대상 없음 HashKey={Key} TargetId={TargetId}", hashKey, req.TargetId); + } + + // 자신에게도 전송 (귓말 확인용) + SendTo(peer, data); + Log.Debug("[Chat] WHISPER HashKey={Key} TargetId={TargetId} Message={Msg}", hashKey, req.TargetId, req.Message); + break; + } + } + private void SendError(NetPeer peer, ErrorCode code) { ErrorPacket err = new ErrorPacket { Code = code }; diff --git a/MMOTestServer/MMOserver/Packet/PacketBody.cs b/MMOTestServer/MMOserver/Packet/PacketBody.cs index 0f668dd..90bb458 100644 --- a/MMOTestServer/MMOserver/Packet/PacketBody.cs +++ b/MMOTestServer/MMOserver/Packet/PacketBody.cs @@ -185,7 +185,7 @@ public class ChannelInfo } [ProtoMember(2)] - public int ChannelUserConut + public int ChannelUserCount { get; set; @@ -579,6 +579,63 @@ public class RequestPartyPacket } // CREATE 시 사용 } +// ============================================================ +// 채팅 +// ============================================================ + +public enum ChatType +{ + GLOBAL, // 전체 채널 + PARTY, // 파티원 + WHISPER // 귓말 +} + +// CHAT (클라 -> 서버 & 서버 -> 클라) +// 클라->서버: Type, TargetId(WHISPER 시), Message +// 서버->클라: Type, SenderId, SenderNickname, TargetId(WHISPER 시), Message +[ProtoContract] +public class ChatPacket +{ + [ProtoMember(1)] + public ChatType Type + { + get; + set; + } + + [ProtoMember(2)] + public int SenderId + { + get; + set; + } // 서버에서 채워줌 + + [ProtoMember(3)] + public string SenderNickname + { + get; + set; + } // 서버에서 채워줌 + + [ProtoMember(4)] + public int TargetId + { + get; + set; + } // WHISPER일 때 대상 PlayerId + + [ProtoMember(5)] + public string Message + { + get; + set; + } +} + +// ============================================================ +// 파티 +// ============================================================ + // UPDATE_PARTY (서버 -> 클라) - 파티 생성/삭제: LeaderId 사용 / 파티원 추가/제거: PlayerId 사용 [ProtoContract] public class UpdatePartyPacket diff --git a/MMOTestServer/MMOserver/Packet/PacketHeader.cs b/MMOTestServer/MMOserver/Packet/PacketHeader.cs index aca58a9..ab11ccc 100644 --- a/MMOTestServer/MMOserver/Packet/PacketHeader.cs +++ b/MMOTestServer/MMOserver/Packet/PacketHeader.cs @@ -54,6 +54,9 @@ public enum PacketCode : ushort // 파티 참가/탈퇴/생성/해산 요청 (클라 -> 서버) REQUEST_PARTY, + // 채팅 (클라 -> 서버 & 서버 -> 클라) - GLOBAL / PARTY / WHISPER + CHAT, + // 요청 실패 응답 (서버 -> 클라) ERROR = 9999 }