160 lines
6.7 KiB
C#
160 lines
6.7 KiB
C#
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<IntoChannelPacket>(new ReadOnlyMemory<byte>(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.Position.X = (float)profile.LastPosX;
|
|
newPlayer.Position.Y = (float)profile.LastPosY;
|
|
newPlayer.Position.Z = (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}");
|
|
}
|
|
}
|
|
}
|