using LiteNetLib; using MMOserver.Api; using MMOserver.Game.Channel; using MMOserver.Game.Channel.Maps; using MMOserver.Game.Party; using MMOserver.Packet; using ProtoBuf; using Serilog; using ServerLib.Packet; using ServerLib.Service; namespace MMOserver.Game.Service; /* * 보스레이드 접속 메시지 핸들러 */ public partial class GameServer : ServerBase { private async void OnIntoBossRaid(NetPeer peer, int hashKey, byte[] payload) { try { IntoBossRaidPacket packet = Serializer.Deserialize(new ReadOnlyMemory(payload)); ChannelManager cm = ChannelManager.Instance; int channelId = cm.HasUser(hashKey); if (channelId < 0) { return; } Channel.Channel channel = cm.GetChannel(channelId); // 파티 조회 PartyInfo? party = channel.GetPartyManager().GetPartyByPlayer(hashKey); if (party == null) { Log.Warning("[GameServer] INTO_BOSS_RAID 파티 없음 HashKey={Key}", hashKey); SendTo(peer, PacketSerializer.Serialize((ushort)PacketCode.INTO_BOSS_RAID, new IntoBossRaidPacket { RaidId = packet.RaidId, IsSuccess = false, Reason = "파티에 속해있지 않습니다." })); return; } // 파티장만 요청 가능 if (party.LeaderId != hashKey) { Log.Warning("[GameServer] INTO_BOSS_RAID 파티장 아님 HashKey={Key} LeaderId={LeaderId}", hashKey, party.LeaderId); SendTo(peer, PacketSerializer.Serialize((ushort)PacketCode.INTO_BOSS_RAID, new IntoBossRaidPacket { RaidId = packet.RaidId, IsSuccess = false, Reason = "파티장만 보스 레이드를 시작할 수 있습니다." })); return; } // API로 접속 체크 List userNames = new List(); foreach (int memberId in party.PartyMemberIds) { Player? memberPlayer = channel.GetPlayer(memberId); if (memberPlayer != null) { userNames.Add(memberPlayer.Nickname); } } BossRaidResult? result = await RestApi.Instance.BossRaidAccessAsync(userNames, packet.RaidId); // await 이후 — 공유 자원 접근 보호 lock (sessionLock) { // 입장 실패 if (result == null || result.BossId <= 0) { SendTo(peer, PacketSerializer.Serialize((ushort)PacketCode.INTO_BOSS_RAID, new IntoBossRaidPacket { RaidId = -1, IsSuccess = false, Reason = "보스 레이드 방을 배정받지 못했습니다. 잠시 후 다시 시도해주세요." })); Log.Debug("[GameServer] INTO_BOSS_RAID HashKey={Key} PartyId={PartyId} AssignedRaidMapId={RaidId} Failed", hashKey, party.PartyId, -1); return; } // await 이후 상태 재검증 channelId = cm.HasUser(hashKey); if (channelId < 0) { return; } channel = cm.GetChannel(channelId); party = channel.GetPartyManager().GetPartyByPlayer(hashKey); if (party == null) { return; } // 레이드 맵 할당 (미사용 맵 탐색 → 없으면 동적 생성) int assignedRaidMapId = channel.GetOrCreateAvailableRaidMap(); // 진행중 맵으로 등록 channel.AddInstanceMap(assignedRaidMapId); // 파티원 전체 레이드 맵으로 이동 + 각자에게 알림 foreach (int memberId in party.PartyMemberIds) { Player? memberPlayer = channel.GetPlayer(memberId); if (memberPlayer == null) { continue; } // 이전 맵 캐싱 (레이드 종료 후 복귀용) memberPlayer.PreviousMapId = memberPlayer.CurrentMapId; int oldMapId = memberPlayer.CurrentMapId; channel.ChangeMap(memberId, memberPlayer, assignedRaidMapId); if (!sessions.TryGetValue(memberId, out NetPeer? memberPeer)) { continue; } PlayerInfo memberInfo = memberPlayer.ToPlayerInfo(); // 기존 맵 유저들에게 퇴장 알림 ChangeMapPacket exitNotify = new() { MapId = oldMapId, IsAdd = false, Player = memberInfo }; BroadcastToMap(channelId, oldMapId, PacketSerializer.Serialize((ushort)PacketCode.CHANGE_MAP, exitNotify)); // 새 맵 유저들에게 입장 알림 (본인 제외) ChangeMapPacket enterNotify = new() { MapId = assignedRaidMapId, IsAdd = true, Player = memberInfo }; BroadcastToMap(channelId, assignedRaidMapId, PacketSerializer.Serialize((ushort)PacketCode.CHANGE_MAP, enterNotify), memberPeer); // 본인에게 새 맵 플레이어 목록 전달 ChangeMapPacket response = new() { MapId = assignedRaidMapId }; AMap? raidMap = channel.GetMap(assignedRaidMapId); if (raidMap != null) { foreach ((int uid, Player channelPlayer) in raidMap.GetUsers()) { if (uid != memberId) { response.Players.Add(channelPlayer.ToPlayerInfo()); } } } SendTo(memberPeer, PacketSerializer.Serialize((ushort)PacketCode.CHANGE_MAP, response)); // 각 파티원에게 레이드 입장 결과 전달 (Session, Token 포함) SendTo(memberPeer, PacketSerializer.Serialize((ushort)PacketCode.INTO_BOSS_RAID, new IntoBossRaidPacket { RaidId = assignedRaidMapId, IsSuccess = true, Session = result.SessionName, Token = result.Tokens != null && result.Tokens.TryGetValue(memberPlayer.Nickname, out string? memberToken) ? memberToken : "" })); } Log.Debug("[GameServer] INTO_BOSS_RAID HashKey={Key} PartyId={PartyId} AssignedRaidMapId={RaidId}", hashKey, party.PartyId, assignedRaidMapId); } } catch (Exception e) { Log.Warning($"[GameServer] Error : {e.Message}"); } } }