using LiteNetLib; using MMOserver.Game.Channel; using MMOserver.Game.Party; using MMOserver.Packet; using ProtoBuf; using ServerLib.Packet; using ServerLib.Service; namespace MMOserver.Game.Service; /* * 파티 요청(추가, 탈퇴, 정보변경) 메시지 핸들러 */ public partial class GameServer : ServerBase { private void OnRequestParty(NetPeer peer, int hashKey, byte[] payload) { RequestPartyPacket req = Serializer.Deserialize(new ReadOnlyMemory(payload)); ChannelManager cm = ChannelManager.Instance; int channelId = cm.HasUser(hashKey); if (channelId < 0) { return; } PartyManager pm = cm.GetChannel(channelId).GetPartyManager(); switch (req.Type) { case PartyUpdateType.CREATE: { if (!pm.CreateParty(hashKey, req.PartyName, out PartyInfo? party)) { SendError(peer, ErrorCode.PARTY_ALREADY_IN_PARTY); return; } UpdatePartyPacket notify = new() { PartyId = party!.PartyId, Type = PartyUpdateType.CREATE, LeaderId = party.LeaderId, PlayerId = hashKey, PartyName = party.PartyName }; byte[] data = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, notify); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 break; } case PartyUpdateType.JOIN: { if (!pm.JoinParty(hashKey, req.PartyId, out PartyInfo? party)) { SendError(peer, ErrorCode.PARTY_JOIN_FAILED); return; } UpdatePartyPacket notify = new() { PartyId = party!.PartyId, Type = PartyUpdateType.JOIN, LeaderId = party.LeaderId, PlayerId = hashKey }; byte[] data = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, notify); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 break; } case PartyUpdateType.LEAVE: { if (!pm.LeaveParty(hashKey, out PartyInfo? party) || party == null) { SendError(peer, ErrorCode.PARTY_NOT_IN_PARTY); return; } UpdatePartyPacket notify = new() { PartyId = party.PartyId, Type = PartyUpdateType.DELETE, LeaderId = party.LeaderId, PlayerId = hashKey }; // 파티가 남아있으면 살린다. if (party.PartyMemberIds.Count > 0) { notify.Type = PartyUpdateType.LEAVE; } byte[] data = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, notify); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 (탈퇴자 포함) break; } case PartyUpdateType.DELETE: { if (!pm.DeleteParty(hashKey, req.PartyId, out PartyInfo? party)) { SendError(peer, ErrorCode.PARTY_DELETE_FAILED); return; } UpdatePartyPacket notify = new() { PartyId = party!.PartyId, Type = PartyUpdateType.DELETE, LeaderId = party.LeaderId }; byte[] data = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, notify); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 break; } case PartyUpdateType.UPDATE: { if (!pm.UpdateParty(hashKey, req.PartyId, req.PartyName, out PartyInfo? party)) { SendError(peer, ErrorCode.PARTY_UPDATE_FAILED); return; } UpdatePartyPacket notify = new() { PartyId = req.PartyId, Type = PartyUpdateType.UPDATE, LeaderId = party?.LeaderId ?? 0, PlayerId = hashKey, PartyName = req.PartyName }; byte[] data = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, notify); BroadcastToChannel(channelId, data); // 채널 전체 파티 목록 갱신 break; } case PartyUpdateType.INVITE: { // 리더만 초대 가능 PartyInfo? myParty = pm.GetPartyByPlayer(hashKey); if (myParty == null || myParty.LeaderId != hashKey) { SendError(peer, ErrorCode.PARTY_JOIN_FAILED); return; } if (myParty.GetPartyMemberCount() >= PartyInfo.partyMemberMax) { SendError(peer, ErrorCode.PARTY_JOIN_FAILED); return; } // 대상 플레이어가 같은 채널에 있는지 확인 int targetId = req.TargetPlayerId; Channel.Channel? ch = cm.GetChannel(channelId); if (ch == null || ch.GetPlayer(targetId) == null) { SendError(peer, ErrorCode.PARTY_JOIN_FAILED); return; } // 대상에게 초대 알림 전송 NetPeer? targetPeer = ch.GetPeer(targetId); if (targetPeer == null) { SendError(peer, ErrorCode.PARTY_JOIN_FAILED); return; } UpdatePartyPacket inviteNotify = new() { PartyId = myParty.PartyId, Type = PartyUpdateType.INVITE, LeaderId = hashKey, PlayerId = targetId, PartyName = myParty.PartyName }; byte[] inviteData = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, inviteNotify); SendTo(targetPeer, inviteData); break; } case PartyUpdateType.KICK: { // 리더만 추방 가능 PartyInfo? myParty2 = pm.GetPartyByPlayer(hashKey); if (myParty2 == null || myParty2.LeaderId != hashKey) { SendError(peer, ErrorCode.PARTY_DELETE_FAILED); return; } int kickTarget = req.TargetPlayerId; if (kickTarget == hashKey) { return; // 자기 자신은 추방 불가 } if (!pm.LeaveParty(kickTarget, out PartyInfo? kickedParty) || kickedParty == null) { SendError(peer, ErrorCode.PARTY_NOT_IN_PARTY); return; } UpdatePartyPacket kickNotify = new() { PartyId = kickedParty.PartyId, Type = PartyUpdateType.KICK, LeaderId = kickedParty.LeaderId, PlayerId = kickTarget }; byte[] kickData = PacketSerializer.Serialize((ushort)PacketCode.UPDATE_PARTY, kickNotify); BroadcastToChannel(channelId, kickData); break; } } } }