TIL-sparta

스파르타) The Last Rollback (D-26, Node.js 게임 서버 최종 프로젝트)

Megadr0ne 2024. 8. 1. 22:47


 > 과제 진행 간 완료한 사항 및 문제점과 해결 과정을 정리해보았다.

 

학습 키워드: Node.js, C#, Unity

 

1. 완료한 과제 진행 사항

1) 던전 위치 동기화 수정 및 낮/밤 라운드 기반 구조 작성:

 

49 update 낮 밤 라운드 기능 by donkim1212 · Pull Request #53 · eliotjang/the-last-rollback-server

관련 Issue #49 작업 내역 #49 하단의 작업 이력 (24-08-01) 참고 참고 사항 던전 캐릭터 안보이던 문제와 위치 동기화 문제 해결로 인해 중도에 merge 하게 됨

github.com

 - 상세 작업 내용은 위 PR에 연결된 Issue 참고

 

2. 과제 진행 간 문제점

1) 같은 던전 세션의 다른 캐릭터가 표시되지 않던 문제 (해결):

Figure 1. 우측 클라이언트에서 좌측 클라이언트 유저가 보이지 않음

 

 던전 입장 시 먼저 입장한 캐릭터의 클라이언트에서 나중에 입장한 캐릭터가 보이지 않는 문제가 있었다. 팀원들과 같이 원인을 찾아봐도 답이 나오지 않아서 보류하고 Issue #49 의 작업을 진행, enter-dungeon.js 코드에서 비효율적인 구조를 찾게되어서 수정했다.

 

 그런데 수정 이후부터 양 쪽 클라이언트에서 정상적으로 모든 유저가 표시되었다. 정확한 원인은 알 수 없으나 캐릭터 정보를 불러와 패킷에 담는 과정에서 데이터가 꼬인 듯 싶다.

 

 + 24-08-28 추가: 커밋을 찾아서 다시 조사해보니 기존 코드의 문제점을 바로 알 수 있었다.

 

Fix: 던전 동시 스폰 안되던 문제 · eliotjang/the-last-rollback-server@2f4f17a

donkim1212 committed Aug 1, 2024

github.com

// match_queue.js (기존 코드)
    ...
    for (const accountId of accountIds) {
      // 각 유저의 town session 내 다른 유저에게 Despawn 패킷 전송
      const townSession = getTownSessionByUserId(accountId);
      townSession.removeUser(accountId);
  
      // users의 유저를 battle session에 추가
      // redis
      const playerInfo = await townRedis.getPlayerInfo(accountId);
      await dungeonRedis.createGuest(playerInfo, hostId, accountId);
      // 인메모리
      const user = getUserById(accountId);
      dungeonSession.addUser(user);
      // town session에서 유저 제거
      await townRedis.removePlayer(accountId, false);

      // 각 클라이언트에게 S_EnterDungeon 패킷 전송
      enterDungeonSession(accountId, dungeonCode);
    }
  }
};

 

 기존 코드에서는 for 루프를 돌면서 던전에 유저를 추가하고 패킷을 전송했는데, 이렇게 되면 보내는 순서에 따라 먼저 추가된 유저에게서 나머지 유저의 위치 정보를 받을 수 없는 현상이 나타나게 된다. 이후 고쳤던 부분이 루프 맨 아래의 enterDungeonSession 인데, 원래 구조에서 동일한 데이터를 가져오는 I/O 작업이 반복적으로 이루어지는 것을 발견해서 루프의 바깥으로 빼놓는 과정을 거쳤다. 이로 인해 모든 유저가 추가된 이후 패킷을 보내는 방식으로 자연스럽게 바뀌게되어 문제가 해결된 것이다.

 

// match_queue.js (바뀐 코드)
	...
    for (const accountId of accountIds) {
      // 각 유저의 town session 내 다른 유저에게 Despawn 패킷 전송
      const townSession = getTownSessionByUserId(accountId);
      townSession.removeUser(accountId);

      // users의 유저를 battle session에 추가
      // redis
      // const playerInfo = await townRedis.getPlayerInfo(accountId);
      // await dungeonRedis.createGuest(playerInfo, hostId, accountId);
      // 인메모리
      const user = getUserById(accountId);
      dungeonSession.addUser(user);
      // town session에서 유저 제거
      await townRedis.removePlayer(accountId, false);

      // 각 클라이언트에게 S_EnterDungeon 패킷 전송
      // enterDungeonSession(accountId, dungeonCode);
    }
    enterDungeonSession(dungeonSession, dungeonCode);
  }
};

 

 enterDungeonSession 코드는 기존에 한 명의 유저에게만 패킷을 보내고 있었는데, 이 부분에 for loop를 추가하여 모든 유저가 참가한 이후의 정보를 보내줄 수 있도록 변경했다. 자세한 내용은 위 커밋의 enter-dungeon.js 파일을 참고하자.

 

--

 

 이후에 위치 정보 패킷을 주고 받는 과정에서 패킷은 정상적으로 들어오는데 캐릭터는 움직이지 않는 이슈가 있었으나, 단순히 Update() 의 조건문에 걸려서 코드가 실행되지 않고 있는 문제였다.

public void Spawn (PlayerInfo playerInfo, bool mine) {
    var tr = playerInfo.Transform;

    var spawnPos = spawnArea.position;
    spawnPos.x += tr.PosX;
    spawnPos.z += tr.PosZ;

    if (mine) {
        myPlayer = CreatePlayer(playerInfo, spawnPos);
        myPlayer.SetIsMine(true);

        foreach (Monster monster in monsterObjs)
        {
            monster.destinations.Add(myPlayer.transform);
        }
    } else {
        CreatePlayer(playerInfo, spawnPos).SetIsMine(false); // SetIsMine 추가
    }
}

 

 SetIsMine 함수가 캐릭터의 init 상태를 세팅해주고 있어서 자신의 캐릭터가 아니어도 false 값으로 콜해줘야만 동기화가 진행된다.

 

Figure 2. 던전 위치 동기화 수정 완료

 

 

--


REFERENCES:

 

 

GitHub - eliotjang/the-last-rollback-server: 액션 MORPG

액션 MORPG. Contribute to eliotjang/the-last-rollback-server development by creating an account on GitHub.

github.com

 > 프로젝트 repo

 

728x90