feat : 맵관리 코드 추가 작업

This commit is contained in:
qornwh1
2026-03-16 14:50:29 +09:00
parent f564199cb5
commit e4429177db
9 changed files with 282 additions and 21 deletions

View File

@@ -1,27 +1,45 @@
using LiteNetLib; using LiteNetLib;
using MMOserver.Game.Channel.Maps; using MMOserver.Game.Channel.Maps;
using MMOserver.Game.Channel.Maps.InstanceDungeun;
using MMOserver.Game.Party; using MMOserver.Game.Party;
namespace MMOserver.Game.Channel; namespace MMOserver.Game.Channel;
public class Channel public class Channel
{ {
// 로비 // 채널 내 유저 NetPeer (hashKey → NetPeer) — BroadcastToChannel 교차 조회 제거용
private Robby robby = new Robby(); private readonly Dictionary<int, NetPeer> connectPeers = new();
// 파티
private PartyManager partyManager = new PartyManager();
// 채널 내 유저 상태 (hashKey → Player) // 채널 내 유저 상태 (hashKey → Player)
private Dictionary<int, Player> connectUsers = new Dictionary<int, Player>(); private readonly Dictionary<int, Player> connectUsers = new();
// 채널 내 유저 NetPeer (hashKey → NetPeer) — BroadcastToChannel 교차 조회 제거용 // 채널 맵 관리
private Dictionary<int, NetPeer> connectPeers = new Dictionary<int, NetPeer>(); private readonly Dictionary<int, AMap> maps = new();
// 파티
private readonly PartyManager partyManager = new();
public Channel(int channelId)
{
ChannelId = channelId;
// 일단 하드코딩으로 맵 생성
{
// 로비
maps.Add(1, new Robby(1));
// 인던
int defaultValue = 10;
for (int i = 1; i <= 10; i++)
{
maps.Add(i + defaultValue, new BossInstance(i + defaultValue));
}
}
}
public int ChannelId public int ChannelId
{ {
get; get;
private set;
} }
public int UserCount public int UserCount
@@ -36,25 +54,48 @@ public class Channel
private set; private set;
} = 100; } = 100;
public Channel(int channelId)
{
ChannelId = channelId;
}
public void AddUser(int userId, Player player, NetPeer peer) public void AddUser(int userId, Player player, NetPeer peer)
{ {
connectUsers[userId] = player; connectUsers[userId] = player;
connectPeers[userId] = peer; connectPeers[userId] = peer;
UserCount++; UserCount++;
// 처음 접속 시 1번 맵(로비)으로 입장
ChangeMap(userId, player, 1);
} }
public void RemoveUser(int userId) public void RemoveUser(int userId)
{ {
// 현재 맵에서도 제거
if (connectUsers.TryGetValue(userId, out Player? player))
{
maps[player.CurrentMapId].RemoveUser(userId);
}
connectUsers.Remove(userId); connectUsers.Remove(userId);
connectPeers.Remove(userId); connectPeers.Remove(userId);
UserCount--; UserCount--;
} }
// 맵 이동 (현재 맵 제거 → 새 맵 추가 → CurrentMapId 갱신)
public bool ChangeMap(int userId, Player player, int mapId)
{
if (!maps.TryGetValue(mapId, out AMap? newMap))
{
return false;
}
// 기존 맵에서 제거
if (maps.TryGetValue(player.CurrentMapId, out AMap? oldMap))
{
oldMap.RemoveUser(userId);
}
newMap.AddUser(userId, player);
player.CurrentMapId = mapId;
return true;
}
// 재연결(WiFi→LTE 등) 시 동일 유저의 peer 교체 // 재연결(WiFi→LTE 등) 시 동일 유저의 peer 교체
public void UpdatePeer(int userId, NetPeer peer) public void UpdatePeer(int userId, NetPeer peer)
{ {
@@ -96,10 +137,18 @@ public class Channel
return -1; return -1;
} }
// 로비 가져옴 // 맵들 가져옴
public Robby GetRobby() public Dictionary<int, AMap> GetMaps()
{ {
return robby; return maps;
}
// 맵 가져옴
public AMap? GetMap(int mapId)
{
AMap? map = null;
maps.TryGetValue(mapId, out map);
return map;
} }
// 파티매니저 가져옴 // 파티매니저 가져옴

View File

@@ -0,0 +1,24 @@
namespace MMOserver.Game.Channel.Maps;
public abstract class AMap
{
private Dictionary<int, Player> users = new Dictionary<int, Player>();
public abstract EnumMap GetMapType();
public abstract int GetMapId();
public void AddUser(int userId, Player player)
{
users[userId] = player;
}
public void RemoveUser(int userId)
{
users.Remove(userId);
}
public Dictionary<int, Player> GetUsers()
{
return users;
}
}

View File

@@ -0,0 +1,8 @@
namespace MMOserver.Game.Channel.Maps;
public enum EnumMap : int
{
NONE = 0,
ROBBY = 1,
INSTANCE = 10,
}

View File

@@ -0,0 +1,34 @@
using MMOserver.Game.Channel.Maps;
using MMOserver.Game.Engine;
namespace MMOserver.Game.Channel.Maps.InstanceDungeun;
// 인스턴스 보스 맵에 들어갈때 쓰는 것
public class BossInstance : AMap
{
private EnumMap enumMap;
private int mapId;
// 마을 시작 지점 넣어 둔다.
public static Vector3 StartPosition
{
get;
set;
} = new Vector3(0, 0, 0);
public BossInstance(int mapId, EnumMap enumMap = EnumMap.INSTANCE)
{
this.enumMap = enumMap;
this.mapId = mapId;
}
public override EnumMap GetMapType()
{
return enumMap;
}
public override int GetMapId()
{
return mapId;
}
}

View File

@@ -2,8 +2,11 @@
namespace MMOserver.Game.Channel.Maps; namespace MMOserver.Game.Channel.Maps;
public class Robby public class Robby : AMap
{ {
private EnumMap enumMap;
private int mapId;
// 마을 시작 지점 넣어 둔다. // 마을 시작 지점 넣어 둔다.
public static Vector3 StartPosition public static Vector3 StartPosition
{ {
@@ -11,7 +14,19 @@ public class Robby
set; set;
} = new Vector3(0, 0, 0); } = new Vector3(0, 0, 0);
public Robby() public Robby(int mapId, EnumMap enumMap = EnumMap.ROBBY)
{ {
this.enumMap = enumMap;
this.mapId = mapId;
}
public override EnumMap GetMapType()
{
return enumMap;
}
public override int GetMapId()
{
return mapId;
} }
} }

View File

@@ -27,6 +27,8 @@ public class GameServer : ServerBase
[(ushort)PacketCode.TRANSFORM_PLAYER] = OnTransformPlayer, [(ushort)PacketCode.TRANSFORM_PLAYER] = OnTransformPlayer,
[(ushort)PacketCode.ACTION_PLAYER] = OnActionPlayer, [(ushort)PacketCode.ACTION_PLAYER] = OnActionPlayer,
[(ushort)PacketCode.STATE_PLAYER] = OnStatePlayer, [(ushort)PacketCode.STATE_PLAYER] = OnStatePlayer,
[(ushort)PacketCode.CHANGE_MAP] = OnChangeMap,
[(ushort)PacketCode.PARTY_CHANGE_MAP] = OnPartyChangeMap,
[(ushort)PacketCode.REQUEST_PARTY] = OnRequestParty, [(ushort)PacketCode.REQUEST_PARTY] = OnRequestParty,
[(ushort)PacketCode.CHAT] = OnChat, [(ushort)PacketCode.CHAT] = OnChat,
}; };
@@ -78,7 +80,8 @@ public class GameServer : ServerBase
{ {
HashKey = hashKey, HashKey = hashKey,
PlayerId = hashKey, PlayerId = hashKey,
Nickname = hashKey.ToString() Nickname = hashKey.ToString(),
CurrentMapId = 1
}; };
cm.AddUser(1, hashKey, newPlayer, peer); cm.AddUser(1, hashKey, newPlayer, peer);
@@ -421,9 +424,11 @@ public class GameServer : ServerBase
{ {
HashKey = hashKey, HashKey = hashKey,
PlayerId = hashKey, PlayerId = hashKey,
Nickname = hashKey.ToString() Nickname = hashKey.ToString(),
CurrentMapId = 1
}; };
// 채널에 추가
cm.AddUser(packet.ChannelId, hashKey, newPlayer, peer); cm.AddUser(packet.ChannelId, hashKey, newPlayer, peer);
Log.Debug("[GameServer] INTO_CHANNEL HashKey={Key} ChannelId={ChannelId}", hashKey, packet.ChannelId); Log.Debug("[GameServer] INTO_CHANNEL HashKey={Key} ChannelId={ChannelId}", hashKey, packet.ChannelId);
@@ -828,6 +833,84 @@ public class GameServer : ServerBase
} }
} }
private void OnChangeMap(NetPeer peer, int hashKey, byte[] payload)
{
ChangeMapPacket packet = Serializer.Deserialize<ChangeMapPacket>(new ReadOnlyMemory<byte>(payload));
ChannelManager cm = ChannelManager.Instance;
int channelId = cm.HasUser(hashKey);
if (channelId < 0)
{
return;
}
Channel.Channel channel = cm.GetChannel(channelId);
Player? player = channel.GetPlayer(hashKey);
if (player != null)
{
if (!channel.ChangeMap(hashKey, player, packet.MapId))
{
// 맵이 없으면 무효
Log.Warning("[GameServer] CHANGE_MAP 유효하지 않은 맵 HashKey={Key} MapId={MapId}", hashKey, packet.MapId);
return;
}
// 이동 완료 알림
byte[] data = PacketSerializer.Serialize<ChangeMapPacket>((ushort)PacketCode.CHANGE_MAP, packet);
SendTo(peer, data);
Log.Debug("[GameServer] CHANGE_MAP HashKey={Key} MapId={MapId}", hashKey, packet.MapId);
}
}
private void OnPartyChangeMap(NetPeer peer, int hashKey, byte[] payload)
{
PartyChangeMapPacket packet = Serializer.Deserialize<PartyChangeMapPacket>(new ReadOnlyMemory<byte>(payload));
ChannelManager cm = ChannelManager.Instance;
int channelId = cm.HasUser(hashKey);
if (channelId < 0)
{
return;
}
Channel.Channel channel = cm.GetChannel(channelId);
// 맵 유효성 체크
if (channel.GetMap(packet.MapId) == null)
{
Log.Warning("[GameServer] PARTY_CHANGE_MAP 유효하지 않은 맵 HashKey={Key} MapId={MapId}", hashKey, packet.MapId);
return;
}
// 파티 확인
PartyInfo? party = channel.GetPartyManager().GetParty(packet.PartyId);
if (party == null)
{
Log.Warning("[GameServer] PARTY_CHANGE_MAP 파티 없음 HashKey={Key} PartyId={PartyId}", hashKey, packet.PartyId);
return;
}
// 파티원 전체 맵 이동 + 각자에게 알림
byte[] data = PacketSerializer.Serialize<PartyChangeMapPacket>((ushort)PacketCode.PARTY_CHANGE_MAP, packet);
foreach (int memberId in party.PartyMemberIds)
{
Player? memberPlayer = channel.GetPlayer(memberId);
if (memberPlayer == null)
{
continue;
}
channel.ChangeMap(memberId, memberPlayer, packet.MapId);
if (sessions.TryGetValue(memberId, out NetPeer? memberPeer))
{
SendTo(memberPeer, data);
}
}
Log.Debug("[GameServer] PARTY_CHANGE_MAP HashKey={Key} PartyId={PartyId} MapId={MapId}", hashKey, packet.PartyId, packet.MapId);
}
// 채널 퇴장/연결 해제 시 파티 자동 탈퇴 처리 // 채널 퇴장/연결 해제 시 파티 자동 탈퇴 처리
private void HandlePartyLeaveOnExit(int channelId, int hashKey) private void HandlePartyLeaveOnExit(int channelId, int hashKey)
{ {

View File

@@ -74,4 +74,11 @@ public class Player
get; get;
set; set;
} }
// 현재 위치한 맵 ID
public int CurrentMapId
{
get;
set;
}
} }

View File

@@ -709,6 +709,41 @@ public class ChatPacket
set; set;
} }
} }
// ============================================================
// 맵 이동
// ============================================================
// CHANGE_MAP (클라 -> 서버 & 서버 -> 클라)
[ProtoContract]
public class ChangeMapPacket
{
[ProtoMember(1)]
public int MapId
{
get;
set;
}
}
// PARTY_CHANGE_MAP (클라 -> 서버 & 서버 -> 클라)
[ProtoContract]
public class PartyChangeMapPacket
{
[ProtoMember(1)]
public int MapId
{
get;
set;
}
[ProtoMember(2)]
public int PartyId
{
get;
set;
}
}
// ============================================================ // ============================================================
// 파티 // 파티

View File

@@ -30,6 +30,12 @@ public enum PacketCode : ushort
// 채널 나가기 (클라 -> 서버) // 채널 나가기 (클라 -> 서버)
EXIT_CHANNEL, EXIT_CHANNEL,
// 맵 이동
CHANGE_MAP,
// 단체로 맵 이동
PARTY_CHANGE_MAP,
// 플레이어 위치, 방향 (서버 -> 클라 \ 클라 -> 서버) // 플레이어 위치, 방향 (서버 -> 클라 \ 클라 -> 서버)
TRANSFORM_PLAYER, TRANSFORM_PLAYER,