전체 글 42

뱀서라이크 던전 돌파 게임 만들기 (22)

게임 서버, C++로 처음부터 만들어보기 — 숫자만 보고 있으면 안 보이는 것22편을 이렇게 끝냈다.밸런스는 화면을 보면서 만질 일이라 클라이언트가 생긴 뒤로 미뤘다.핑계처럼 들렸지만 진심이었다. 22편 검증에서 던전 클리어가 불가능하다는 걸 알아냈는데, 알아낸 게 거기까지였다. 서버 로그가 주는 건 이게 전부다.[dungeon] battle=3 result=wipe room=0 wave=1 kills=0 players=[alice, bob]kills=0. 180초 동안 한 마리도 못 잡고 전멸했다. 왜? 로그는 말해주지 않는다. 그래서 가설을 세 개 적어뒀다 — 마릿수에 인원수를 곱한 것, 몬스터가 뭉치는 것, 사거리와 속도차. 전부 추측이었다.이번 편에서는 유니티 클라이언트를 붙여서 그 추측을 눈으로..

뱀서라이크 던전 돌파 게임 만들기 (번외)

게임 서버, C++로 처음부터 만들어보기 — 15스텝 전에 예고한 한계6편에서 로비 서버를 main.cpp 한 파일로 쓰면서 이렇게 적어뒀다.헤더/소스 분리가 필요해지는 이유를 한 파일 안에서 미리 겪은 셈이다.그때 300줄쯤이었다. 21편까지 오면서 1,640줄이 됐고, 이번에 나눴다.예고한 것과 겪은 것은 달랐다나눠야 하는 이유를 그때는 "길어서"로 생각했다. 실제로 아팠던 건 다른 거였다.20편에서 로비에 배틀 서버 연동 코드를 넣다가 컴파일이 안 됐다.error: invalid use of incomplete type 'class Server'Session이 Server보다 먼저 정의돼 있다. 그래서 server_.xxx()를 쓰는 Session 메서드는 클래스 안에 본문을 둘 수 없다. 파일은 ..

뱀서라이크 던전 돌파 게임 만들기 (21)

게임 서버, C++로 처음부터 만들어보기 — 전역 멤버가 곧 가정이었다지난 편에서 두 서버를 연결하고 끝에서 끝까지 돌려봤다. 성공한 로그 한가운데 이런 줄이 있었다.Handshake from 172.19.0.1:54346 -> conn 3 (entity 27, user alice)엔티티 ID가 27이다.첫 플레이어인데 27번. 이전 테스트의 몬스터들이 번호를 쓰고 갔다는 뜻이다.던전이 끝나도 월드가 남는다배틀 서버에 World가 하나뿐이다. 던전이 끝나도 리셋되지 않는다.그래서 두 번째 파티는 첫 파티의 전멸 상태를 물려받는다. Failed면 update가 맨 앞에서 돌아서니 아예 못 논다.20편에서 "여러 대로 늘려도 구조가 같다"고 적어놨는데, 한 대가 한 판만 도는 상태였다. 연동 작업의 절반이 ..

뱀서라이크 던전 돌파 게임 만들기 (20)

게임 서버, C++로 처음부터 만들어보기 — 한 방향 연결, 두 방향 대화18편에서 던전 결과를 로비로 보냈다. 그리고 이렇게 적어뒀다.그런데 저 결과에 치명적인 게 빠져 있다.{"result":"wipe","room":0,"wave":0,"kills":3,"players":1}누구의 던전인지가 없다.배틀 서버는 엔티티 ID만 안다. 계정이 뭔지, 어느 파티인지 모른다. 로비가 "이 파티가 간다"를 알려주는 경로가 없었기 때문이다.이번 편에서 그 경로를 만든다.누가 누구에게 거나지금까지는 배틀이 로비에 결과를 보낼 때만 붙었다 끊었다. 이제 로비가 먼저 말을 걸어야 한다.방식 배틀에 TCP 리스너 추가포트가 하나 더 늘고, 로비가 배틀 주소를 알아야 하고, NAT 뒤면 못 감로비가 배틀의 UDP로 관리 ..

뱀서라이크 던전 돌파 게임 만들기 (19)

게임 서버, C++로 처음부터 만들어보기 — 재보니 네 방 뒤였다서버 로드맵의 마지막 편이다.14편부터 18편까지, "나중에 재고 고친다"고 미뤄둔 게 넷 쌓였다.미뤄둔 것언제몬스터를 몇 마리까지 넣을 수 있나17편인원이 늘면 난이도가 제곱으로 쉬워짐18편RTT가 네트워크 지연이 아니라 ack 지연15편인플라이트 상한에 밀려 유실 판정도 못 받는 패킷16편전부 "그럴 것 같다"로만 적어둔 것들이다.재보니 둘은 맞았고, 하나는 생각보다 훨씬 가까웠고, 하나는 이름만 고치면 되는 문제였다.먼저 자를 만들어야 한다지금까지 테스트는 PowerShell 스크립트로 했다. 30Hz로 입력을 보내도록 짰는데, 세어보니:클라가 보낸 Input 패킷: 122개 (20.3/초)21개만 보냈다. 목표의 70%다.측정 도구가..

뱀서라이크 던전 돌파 게임 만들기 (18)

게임 서버, C++로 처음부터 만들어보기 — 실패해도 되게 만들기지금까지 만든 던전은 끝나지 않았다. 방이 무한히 이어지고, 몬스터가 닿아도 아무 일이 없었다.이번 편에서 끝나는 길이 생긴다. 맞으면 쓰러지고, 동료가 일으켜 세우고, 전원이 쓰러지면 실패.그리고 끝났다는 걸 로비에 알려야 한다. 13편에서 서버를 둘로 나누며 "나중에 붙인다"고 했던 그 나중이다.쓰러진 사람을 쫓으면 안 된다몬스터가 가장 가까운 플레이어를 쫓는다. 그대로 두면 쓰러진 사람 위에 앉아 있는다.구하러 가면 그 떼를 전부 상대해야 하고, 구조가 사실상 불가능해진다.for (const Player& p : players_) { if (!p.is_alive()) continue; // 쓰러진 사람은 쫓지 않는다 ....

뱀서라이크 던전 돌파 게임 만들기 (17)

게임 서버, C++로 처음부터 만들어보기 — 조건이 네 개면 이미 틀렸다지금까지 만든 서버에는 게임의 흐름이 없었다. 몬스터가 있고, 다가오고, 죽는다. 그게 전부였다.이번 편에서 흐름이 생긴다. 몇 마리를 언제 내보낼지, 다 죽이면 뭐가 되는지, 언제 다음 방으로 넘어갈지.같은 열거형, 다른 성격로비 서버에도 RoomPhase라는 게 있었다. Waiting, Fighting, Cleared.여기서도 비슷한 걸 만든다. 그런데 결정적으로 다른 게 있다.로비: 누가 공격 메시지를 보낸다 → 체력이 깎인다 → 0이 되면 Cleared배틀: 아무도 아무것도 안 보낸다 → 그래도 웨이브가 끝나고 방이 넘어간다로비에서는 메시지가 상태를 바꿨고, 여기서는 틱이 바꾼다.10편에서 경매 타이머를 만들며 "아무도 부..

뱀서라이크 던전 돌파 게임 만들기 (16)

게임 서버, C++로 처음부터 만들어보기 — 언제 나눌지는 코드가 알려준다지난 편에서 이렇게 적었다.Player를 Connection 안에 둔 이유: 지금 플레이어와 연결은 1:1이다. 분리할 이유가 없다.이번 편에서 몬스터를 만든다. 그 말이 거짓이 되는 순간이다.몬스터는 연결이 없다. ConnectionMap에 넣을 수 없고, 넣는다 해도 endpoint가 뭐가 되나. 포트 번호가 없는 클라이언트는 없다.미리 만들지 않아서 좋았던 점Step 15에서 World를 미리 만들 수도 있었다. 어차피 몬스터를 넣을 걸 알고 있었으니까.안 만들었다. 그랬으면 플레이어만 든 빈 껍데기가 하나 생겼을 것이다. 그리고 더 중요한 건, 그 모양이 맞는지 판단할 근거가 없었다는 것이다.지금은 안다. 몬스터를 넣어보니 ..

뱀서라이크 던전 돌파 게임 만들기 (15)

게임 서버, C++로 처음부터 만들어보기 — 서버가 먼저 말을 건다Step 1부터 14까지, 이 서버의 모든 코드는 같은 모양이었다.패킷이 온다 → 처리한다 → 응답한다전부 반응이다. 누가 뭔가를 보내지 않으면 서버는 아무것도 하지 않는다.이번 편에서 그게 바뀐다. 아무도 아무것도 안 보내도 초당 30번 세계가 흘러가야 한다.1초에 30번이 아니라, 33.333ms마다틱 타이머 자체는 로비에서 이미 만들었다. 경매 마감을 검사하는 1초짜리였다.그걸 그대로 33ms로 바꾸면 안 된다.tick_timer_.expires_after(std::chrono::milliseconds(33));expires_after는 "지금부터 33ms 뒤"다. 틱 처리에 2ms가 걸렸다면 실제 간격은 35ms가 된다. 그게 매번..

뱀서라이크 던전 돌파 게임 만들기 (14)

게임 서버, C++로 처음부터 만들어보기 — 똑같이 만들면 TCP를 쓰면 되니까지난 편에서 UDP 서버의 연결 계층을 만들었다. 헤더에 ack와 ack_bits라는 필드를 넣어두고 값은 0으로 비워뒀다.이번엔 그걸 채운다. TCP가 공짜로 해주던 걸 손으로 만드는 작업이다.그런데 TCP와 똑같이 만들면 안 된다. 똑같이 만들 거면 TCP를 쓰면 되니까.안 보내도 되는 것들TCP는 모든 바이트를 책임진다. 유실되면 재전송하고, 순서가 틀리면 맞출 때까지 기다린다.게임에서는 그게 손해다.초당 30번 플레이어 위치를 보낸다고 하자. 0.5초 전 위치 패킷이 유실됐다. TCP는 그걸 기어코 다시 보내고, 도착할 때까지 그 뒤 패킷을 전부 막는다. 최신 위치가 이미 도착해서 버퍼에 쌓여 있는데도 꺼내 쓸 수가 없..