이동 동기화를 구현하는 방법은 두 가지가 있다.
이 강의에서는 1번 방법으로 구현해본다.
Protocol.proto에서 아래와 같이 수정했다.
기존에서 플레이어 좌표 뿐만 아니라 플레이어가 향하고 있는 방향정보와 상태정보를 같이 이동 패킷에 포함하도록 변경시켰다.
enum CreatureState {
IDLE = 0;
MOVING = 1;
SKILL = 2;
DEAD = 3;
}
enum MoveDir {
NONE = 0;
UP = 1;
DOWN = 2;
LEFT = 3;
RIGHT = 4;
}
message C_Move {
PositionInfo posInfo = 1;
}
message S_Move {
int32 playerId = 1;
PositionInfo posInfo = 2;
}
message PlayerInfo {
int32 playerId = 1;
string name = 2;
PositionInfo posInfo = 3;
}
message PositionInfo {
CreatureState state = 1;
MoveDir moveDir = 2;
int32 posX = 3;
int32 posY = 4;
}
Protocol.proto 수정 후 GenProto.bat 실행해서 갱신시켜주고 Server를 빌드하였다.
public override void OnConnected(EndPoint endPoint)
{
Console.WriteLine($"OnConnected : {endPoint}");
MyPlayer = PlayerManager.Instance.Add();
{
MyPlayer.Info.Name = $"Player_{MyPlayer.Info.PlayerId}";
MyPlayer.Info.PosInfo.State = CreatureState.Idle;
MyPlayer.Info.PosInfo.MoveDir = MoveDir.None;
MyPlayer.Info.PosInfo.PosX = 0;
MyPlayer.Info.PosInfo.PosY = 0;
MyPlayer.Session = this;
}
RoomManager.Instance.Find(1).EnterGame(MyPlayer);
}
Protocol.proto에서 message PlayerInfo 부분을 수정하였기 때문에 그에 맞춰 MyPlayer 객체를 만들고 초기화 시켜준다.
public static void C_MoveHandler(PacketSession session, IMessage packet)
{
C_Move movePacket = packet as C_Move;
ClientSession clientSession = session as ClientSession;
// 플레이어가 입장한 방에 있는 모든 유저들한테 브로드캐스팅. 이동 했다는 사실을 뿌리는 방식으로 작업
Console.WriteLine($"C_Move ({movePacket.PosInfo.PosX}, {movePacket.PosInfo.PosY}");
if (clientSession.MyPlayer == null)
return;
if (clientSession.MyPlayer.Room == null)
return;
// TODO 검증하기. 거짓된 정보가 올 수도 있기 때문
// 일단 서버에서 좌표 이동
PlayerInfo info = clientSession.MyPlayer.Info; // 현재 서버에 있는 플레이어의 정보
info.PosInfo = movePacket.PosInfo; // 그 정보에 얼마만큼 이동했는지 패킷 이동 정보를 덮어씌어서 서버 상의 플레이어 좌표를 갱신
// 그리고 그 갱신시킨 정보를 다른 플레이어한테도 알려줌
S_Move resMovePacket = new S_Move();
resMovePacket.PlayerId = clientSession.MyPlayer.Info.PlayerId; // 누가 움직이는가?
resMovePacket.PosInfo = movePacket.PosInfo;
clientSession.MyPlayer.Room.Broadcast(resMovePacket);
}
클라이언트 쪽에서 Move 패킷이 오면 서버에서 그 패킷을 처리해주게 되는데 패킷이 잘 도착했는지 검증 후 같은 룸에 존재하는 모든 다른 플레이어들에게 누가 이동했는지 전부 Move 패킷을 뿌려준다.
public void Broadcast(IMessage packet)
{
lock(_lock)
{
foreach(Player p in _players)
{
p.Session.Send(packet);
}
}
}
Broadcast 함수는 C_MoveHandler에서 호출시켜주고 있는데 여러 클라이언트가 C_Move 패킷을 동시에 쏘면 C_MoveHandler가 다양한 쓰레드에서 동시다발적으로 실행될 수 있다. 그래서 반드시 C_MoveHandler에서 호출되는 함수는 정의할 때 lock을 해줘야 한다.
하지만 여기서 주의할 점은 lock을 남발해버리면 멀티쓰레딩의 의미가 사라져버리기 때문에 주의해야 한다.
그래서 lock을 거는 대신 jobQueue에 넣어준 후 단일 쓰레드에서 싹 다 처리하도록 하는게 좋다.