Compare commits
3 Commits
main
...
feature/zo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a777dc5f99 | ||
|
|
162d04d005 | ||
|
|
17ba88e841 |
@@ -13,14 +13,11 @@ namespace MMOserver.Game.Channel;
|
|||||||
* - 인스턴스 던전 관리
|
* - 인스턴스 던전 관리
|
||||||
* - 동적 맵 id 생성
|
* - 동적 맵 id 생성
|
||||||
*/
|
*/
|
||||||
public class Channel
|
public class Channel : UserContainer
|
||||||
{
|
{
|
||||||
// 채널 내 유저 NetPeer (hashKey → NetPeer) — BroadcastToChannel 교차 조회 제거용
|
// 채널 내 유저 NetPeer (hashKey → NetPeer) — BroadcastToChannel 교차 조회 제거용
|
||||||
private readonly Dictionary<int, NetPeer> connectPeers = new();
|
private readonly Dictionary<int, NetPeer> connectPeers = new();
|
||||||
|
|
||||||
// 채널 내 유저 상태 (hashKey → Player)
|
|
||||||
private readonly Dictionary<int, Player> connectUsers = new();
|
|
||||||
|
|
||||||
// 채널 맵 관리
|
// 채널 맵 관리
|
||||||
private readonly Dictionary<int, AMap> maps = new();
|
private readonly Dictionary<int, AMap> maps = new();
|
||||||
|
|
||||||
@@ -37,17 +34,32 @@ public class Channel
|
|||||||
{
|
{
|
||||||
ChannelId = channelId;
|
ChannelId = channelId;
|
||||||
|
|
||||||
// 일단 하드코딩으로 맵 생성
|
foreach (MapConfigData config in MapLoader.Data.Maps)
|
||||||
{
|
{
|
||||||
// 로비
|
AMap? map = MapLoader.ParseMapType(config.MapType) switch
|
||||||
maps.Add(1, new Robby(1));
|
|
||||||
|
|
||||||
// 인던
|
|
||||||
int defaultValue = 1000;
|
|
||||||
for (int i = 1; i <= 10; i++)
|
|
||||||
{
|
{
|
||||||
maps.Add(i + defaultValue, new BossInstance(i + defaultValue));
|
EnumMap.ROBBY => new Robby(config.MapId),
|
||||||
|
EnumMap.DUNGEON => new BossInstance(config.MapId),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 맵정보
|
||||||
|
map.SetZoneConfig(config.Rows, config.Cols, config.ZoneSize);
|
||||||
|
// 맵내에 존 생성
|
||||||
|
map.CreateZones(config.Zones);
|
||||||
|
maps.Add(config.MapId, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 인던은 동적 생성 유지
|
||||||
|
int defaultValue = 1000;
|
||||||
|
for (int i = 1; i <= 10; i++)
|
||||||
|
{
|
||||||
|
maps.Add(i + defaultValue, new BossInstance(i + defaultValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +68,6 @@ public class Channel
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int UserCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int UserCountMax
|
public int UserCountMax
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -70,28 +76,26 @@ public class Channel
|
|||||||
|
|
||||||
public void AddUser(int userId, Player player, NetPeer peer)
|
public void AddUser(int userId, Player player, NetPeer peer)
|
||||||
{
|
{
|
||||||
connectUsers[userId] = player;
|
base.AddUser(userId, player);
|
||||||
|
users[userId] = player;
|
||||||
connectPeers[userId] = peer;
|
connectPeers[userId] = peer;
|
||||||
UserCount++;
|
|
||||||
|
|
||||||
// 처음 접속 시 1번 맵(로비)으로 입장
|
// 처음 접속 시 1번 맵(로비)으로 입장
|
||||||
ChangeMap(userId, player, 1);
|
ChangeMap(userId, player, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveUser(int userId)
|
public override void RemoveUser(int userId)
|
||||||
{
|
{
|
||||||
// 현재 맵에서도 제거
|
// 현재 맵에서 제거 (AMap.RemoveUser가 존까지 처리)
|
||||||
if (connectUsers.TryGetValue(userId, out Player? player) &&
|
if (users.TryGetValue(userId, out Player? player))
|
||||||
maps.TryGetValue(player.CurrentMapId, out AMap? currentMap))
|
|
||||||
{
|
{
|
||||||
currentMap.RemoveUser(userId);
|
if (maps.TryGetValue(player.CurrentMapId, out AMap? currentMap))
|
||||||
}
|
{
|
||||||
|
currentMap.RemoveUser(userId);
|
||||||
if (connectUsers.Remove(userId))
|
}
|
||||||
{
|
|
||||||
UserCount--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base.RemoveUser(userId);
|
||||||
connectPeers.Remove(userId);
|
connectPeers.Remove(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +113,7 @@ public class Channel
|
|||||||
oldMap.RemoveUser(userId);
|
oldMap.RemoveUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AMap.AddUser가 존 0 배치 + CurrentZoneId = 0 처리
|
||||||
newMap.AddUser(userId, player);
|
newMap.AddUser(userId, player);
|
||||||
player.CurrentMapId = mapId;
|
player.CurrentMapId = mapId;
|
||||||
return true;
|
return true;
|
||||||
@@ -120,31 +125,12 @@ public class Channel
|
|||||||
connectPeers[userId] = peer;
|
connectPeers[userId] = peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 채널 내 모든 유저의 hashKey 반환 (채널 입장 시 기존 플레이어 목록 조회용)
|
|
||||||
public IEnumerable<int> GetConnectUsers()
|
|
||||||
{
|
|
||||||
return connectUsers.Keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 채널 내 모든 Player 반환
|
|
||||||
public IEnumerable<Player> GetPlayers()
|
|
||||||
{
|
|
||||||
return connectUsers.Values;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 채널 내 모든 NetPeer 반환 — BroadcastToChannel 전용 (sessions 교차 조회 불필요)
|
// 채널 내 모든 NetPeer 반환 — BroadcastToChannel 전용 (sessions 교차 조회 불필요)
|
||||||
public IEnumerable<NetPeer> GetConnectPeers()
|
public IEnumerable<NetPeer> GetConnectPeers()
|
||||||
{
|
{
|
||||||
return connectPeers.Values;
|
return connectPeers.Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 특정 유저의 Player 반환
|
|
||||||
public Player? GetPlayer(int userId)
|
|
||||||
{
|
|
||||||
connectUsers.TryGetValue(userId, out Player? player);
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 특정 유저의 NetPeer 반환
|
// 특정 유저의 NetPeer 반환
|
||||||
public NetPeer? GetPeer(int userId)
|
public NetPeer? GetPeer(int userId)
|
||||||
{
|
{
|
||||||
@@ -152,9 +138,10 @@ public class Channel
|
|||||||
return peer;
|
return peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int HasUser(int userId)
|
// 유저가 존재하면 채널id를 넘긴다
|
||||||
|
public override int HasUser(int userId)
|
||||||
{
|
{
|
||||||
if (connectUsers.ContainsKey(userId))
|
if (users.ContainsKey(userId))
|
||||||
{
|
{
|
||||||
return ChannelId;
|
return ChannelId;
|
||||||
}
|
}
|
||||||
@@ -217,6 +204,4 @@ public class Channel
|
|||||||
{
|
{
|
||||||
return partyManager;
|
return partyManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : 채널 가져오기
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using MMOserver.Game.Channel.Maps.ZoneData;
|
||||||
|
using MMOserver.Game.Engine;
|
||||||
|
|
||||||
namespace MMOserver.Game.Channel.Maps;
|
namespace MMOserver.Game.Channel.Maps;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5,25 +8,102 @@ namespace MMOserver.Game.Channel.Maps;
|
|||||||
* - 각 존 관리
|
* - 각 존 관리
|
||||||
* - 맵의 유저 관리
|
* - 맵의 유저 관리
|
||||||
*/
|
*/
|
||||||
public abstract class AMap
|
public abstract class AMap : UserContainer
|
||||||
{
|
{
|
||||||
private Dictionary<int, Player> users = new Dictionary<int, Player>();
|
|
||||||
|
|
||||||
public abstract EnumMap GetMapType();
|
public abstract EnumMap GetMapType();
|
||||||
public abstract int GetMapId();
|
public abstract int GetMapId();
|
||||||
|
|
||||||
public void AddUser(int userId, Player player)
|
// 존 그리드 설정 (MapLoader에서 주입)
|
||||||
|
public int Rows { get; private set; }
|
||||||
|
public int Cols { get; private set; }
|
||||||
|
public int ZoneSize { get; private set; }
|
||||||
|
|
||||||
|
// 존 관리
|
||||||
|
private readonly Dictionary<int, Zone> zones = new();
|
||||||
|
|
||||||
|
public void SetZoneConfig(int rows, int cols, int zoneSize)
|
||||||
{
|
{
|
||||||
users[userId] = player;
|
Rows = rows;
|
||||||
|
Cols = cols;
|
||||||
|
ZoneSize = zoneSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveUser(int userId)
|
// 존 생성
|
||||||
|
public void CreateZones(List<ZoneConfigData> zoneConfigs)
|
||||||
{
|
{
|
||||||
users.Remove(userId);
|
foreach (ZoneConfigData config in zoneConfigs)
|
||||||
|
{
|
||||||
|
Vector3 position = new Vector3(config.CenterX, 0, config.CenterZ);
|
||||||
|
Vector3 size = new Vector3(ZoneSize, 0, ZoneSize);
|
||||||
|
Zone zone = new Zone(this, config.ZoneId, config.Row, config.Col, position, size);
|
||||||
|
AddZone(zone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<int, Player> GetUsers()
|
public void AddZone(Zone zone)
|
||||||
{
|
{
|
||||||
return users;
|
zones[zone.ZoneId] = zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Zone? GetZone(int zoneId)
|
||||||
|
{
|
||||||
|
zones.TryGetValue(zoneId, out Zone? zone);
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 현재 맵의 근처 존 가져오기
|
||||||
|
public virtual (int, int, int, int) GetNearZone(int zoneId, Vector3? position)
|
||||||
|
{
|
||||||
|
return (zoneId, -1, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 좌상단/우상단/우하단/좌하단 판정
|
||||||
|
public int ZoneAt(int row, int col)
|
||||||
|
{
|
||||||
|
return (row >= 0 && row < Rows && col >= 0 && col < Cols) ? row * Cols + col : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 맵 입장 시 0번 존에 자동 배치
|
||||||
|
public override void AddUser(int userId, Player player)
|
||||||
|
{
|
||||||
|
base.AddUser(userId, player);
|
||||||
|
player.CurrentZoneId = 0;
|
||||||
|
GetZone(0)?.AddUser(userId, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 맵 퇴장 시 현재 존에서 자동 제거
|
||||||
|
public override void RemoveUser(int userId)
|
||||||
|
{
|
||||||
|
if (users.TryGetValue(userId, out Player? player))
|
||||||
|
{
|
||||||
|
GetZone(player.CurrentZoneId)?.RemoveUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.RemoveUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이동시 zone업데이트
|
||||||
|
public bool UpdatePlayerZone(int userId, Player player)
|
||||||
|
{
|
||||||
|
// 플레이어의 위치는 이동한 상태
|
||||||
|
int col = (int)(player.Position.X / ZoneSize);
|
||||||
|
int row = (int)(player.Position.Z / ZoneSize);
|
||||||
|
int newZoneId = ZoneAt(row, col);
|
||||||
|
|
||||||
|
if (newZoneId == player.CurrentZoneId)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 범위 밖으로 이동한 상태 일단 존은 그대로 둔다.
|
||||||
|
if (newZoneId < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetZone(player.CurrentZoneId)?.RemoveUser(userId);
|
||||||
|
GetZone(newZoneId)!.AddUser(userId, player);
|
||||||
|
player.CurrentZoneId = newZoneId;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
MMOTestServer/MMOserver/Game/Channel/Maps/MapLoader.cs
Normal file
60
MMOTestServer/MMOserver/Game/Channel/Maps/MapLoader.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using MMOserver.Game.Channel.Maps.InstanceDungeun;
|
||||||
|
|
||||||
|
namespace MMOserver.Game.Channel.Maps;
|
||||||
|
|
||||||
|
// Zone 데이터 구조
|
||||||
|
public record ZoneConfigData(
|
||||||
|
int ZoneId,
|
||||||
|
int Row,
|
||||||
|
int Col,
|
||||||
|
int CenterX,
|
||||||
|
int CenterZ
|
||||||
|
);
|
||||||
|
|
||||||
|
// Map 데이터 구조
|
||||||
|
public record MapConfigData(
|
||||||
|
int MapId,
|
||||||
|
string MapType,
|
||||||
|
int Rows,
|
||||||
|
int Cols,
|
||||||
|
int ZoneSize,
|
||||||
|
List<ZoneConfigData> Zones
|
||||||
|
);
|
||||||
|
|
||||||
|
// 한 채널의 Map정보 List
|
||||||
|
public record MapFileData(
|
||||||
|
List<MapConfigData> Maps
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* maps.json 로더
|
||||||
|
* - JSON 파싱만 담당
|
||||||
|
* - Program.cs에서 Initialize() 1회 호출
|
||||||
|
*/
|
||||||
|
public static class MapLoader
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions Options = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
public static MapFileData Data { get; private set; } = null!;
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText("maps.json");
|
||||||
|
Data = JsonSerializer.Deserialize<MapFileData>(json, Options) ?? throw new InvalidOperationException("maps.json 파싱 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumMap ParseMapType(string mapType)
|
||||||
|
{
|
||||||
|
return mapType.ToUpper() switch
|
||||||
|
{
|
||||||
|
"ROBBY" => EnumMap.ROBBY,
|
||||||
|
"DUNGEON" => EnumMap.DUNGEON,
|
||||||
|
"INSTANCE" => EnumMap.INSTANCE,
|
||||||
|
_ => EnumMap.NONE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using MMOserver.Game.Engine;
|
using MMOserver.Game.Channel.Maps.ZoneData;
|
||||||
|
using MMOserver.Game.Engine;
|
||||||
|
|
||||||
namespace MMOserver.Game.Channel.Maps;
|
namespace MMOserver.Game.Channel.Maps;
|
||||||
|
|
||||||
@@ -7,10 +8,10 @@ namespace MMOserver.Game.Channel.Maps;
|
|||||||
*/
|
*/
|
||||||
public class Robby : AMap
|
public class Robby : AMap
|
||||||
{
|
{
|
||||||
private EnumMap enumMap;
|
private readonly EnumMap enumMap;
|
||||||
private int mapId;
|
private readonly int mapId;
|
||||||
|
|
||||||
public Robby(int mapId, EnumMap enumMap = EnumMap.ROBBY)
|
public Robby(int mapId, EnumMap enumMap = EnumMap.ROBBY)
|
||||||
{
|
{
|
||||||
this.enumMap = enumMap;
|
this.enumMap = enumMap;
|
||||||
this.mapId = mapId;
|
this.mapId = mapId;
|
||||||
@@ -25,4 +26,31 @@ public class Robby : AMap
|
|||||||
{
|
{
|
||||||
return mapId;
|
return mapId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override (int, int, int, int) GetNearZone(int zoneId, Vector3? position)
|
||||||
|
{
|
||||||
|
if (position == null || zoneId < 0)
|
||||||
|
{
|
||||||
|
return base.GetNearZone(zoneId, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zone? zone = GetZone(zoneId);
|
||||||
|
if (zone == null)
|
||||||
|
{
|
||||||
|
return base.GetNearZone(zoneId, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
int quadrant = zone.GetFourquadrant(position);
|
||||||
|
int row = zone.Row;
|
||||||
|
int col = zone.Col;
|
||||||
|
|
||||||
|
return quadrant switch
|
||||||
|
{
|
||||||
|
1 => (zoneId, ZoneAt(row - 1, col), ZoneAt(row, col - 1), ZoneAt(row - 1, col - 1)), // 위, 좌, 좌상단
|
||||||
|
2 => (zoneId, ZoneAt(row - 1, col), ZoneAt(row, col + 1), ZoneAt(row - 1, col + 1)), // 위, 우, 우상단
|
||||||
|
3 => (zoneId, ZoneAt(row + 1, col), ZoneAt(row, col + 1), ZoneAt(row + 1, col + 1)), // 아래, 우, 우하단
|
||||||
|
4 => (zoneId, ZoneAt(row + 1, col), ZoneAt(row, col - 1), ZoneAt(row + 1, col - 1)), // 아래, 좌, 좌하단
|
||||||
|
_ => base.GetNearZone(zoneId, position) // 0 = 중앙, 현재 존만
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
89
MMOTestServer/MMOserver/Game/Channel/Maps/ZoneData/Zone.cs
Normal file
89
MMOTestServer/MMOserver/Game/Channel/Maps/ZoneData/Zone.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using MMOserver.Game.Engine;
|
||||||
|
|
||||||
|
namespace MMOserver.Game.Channel.Maps.ZoneData;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 존 단위로 변경시킨다.
|
||||||
|
* 기존 맵 단위로 유저관리를 존 단위로 나눈다.
|
||||||
|
* 마을 존 / 전투 필드 존 타입을 분기 시킨다.
|
||||||
|
*/
|
||||||
|
public class Zone : UserContainer
|
||||||
|
{
|
||||||
|
public Zone(AMap map, int zoneId, int row, int col, Vector3 position, Vector3 size)
|
||||||
|
{
|
||||||
|
Map = new WeakReference<AMap>(map);
|
||||||
|
ZoneId = zoneId;
|
||||||
|
Row = row;
|
||||||
|
Col = col;
|
||||||
|
Position = position;
|
||||||
|
ZoneSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map은 약한참조를 들고 있는다 순환참조 방지
|
||||||
|
public WeakReference<AMap> Map
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 그리드 인덱스 (zoneId = Row * Cols + Col)
|
||||||
|
public int ZoneId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
public int Row
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
public int Col
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 존 중심 좌표
|
||||||
|
public Vector3 Position
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 존 사이즈
|
||||||
|
public Vector3 ZoneSize
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 플레이어 위치 기준 현재 존 내 분면 반환
|
||||||
|
public int GetFourquadrant(Vector3 position)
|
||||||
|
{
|
||||||
|
float dx = position.X - Position.X;
|
||||||
|
float dz = position.Z - Position.Z;
|
||||||
|
|
||||||
|
// 중앙 영역 (존 크기의 1/4 이내) → 현재 존만 브로드캐스트
|
||||||
|
float threshold = ZoneSize.X / 4;
|
||||||
|
if (Math.Abs(dx) <= threshold && Math.Abs(dz) <= threshold)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (dx <= 0 && dz <= 0)
|
||||||
|
{
|
||||||
|
// 좌상단
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (dx > 0 && dz <= 0)
|
||||||
|
{
|
||||||
|
// 우상단
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (dx > 0 && dz > 0)
|
||||||
|
{
|
||||||
|
// 우하단
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
// 좌하단
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
MMOTestServer/MMOserver/Game/Channel/UserContainer.cs
Normal file
47
MMOTestServer/MMOserver/Game/Channel/UserContainer.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using MMOserver.Game.Service;
|
||||||
|
using ServerLib.Service;
|
||||||
|
|
||||||
|
namespace MMOserver.Game.Channel;
|
||||||
|
|
||||||
|
public abstract class UserContainer
|
||||||
|
{
|
||||||
|
protected readonly Dictionary<int, Player> users = new();
|
||||||
|
|
||||||
|
public virtual void AddUser(int userId, Player player)
|
||||||
|
{
|
||||||
|
users.Add(userId, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void RemoveUser(int userId)
|
||||||
|
{
|
||||||
|
users.Remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int HasUser(int userId)
|
||||||
|
{
|
||||||
|
return users.ContainsKey(userId) ? userId : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Player? GetPlayer(int userId)
|
||||||
|
{
|
||||||
|
users.TryGetValue(userId, out Player? player);
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<Player> GetPlayers()
|
||||||
|
{
|
||||||
|
return users.Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<int> GetConnectUsers()
|
||||||
|
{
|
||||||
|
return users.Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<int, Player> GetUsers()
|
||||||
|
{
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UserCount => users.Count;
|
||||||
|
}
|
||||||
@@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
public class Vector3
|
public class Vector3
|
||||||
{
|
{
|
||||||
public int X
|
public float X
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Y
|
public float Y
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Z
|
public float Z
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
@@ -33,4 +33,9 @@ public class Vector3
|
|||||||
Y = y; // 수직 사실상 안쓰겠다.
|
Y = y; // 수직 사실상 안쓰겠다.
|
||||||
Z = z;
|
Z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector3 Zero()
|
||||||
|
{
|
||||||
|
return new Vector3(0, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using MMOserver.Game.Engine;
|
||||||
using MMOserver.Packet;
|
using MMOserver.Packet;
|
||||||
|
|
||||||
namespace MMOserver.Game;
|
namespace MMOserver.Game;
|
||||||
@@ -82,24 +83,11 @@ public class Player
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 위치/방향 (클라이언트 패킷과 동일하게 float)
|
public Vector3 Position
|
||||||
public float PosX
|
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
} = new Vector3(0, 0, 0);
|
||||||
|
|
||||||
public float PosY
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float PosZ
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float RotY
|
public float RotY
|
||||||
{
|
{
|
||||||
@@ -121,6 +109,14 @@ public class Player
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 현재 위치한 맵의 존 ID
|
||||||
|
public int CurrentZoneId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = 0;
|
||||||
|
|
||||||
|
// 패킷용 전환
|
||||||
public PlayerInfo ToPlayerInfo()
|
public PlayerInfo ToPlayerInfo()
|
||||||
{
|
{
|
||||||
return new PlayerInfo
|
return new PlayerInfo
|
||||||
@@ -132,7 +128,7 @@ public class Player
|
|||||||
MaxHp = this.MaxHp,
|
MaxHp = this.MaxHp,
|
||||||
Mp = this.Mp,
|
Mp = this.Mp,
|
||||||
MaxMp = this.MaxMp,
|
MaxMp = this.MaxMp,
|
||||||
Position = new Position { X = this.PosX, Y = this.PosY, Z = this.PosZ },
|
Position = new Position { X = this.Position.X, Y = this.Position.Y, Z = this.Position.Z },
|
||||||
RotY = this.RotY,
|
RotY = this.RotY,
|
||||||
Experience = this.Experience,
|
Experience = this.Experience,
|
||||||
NextExp = this.NextExp,
|
NextExp = this.NextExp,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ using LiteNetLib.Utils;
|
|||||||
using MMOserver.Api;
|
using MMOserver.Api;
|
||||||
using MMOserver.Game.Channel;
|
using MMOserver.Game.Channel;
|
||||||
using MMOserver.Game.Channel.Maps;
|
using MMOserver.Game.Channel.Maps;
|
||||||
|
using MMOserver.Game.Channel.Maps.ZoneData;
|
||||||
|
using MMOserver.Game.Engine;
|
||||||
using MMOserver.Game.Party;
|
using MMOserver.Game.Party;
|
||||||
using MMOserver.Packet;
|
using MMOserver.Packet;
|
||||||
using MMOserver.Utils;
|
using MMOserver.Utils;
|
||||||
@@ -93,8 +95,10 @@ public partial class GameServer : ServerBase
|
|||||||
HashKey = hashKey,
|
HashKey = hashKey,
|
||||||
PlayerId = hashKey,
|
PlayerId = hashKey,
|
||||||
Nickname = hashKey.ToString(),
|
Nickname = hashKey.ToString(),
|
||||||
CurrentMapId = 1
|
CurrentMapId = 1,
|
||||||
|
CurrentZoneId = 0,
|
||||||
};
|
};
|
||||||
|
ChannelManager.Instance.GetChannel(1).GetMap(0)?.GetZone(0)?.AddUser(hashKey, newPlayer);
|
||||||
|
|
||||||
cm.AddUser(1, hashKey, newPlayer, peer);
|
cm.AddUser(1, hashKey, newPlayer, peer);
|
||||||
}
|
}
|
||||||
@@ -161,7 +165,7 @@ public partial class GameServer : ServerBase
|
|||||||
Log.Information("[Server] 토큰 검증 성공 Username={Username} PeerId={Id}", username, peer.Id);
|
Log.Information("[Server] 토큰 검증 성공 Username={Username} PeerId={Id}", username, peer.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// await 이후 — 공유 자원 접근 보호
|
// 공유 자원 접근 보호
|
||||||
lock (sessionLock)
|
lock (sessionLock)
|
||||||
{
|
{
|
||||||
peer.Tag = new Session(hashKey, peer);
|
peer.Tag = new Session(hashKey, peer);
|
||||||
@@ -224,7 +228,7 @@ public partial class GameServer : ServerBase
|
|||||||
}
|
}
|
||||||
_ = RestApi.Instance.SaveGameDataAsync(
|
_ = RestApi.Instance.SaveGameDataAsync(
|
||||||
session.Username,
|
session.Username,
|
||||||
player.PosX, player.PosY, player.PosZ, player.RotY,
|
player.Position.X, player.Position.Y, player.Position.Z, player.RotY,
|
||||||
playTimeDelta > 0 ? playTimeDelta : null
|
playTimeDelta > 0 ? playTimeDelta : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -320,7 +324,7 @@ public partial class GameServer : ServerBase
|
|||||||
Channel.Channel channel = cm.GetChannel(channelId);
|
Channel.Channel channel = cm.GetChannel(channelId);
|
||||||
Player? myPlayer = channel.GetPlayer(hashKey);
|
Player? myPlayer = channel.GetPlayer(hashKey);
|
||||||
|
|
||||||
// 1. 새 유저에게: 자신을 제외한 기존 채널 유저 목록 + 파티 목록 전송
|
// 새 유저에게 자신을 제외한 기존 채널 유저 목록 + 파티 목록 전송
|
||||||
IntoChannelPacket response = new() { ChannelId = channelId };
|
IntoChannelPacket response = new() { ChannelId = channelId };
|
||||||
foreach (int userId in channel.GetConnectUsers())
|
foreach (int userId in channel.GetConnectUsers())
|
||||||
{
|
{
|
||||||
@@ -350,7 +354,7 @@ public partial class GameServer : ServerBase
|
|||||||
byte[] toNewUser = PacketSerializer.Serialize((ushort)PacketCode.INTO_CHANNEL, response);
|
byte[] toNewUser = PacketSerializer.Serialize((ushort)PacketCode.INTO_CHANNEL, response);
|
||||||
SendTo(peer, toNewUser);
|
SendTo(peer, toNewUser);
|
||||||
|
|
||||||
// 2. 기존 유저들에게: 새 유저 입장 알림
|
// 기존 유저들에게 새 유저 입장 알림
|
||||||
if (myPlayer != null)
|
if (myPlayer != null)
|
||||||
{
|
{
|
||||||
UpdateChannelUserPacket notify = new()
|
UpdateChannelUserPacket notify = new()
|
||||||
@@ -423,7 +427,8 @@ public partial class GameServer : ServerBase
|
|||||||
|
|
||||||
foreach (int userId in map.GetUsers().Keys)
|
foreach (int userId in map.GetUsers().Keys)
|
||||||
{
|
{
|
||||||
if (!sessions.TryGetValue(userId, out NetPeer? targetPeer))
|
NetPeer? targetPeer = ChannelManager.Instance.GetChannel(channelId).GetPeer(userId);
|
||||||
|
if (targetPeer == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -437,6 +442,46 @@ public partial class GameServer : ServerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 특정 맵의 근처 유저들에게 전송 (exclude 지정 시 해당 피어 제외)
|
||||||
|
private void BroadcastToNear(int channelId, int mapId, int zoneId, Vector3 position, byte[] data, NetPeer? exclude = null, DeliveryMethod method = DeliveryMethod.ReliableOrdered)
|
||||||
|
{
|
||||||
|
Channel.Channel channel = ChannelManager.Instance.GetChannel(channelId);
|
||||||
|
AMap? map = channel.GetMap(mapId);
|
||||||
|
if (map == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 인접한 존을 가져온다.
|
||||||
|
(int zoneId1, int zoneId2, int zoneId3, int zoneId4) = map.GetNearZone(mapId, position);
|
||||||
|
// new / []로 동적할당 비용 감소를 위해 Span 스택에 할당되는 방식사용
|
||||||
|
Span<int> zoneIds = [zoneId1, zoneId2, zoneId3, zoneId4];
|
||||||
|
foreach (int nearZoneId in zoneIds)
|
||||||
|
{
|
||||||
|
Zone? nearZone = map.GetZone(nearZoneId);
|
||||||
|
if (nearZone == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int userId in nearZone.GetUsers().Keys)
|
||||||
|
{
|
||||||
|
NetPeer? targetPeer = channel.GetPeer(userId);
|
||||||
|
if (targetPeer == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclude != null && targetPeer.Id == exclude.Id)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendTo(targetPeer, data, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 채널 퇴장/연결 해제 시 파티 자동 탈퇴 처리
|
// 채널 퇴장/연결 해제 시 파티 자동 탈퇴 처리
|
||||||
private void HandlePartyLeaveOnExit(int channelId, int hashKey)
|
private void HandlePartyLeaveOnExit(int channelId, int hashKey)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,9 +92,9 @@ public partial class GameServer : ServerBase
|
|||||||
newPlayer.AttackPower = (float)profile.AttackPower;
|
newPlayer.AttackPower = (float)profile.AttackPower;
|
||||||
newPlayer.AttackRange = (float)profile.AttackRange;
|
newPlayer.AttackRange = (float)profile.AttackRange;
|
||||||
newPlayer.SprintMultiplier = (float)profile.SprintMultiplier;
|
newPlayer.SprintMultiplier = (float)profile.SprintMultiplier;
|
||||||
newPlayer.PosX = (float)profile.LastPosX;
|
newPlayer.Position.X = (float)profile.LastPosX;
|
||||||
newPlayer.PosY = (float)profile.LastPosY;
|
newPlayer.Position.Y = (float)profile.LastPosY;
|
||||||
newPlayer.PosZ = (float)profile.LastPosZ;
|
newPlayer.Position.Z = (float)profile.LastPosZ;
|
||||||
newPlayer.RotY = (float)profile.LastRotY;
|
newPlayer.RotY = (float)profile.LastRotY;
|
||||||
Log.Information("[GameServer] 프로필 로드 완료 Username={Username} Level={Level} MaxHp={MaxHp}",
|
Log.Information("[GameServer] 프로필 로드 완료 Username={Username} Level={Level} MaxHp={MaxHp}",
|
||||||
username, profile.Level, profile.MaxHp);
|
username, profile.Level, profile.MaxHp);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using LiteNetLib;
|
using LiteNetLib;
|
||||||
using MMOserver.Game.Channel;
|
using MMOserver.Game.Channel;
|
||||||
|
using MMOserver.Game.Channel.Maps;
|
||||||
using MMOserver.Packet;
|
using MMOserver.Packet;
|
||||||
using ProtoBuf;
|
using ProtoBuf;
|
||||||
|
using Serilog;
|
||||||
using ServerLib.Packet;
|
using ServerLib.Packet;
|
||||||
using ServerLib.Service;
|
using ServerLib.Service;
|
||||||
|
|
||||||
@@ -30,13 +32,28 @@ public partial class GameServer : ServerBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.PosX = packet.Position.X;
|
AMap? map = cm.GetChannel(channelId).GetMap(player.CurrentMapId);
|
||||||
player.PosY = packet.Position.Y;
|
if (map == null)
|
||||||
player.PosZ = packet.Position.Z;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Position.X = packet.Position.X;
|
||||||
|
player.Position.Y = packet.Position.Y;
|
||||||
|
player.Position.Z = packet.Position.Z;
|
||||||
player.RotY = packet.RotY;
|
player.RotY = packet.RotY;
|
||||||
|
|
||||||
// 같은 맵 유저들에게 위치/방향 브로드캐스트 (나 제외)
|
// 같은 맵 유저들에게 위치/방향 브로드캐스트 (나 제외)
|
||||||
byte[] data = PacketSerializer.Serialize((ushort)PacketCode.TRANSFORM_PLAYER, packet);
|
byte[] data = PacketSerializer.Serialize((ushort)PacketCode.TRANSFORM_PLAYER, packet);
|
||||||
BroadcastToMap(channelId, player.CurrentMapId, data, peer, DeliveryMethod.Unreliable);
|
int mapId = player.CurrentMapId;
|
||||||
|
int zoneId = player.CurrentZoneId;
|
||||||
|
|
||||||
|
// 유저가 이동가는한 공간을 탈출하면 일단 리턴
|
||||||
|
if (!map.UpdatePlayerZone(hashKey, player))
|
||||||
|
{
|
||||||
|
Log.Warning("플레이어 이동이 불가는한 공간에 이동했습니다.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BroadcastToNear(channelId, mapId, zoneId, player.Position, data, peer, DeliveryMethod.Unreliable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
<None Update="config.json">
|
<None Update="config.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="maps.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using MMOserver.Config;
|
using MMOserver.Config;
|
||||||
|
using MMOserver.Game.Channel.Maps;
|
||||||
using MMOserver.Game.Service;
|
using MMOserver.Game.Service;
|
||||||
using MMOserver.RDB;
|
using MMOserver.RDB;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@@ -23,6 +24,7 @@ class Program
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
AppConfig.Initialize(config);
|
AppConfig.Initialize(config);
|
||||||
|
MapLoader.Initialize();
|
||||||
|
|
||||||
// DB 연결
|
// DB 연결
|
||||||
// DbConnectionFactory dbFactory = new DbConnectionFactory(config);
|
// DbConnectionFactory dbFactory = new DbConnectionFactory(config);
|
||||||
|
|||||||
51
MMOTestServer/MMOserver/maps.json
Normal file
51
MMOTestServer/MMOserver/maps.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"maps": [
|
||||||
|
{
|
||||||
|
"mapId": 1,
|
||||||
|
"mapType": "ROBBY",
|
||||||
|
"rows": 2,
|
||||||
|
"cols": 2,
|
||||||
|
"zoneSize": 50,
|
||||||
|
"zones": [
|
||||||
|
{ "zoneId": 0, "row": 0, "col": 0, "centerX": 25, "centerZ": 25 },
|
||||||
|
{ "zoneId": 1, "row": 0, "col": 1, "centerX": 75, "centerZ": 25 },
|
||||||
|
{ "zoneId": 2, "row": 1, "col": 0, "centerX": 25, "centerZ": 75 },
|
||||||
|
{ "zoneId": 3, "row": 1, "col": 1, "centerX": 75, "centerZ": 75 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mapId": 2,
|
||||||
|
"mapType": "DUNGEON",
|
||||||
|
"rows": 5,
|
||||||
|
"cols": 5,
|
||||||
|
"zoneSize": 100,
|
||||||
|
"zones": [
|
||||||
|
{ "zoneId": 0, "row": 0, "col": 0, "centerX": 50, "centerZ": 50 },
|
||||||
|
{ "zoneId": 1, "row": 0, "col": 1, "centerX": 150, "centerZ": 50 },
|
||||||
|
{ "zoneId": 2, "row": 0, "col": 2, "centerX": 250, "centerZ": 50 },
|
||||||
|
{ "zoneId": 3, "row": 0, "col": 3, "centerX": 350, "centerZ": 50 },
|
||||||
|
{ "zoneId": 4, "row": 0, "col": 4, "centerX": 450, "centerZ": 50 },
|
||||||
|
{ "zoneId": 5, "row": 1, "col": 0, "centerX": 50, "centerZ": 150 },
|
||||||
|
{ "zoneId": 6, "row": 1, "col": 1, "centerX": 150, "centerZ": 150 },
|
||||||
|
{ "zoneId": 7, "row": 1, "col": 2, "centerX": 250, "centerZ": 150 },
|
||||||
|
{ "zoneId": 8, "row": 1, "col": 3, "centerX": 350, "centerZ": 150 },
|
||||||
|
{ "zoneId": 9, "row": 1, "col": 4, "centerX": 450, "centerZ": 150 },
|
||||||
|
{ "zoneId": 10, "row": 2, "col": 0, "centerX": 50, "centerZ": 250 },
|
||||||
|
{ "zoneId": 11, "row": 2, "col": 1, "centerX": 150, "centerZ": 250 },
|
||||||
|
{ "zoneId": 12, "row": 2, "col": 2, "centerX": 250, "centerZ": 250 },
|
||||||
|
{ "zoneId": 13, "row": 2, "col": 3, "centerX": 350, "centerZ": 250 },
|
||||||
|
{ "zoneId": 14, "row": 2, "col": 4, "centerX": 450, "centerZ": 250 },
|
||||||
|
{ "zoneId": 15, "row": 3, "col": 0, "centerX": 50, "centerZ": 350 },
|
||||||
|
{ "zoneId": 16, "row": 3, "col": 1, "centerX": 150, "centerZ": 350 },
|
||||||
|
{ "zoneId": 17, "row": 3, "col": 2, "centerX": 250, "centerZ": 350 },
|
||||||
|
{ "zoneId": 18, "row": 3, "col": 3, "centerX": 350, "centerZ": 350 },
|
||||||
|
{ "zoneId": 19, "row": 3, "col": 4, "centerX": 450, "centerZ": 350 },
|
||||||
|
{ "zoneId": 20, "row": 4, "col": 0, "centerX": 50, "centerZ": 450 },
|
||||||
|
{ "zoneId": 21, "row": 4, "col": 1, "centerX": 150, "centerZ": 450 },
|
||||||
|
{ "zoneId": 22, "row": 4, "col": 2, "centerX": 250, "centerZ": 450 },
|
||||||
|
{ "zoneId": 23, "row": 4, "col": 3, "centerX": 350, "centerZ": 450 },
|
||||||
|
{ "zoneId": 24, "row": 4, "col": 4, "centerX": 450, "centerZ": 450 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -10,21 +10,17 @@ using ServerLib.Packet;
|
|||||||
|
|
||||||
namespace ServerLib.Service;
|
namespace ServerLib.Service;
|
||||||
|
|
||||||
/// <summary>
|
/*
|
||||||
/// 네트워킹 추상 베이스 (protobuf 없음)
|
* 순서
|
||||||
///
|
* OnPeerConnected → 대기 목록 등록 (아직 Auth로 로그인 안된상테)
|
||||||
/// 흐름:
|
* OnNetworkReceive → Auth 패킷(type=1)이면 HashKey(4byte int) 읽어 인증 / 이미 같은 HashKey 세션 있으면 이전 피어 끊고 재연결 (WiFi→LTE) / 그 외 패킷은 HandlePacket() 으로 전달
|
||||||
/// OnPeerConnected → 대기 목록 등록
|
* OnPeerDisconnected → 세션/대기 목록에서 제거
|
||||||
/// OnNetworkReceive → Auth 패킷(type=1)이면 HashKey(4byte int) 읽어 인증
|
*
|
||||||
/// → 이미 같은 HashKey 세션 있으면 이전 피어 끊고 재연결 (WiFi→LTE)
|
* 서브 클래스
|
||||||
/// → 그 외 패킷은 HandlePacket() 으로 전달
|
* OnSessionConnected - 인증 완료 시
|
||||||
/// OnPeerDisconnected → 세션/대기 목록에서 제거
|
* OnSessionDisconnected - 세션 정상 해제 시 (재연결 교체는 호출 x)
|
||||||
///
|
* HandlePacket - 패킷 파싱
|
||||||
/// 서브클래스 구현:
|
*/
|
||||||
/// OnSessionConnected - 인증 완료 시
|
|
||||||
/// OnSessionDisconnected - 세션 정상 해제 시 (재연결 교체는 호출 안 함)
|
|
||||||
/// HandlePacket - 인증된 피어의 게임 패킷 처리
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ServerBase : INetEventListener
|
public abstract class ServerBase : INetEventListener
|
||||||
{
|
{
|
||||||
protected NetManager netManager = null!;
|
protected NetManager netManager = null!;
|
||||||
@@ -62,6 +58,7 @@ public abstract class ServerBase : INetEventListener
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 최적화 되면 안되므로 volatile로 선언
|
||||||
private volatile bool isListening = false;
|
private volatile bool isListening = false;
|
||||||
|
|
||||||
public ServerBase(int port, string connectionString)
|
public ServerBase(int port, string connectionString)
|
||||||
@@ -101,7 +98,7 @@ public abstract class ServerBase : INetEventListener
|
|||||||
isListening = false;
|
isListening = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 클라이언트 연결 요청 수신 → Accept / Reject 결정
|
// 클라이언트 연결 요청 수신 Accept / Reject 결정
|
||||||
public void OnConnectionRequest(ConnectionRequest request)
|
public void OnConnectionRequest(ConnectionRequest request)
|
||||||
{
|
{
|
||||||
// 벤 기능 추가? 한국 ip만?
|
// 벤 기능 추가? 한국 ip만?
|
||||||
|
|||||||
Reference in New Issue
Block a user