패킷의 설계
// Struct.proto
syntax = "proto3";
package Protocol;
import "Enum.proto";
message Player
{
uint64 id = 1;
string name = 2;
PlayerType playerType = 3;
// 스탯 정보...
}
// Protocol.proto
syntax = "proto3";
package Protocol;
import "Enum.proto";
import "Struct.proto";
// 서버/클라 로그인 요청
message C_LOGIN
{
}
message S_LOGIN
{
bool success = 1;
repeated Player players = 2; // 아이디 발급 전
}
// 서버/클라 게임접속
message C_ENTER_GAME
{
uint64 playerIndex = 1;
}
message S_ENTER_GAME
{
bool success = 1;
}
// 채팅
message C_CHAT
{
string msg = 1;
}
message S_CHAT
{
uint64 playerId = 1;
string msg = 2;
}
대략 이렇게 패킷을 정의하고 패킷의 처리는
서버와의 연결 성공 후 클라이언트 로그인시도
class ServerSession : public PacketSession
{
public:
~ServerSession()
{
cout << "~ServerSession" << endl;
}
virtual void OnConnected() override
{
Protocol::C_LOGIN pkt;
auto sendBuffer = ServerPacketHandler::MakeSendBuffer(pkt);
Send(sendBuffer);
}
// ...
서버에서 로그인 처리
bool Handle_C_LOGIN(PacketSessionRef& session, Protocol::C_LOGIN& pkt)
{
GameSessionRef gameSession = static_pointer_cast<GameSession>(session);
// TODO : Validation 체크
Protocol::S_LOGIN loginPkt;
loginPkt.set_success(true);
// DB에서 플레이 정보를 긁어온다
// GameSession에 플레이 정보를 저장 (메모리)
// ID 발급 (DB 아이디가 아니고, 인게임 아이디)
static Atomic<uint64> idGenerator = 1;
{
auto player = loginPkt.add_players();
player->set_name(u8"DB에서긁어온이름1");
player->set_playertype(Protocol::PLAYER_TYPE_KNIGHT);
PlayerRef playerRef = MakeShared<Player>();
playerRef->playerId = idGenerator++;
playerRef->name = player->name();
playerRef->type = player->playertype();
playerRef->ownerSession = gameSession;
gameSession->_players.push_back(playerRef);
}
{
auto player = loginPkt.add_players();
player->set_name(u8"DB에서긁어온이름2");
player->set_playertype(Protocol::PLAYER_TYPE_MAGE);
PlayerRef playerRef = MakeShared<Player>();
playerRef->playerId = idGenerator++;
playerRef->name = player->name();
playerRef->type = player->playertype();
playerRef->ownerSession = gameSession;
gameSession->_players.push_back(playerRef);
}
auto sendBuffer = ClientPacketHandler::MakeSendBuffer(loginPkt);
session->Send(sendBuffer);
return true;
}
bool Handle_S_LOGIN(PacketSessionRef& session, Protocol::S_LOGIN& pkt)
{
if (pkt.success() == false)
return true;
if (pkt.players().size() == 0)
{
// 캐릭터 생성창
}
// 입장 UI 버튼 눌러서 게임 입장
Protocol::C_ENTER_GAME enterGamePkt;
enterGamePkt.set_playerindex(0); // 첫번째 캐릭터로 입장
auto sendBuffer = ServerPacketHandler::MakeSendBuffer(enterGamePkt);
session->Send(sendBuffer);
return true;
}
나머지는 쉽기에 생략…
대략적 현재코드의 문제점을 지적하자면
void Room::Broadcast(SendBufferRef sendBuffer)
{
WRITE_LOCK;
for (auto& p : _players)
{
p.second->ownerSession->Send(sendBuffer);
}
}
위 코드와 같이 Room에서 모든 유저에게 브로드캐스트해준다
여기까지는 문제가 없지만… 브로드 캐스트의 방법이 LOCK이라는 것에 문제가 있다.
이후 JobQueue에서 이 문제를 해결해본다