들어가며
이 글은 https://www.gabrielgambetta.com/client-server-game-architecture.html 글을 공부하면서 옮긴 것으로, 번역과 의역이 섞여있습니다.
어떤 종류든 게임을 개발하는 것 자체가 어려운 일이지만, 멀티플레이어 게임은 완전히 새로운 문제를 다루어야하는 독특한 어려움이 있습니다. 호흡이 빠른 멀티플레이어 게임의 클라이언트 - 서버 아키텍쳐를 구성하는 방법에 대해서 알아보겠습니다.
부정행위 문제 (The Problem of cheating)
모든 것은 부정행위에서 시작됩니다.
게임 개발자로서, 싱글 플레이 게임에서 플레이어가 부정행위를 행하는지 여부에는 관심을 두지 않습니다. 그러나 멀티 플레이 게임은 다릅니다. 경쟁 게임에서 부정을 저지르는 플레이어는 자신만을 위한 경험을 개선하는 것뿐만 아니라 다른 플레이어에게는 역으로 경험을 엉망으로 만듭니다.
부정행위를 방지하는 데에는 여러 방법이 있지만, 가장 간단한 방법은 “플레이어를 믿지 않는다”는 것입니다. 항상 최악의 상황을 상정하고, 플레이어들이 부정행위를 시도할 것으로 가정하세요.
권위있는 서버와 멍청한 클라이언트 (Authoritative servers and dumb clients)
“플레이어를 믿지 않는다”는 것은 간단한 해결책으로 이어질 수 있습니다. 게임 내 모든 일을 여러분의 통제 아래 중앙 서버에서 발생하도록 만들고, 클라이언트는 게임의 관전자로 만드는 것입니다. 다시 말해, 게임 클라이언트는 입력(Key presses, Commands)을 서버로 보내고, 서버가 게임을 실행하고 결과를 클라이언트에 다시 보내도록 합니다.
권위있는 서버를 사용하면 여러가지 해킹을 방지할 수 있습니다. 예를 들어, 플레이어의 체력을 클라이언트에 신뢰하지 않습니다. 해킹된 클라이언트는 해당 값에 대한 로컬 복사본을 수정하고 플레이어에게 10000%
체력이 있다고 알릴 수 있지만, 서버는 실제 플레이어의 체력이 10%
라는 것을 알고 있습니다. 따라서 플레이어가 공격당하면 해킹된 클라이언트가 생각하는 것과 관계없이 죽을 겁니다.
또한 플레이어의 위치를 신뢰하지 않습니다. 렇게 했다면, 해킹된 클라이언트가 서버에게 "나는 (10,10)
에 있어"라고 말하고 나중에 "나는 (20,10)
에 있어"라고 말할 것입니다. 벽을 통과하거나 다른 플레이어보다 빨리 이동할 수 있습니다. 대신, 서버는 플레이어가 (10,10)
에 있다는 것을 알고 있으며, 클라이언트는 서버에게 "오른쪽으로 한 칸 이동하고 싶어"라고 말하고, 서버는 새로운 플레이어 위치를 (11,10)
으로 업데이트한 다음 플레이어에게 "당신은 (11,10)
에 있어"라고 답합니다.
요약하면, 게임 상태는 오로지 서버에서 관리된다는 것입니다.
클라이언트는 자신의 행동을 서버로 보냅니다. 서버는 주기적으로 게임 상태를 업데이트하고, 새로운 상태를 클라이언트에게 보내며, 클라이언트는 이를 화면에 그대로 표시만 하는 겁니다.
네트워크 다루기
멍청한 클라이언트 구조는 턴제 게임(예를들면 전략이나 포커 등)에는 잘 작동합니다. 그리고 거의 즉각적인 통신이 가능한 LAN 환경에서도 잘 작동합니다. 하지만 빠른속도를 필요로 하면서 인터넷과 같은 네트워크를 통한 게임에 사용할 때 문제가 발생합니다.
물리학에 대해 이야기해 봅시다. 예를 들어, 샌프란시스코에 있다고 가정하고 뉴욕의 서버에 연결되어 있다고 해보겠습니다. 이 거리는 대략 4,000km
입니다. 이 세상의 어떤 것도 빛보다 빠르게 이동할 수 없으며, 인터넷의 로우 레벨에서는 빛의 펄스, 케이블 내의 전자 또는 전자기파로 구성된 바이트조차 빛보다 빠르게 이동하지 않습니다. 빛의 속도는 대략 300,000km/s
로, 4,000km
를 이동하는 데 13ms
가 걸립니다.
꽤 빨라보이지만, 실제로는 아주 낙관적인 설정입니다. 방금의 설정은 데이터가 최단 경로로 빛의 속도로 이동한다고 가정하며, 실제로는 그렇지 않을 가능성이 높습니다. 현실에서는 데이터가 라우터 간에 점프(네트워킹 용어로 호핑(Hopping)이라고 함)를 거치며 이동하며, 대부분의 경우 라우터 간 이동은 빛의 속도로 이루어지지 않으며, 라우터 자체가 패킷을 복사, 검사 및 다시 라우팅해야 하므로 약간의 지연이 발생합니다.
논의를 위해 클라이언트에서 서버까지 데이터가 50ms
가 걸린다고 가정해 봅시다. 이것은 거의 최선의 경우에 가깝습니다. 만약 뉴욕에 있고 도쿄의 서버에 연결된 경우 어떻게 될까요? 어떤 이유로 네트워크 혼잡이 있는 경우 어떻게 될까요? 100ms
, 200ms
, 심지어 500ms
의 지연이 발생할 수 있습니다.
다시 예시로 돌아와서, 클라이언트가 서버로 몇 가지 입력을 보냅니다. ("오른쪽 화살표를 눌렀어"). 서버는 50ms
후에 이를 받습니다. 서버가 요청을 처리하고 업데이트된 상태를 즉시 보냅니다. 클라이언트는 새로운 게임 상태 ("이제 (11, 10)
에 있어")를 50ms
후에 받습니다.
게임 플레이어는 오른쪽 화살표를 눌렀지만 0.1초 동안 아무 일도 일어나지 않았다고 느끼게 됩니다. 0.1초가 지난 후에야 캐릭터가 오른쪽으로 한 칸 이동한 것처럼 보입니다. 이런 입력과 그 결과 간의 지연은 그렇게 크지 않아 보일 수 있지만, 눈에 띄게 느껴집니다. 0.5초의 지연은 눈에 띄게 느껴지는 것뿐만 아니라 실제로 게임을 할 수 없게 만듭니다.
요약
네트워크로 연결된 멀티플레이어 게임은 아주 재미있지만, 완전히 새로운 종류의 도전 과제를 던집니다. 권위 있는 서버 아키텍처는 대부분의 치팅을 방지하는 데 꽤 효과적이지만, 직관적인 구현은 플레이어에게 반응이 느린 게임으로 느껴지게 만듭니다.
다음 글에서는 권위 있는 서버를 기반으로 하되, 플레이어가 경험하는 지연을 최소화하여 지역 또는 싱글 플레이 게임과 거의 구별할 수 없도록 어떻게 시스템을 구축할 수 있는지 알아보겠습니다.
시리즈 모두 보기
[*] 1편: 클라이언트 - 서버 아키텍쳐
2편: 클라이언트측 예측과 서버측 재조정
3편: 엔티티 인터폴레이션
4편: 지연 보상
'프로그래밍 > General' 카테고리의 다른 글
멀티플레이 게임서버 구현 3편: 엔티티 인터폴레이션 (2) | 2023.09.22 |
---|---|
멀티 플레이 게임서버 구현 2편: 클라이언트측 예측과 서버측 재조정 (0) | 2023.09.17 |
CPU Overcommit이란? (0) | 2023.07.23 |
포맷 후 cli로 설치 셋업 (0) | 2023.04.17 |
구글 설문지 (Google Forms) 완료시 데이터 외부 API로 전송하기 (0) | 2023.03.22 |