using LiteNetLib; using MMOserver.Api; using MMOserver.Game.Channel; using MMOserver.Game.Channel.Maps; 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 OnIntoChannel(NetPeer peer, int hashKey, byte[] payload) { try { IntoChannelPacket packet = Serializer.Deserialize(new ReadOnlyMemory(payload)); ChannelManager cm = ChannelManager.Instance; // 이전에 다른 채널에 있었는지 체크 int preChannelId = cm.HasUser(hashKey); Player? prevPlayer = preChannelId >= 0 ? cm.GetChannel(preChannelId).GetPlayer(hashKey) : null; if (preChannelId >= 0) { Player? player = prevPlayer; // 파티 자동 탈퇴 HandlePartyLeaveOnExit(preChannelId, hashKey); cm.RemoveUser(hashKey); Log.Debug("[GameServer] EXIT_CHANNEL HashKey={Key} PlayerId={PlayerId}", hashKey, preChannelId); // 같은 채널 유저들에게 나갔다고 알림 if (player != null) { SendExitChannelPacket(peer, hashKey, preChannelId, player); } } Channel.Channel newChannel = cm.GetChannel(packet.ChannelId); // 최대 인원 체크 if (newChannel.UserCount >= newChannel.UserCountMax) { Log.Warning("[GameServer] INTO_CHANNEL 채널 인원 초과 HashKey={Key} ChannelId={ChannelId} UserCount={Count}/{Max}", hashKey, packet.ChannelId, newChannel.UserCount, newChannel.UserCountMax); // 이전 채널에서 이미 제거된 경우 → 이전 채널로 복귀 if (preChannelId >= 0 && prevPlayer != null) { cm.AddUser(preChannelId, hashKey, prevPlayer, peer); Log.Information("[GameServer] INTO_CHANNEL 만석 → 이전 채널({ChannelId})로 복귀 HashKey={Key}", preChannelId, hashKey); } byte[] full = PacketSerializer.Serialize((ushort)PacketCode.INTO_CHANNEL, new IntoChannelPacket { ChannelId = -1 }); SendTo(peer, full); return; } // API 서버에서 플레이어 프로필 로드 Player newPlayer = new Player { HashKey = hashKey, PlayerId = hashKey, Nickname = ((Session)peer.Tag).Username ?? "" }; Session? session = peer.Tag as Session; string? username = session?.Username; if (!string.IsNullOrEmpty(username)) { try { RestApi.PlayerProfileResponse? profile = await RestApi.Instance.GetPlayerProfileAsync(username); if (profile != null) { newPlayer.Nickname = string.IsNullOrEmpty(profile.Nickname) ? username : profile.Nickname; newPlayer.Level = profile.Level; newPlayer.MaxHp = (int)profile.MaxHp; newPlayer.Hp = (int)profile.MaxHp; newPlayer.MaxMp = (int)profile.MaxMp; newPlayer.Mp = (int)profile.MaxMp; newPlayer.Experience = profile.Experience; newPlayer.NextExp = profile.NextExp; newPlayer.AttackPower = (float)profile.AttackPower; newPlayer.AttackRange = (float)profile.AttackRange; newPlayer.SprintMultiplier = (float)profile.SprintMultiplier; newPlayer.PosX = (float)profile.LastPosX; newPlayer.PosY = (float)profile.LastPosY; newPlayer.PosZ = (float)profile.LastPosZ; newPlayer.RotY = (float)profile.LastRotY; Log.Information("[GameServer] 프로필 로드 완료 Username={Username} Level={Level} MaxHp={MaxHp}", username, profile.Level, profile.MaxHp); } else { newPlayer.Nickname = username; Log.Warning("[GameServer] 프로필 로드 실패 — 기본값 사용 Username={Username}", username); } } catch (Exception ex) { newPlayer.Nickname = username; Log.Error(ex, "[GameServer] 프로필 로드 예외 Username={Username}", username); } } // 채널 입장 시각 기록 (플레이타임 계산용) ((Session)peer.Tag).ChannelJoinedAt = DateTime.UtcNow; // 채널에 추가 cm.AddUser(packet.ChannelId, hashKey, newPlayer, peer); Log.Debug("[GameServer] INTO_CHANNEL HashKey={Key} ChannelId={ChannelId}", hashKey, packet.ChannelId); // 접속된 모든 유저 정보 전달 SendIntoChannelPacket(peer, hashKey); // 내 정보 전달 SendLoadGame(peer, hashKey); // 초기 맵(로비 1번) 진입 알림 // Channel.AddUser → ChangeMap(1) 에서 이미 맵에 추가됨 PlayerInfo playerInfo = newPlayer.ToPlayerInfo(); int initMapId = newPlayer.CurrentMapId; // 기존 맵 유저들에게 입장 알림 (본인 제외) ChangeMapPacket enterNotify = new() { MapId = initMapId, IsAdd = true, Player = playerInfo }; BroadcastToMap(packet.ChannelId, initMapId, PacketSerializer.Serialize((ushort)PacketCode.CHANGE_MAP, enterNotify), peer); // 본인에게 현재 맵의 플레이어 목록 전달 ChangeMapPacket response = new() { MapId = initMapId }; AMap? initMap = newChannel.GetMap(initMapId); if (initMap != null) { foreach (var (userId, channelPlayer) in initMap.GetUsers()) { if (userId == hashKey) { continue; } response.Players.Add(channelPlayer.ToPlayerInfo()); } } SendTo(peer, PacketSerializer.Serialize((ushort)PacketCode.CHANGE_MAP, response)); } catch (Exception e) { Log.Warning($"[GameServer] Error : {e.Message}"); } } }