MMORPG를 싱글 플레이 게임과 구분 짓는 것은 눈에 보이는 모든 것 뒤에 있는 서버입니다. Ancient Online: Remastered 개발 일지의 첫 번째 편인 이번 글에서는 저희가 Unreal Engine 5.8로 MMORPG를 어떻게 만들고 있는지 설명합니다. 클라이언트와 서버가 일을 어떻게 나누는지, 세계를 어떻게 시뮬레이션하는지, 그리고 하나로 통합된 세계를 어떻게 라이브로 편집할 수 있는지입니다.
두 개의 조각: 클라이언트와 서버
Ancient Online은 서로 다른 두 개의 소프트웨어로 이루어져 있습니다.
- 클라이언트는 Unreal Engine 5.8 위에서 C++로 작성되었습니다. 렌더링, 사운드, 인터페이스, 입력이 클라이언트의 일입니다.
- 서버는 Unreal과 독립적입니다. 저희가 C++23으로 처음부터 직접 작성했습니다. 게임의 모든 규칙은 여기에서 돌아갑니다.
왜 서버를 엔진 안이 아니라 따로 작성했을까요? MMORPG 서버에 필요한 것은 게임 엔진에 필요한 것과 다르기 때문입니다. 그래픽은 없지만, 수천 개의 개체와 영속 데이터, 긴 가동 시간, 그리고 예측 가능한 성능이 필요합니다. 서버를 따로 두면 꼭 필요한 일만 하고, 바이트 하나까지 제어할 수 있습니다.
권위 서버: 클라이언트를 믿지 않습니다
저희의 기본 원칙은 이렇습니다. 클라이언트는 주장하고, 서버가 결정합니다.
- 이동: 플레이어의 입력은 서버로 가고, 서버가 위치를 계산합니다. 클라이언트는 서버가 알려 준 위치를 부드러운 보간으로 보여 줍니다. 그래서 “스스로 순간 이동”하거나 벽을 통과하는 클라이언트는 서버 입장에서는 어디에도 가지 못합니다.
- 전투: 피해, 적중, 넉백 같은 결과는 서버에서 계산합니다. 예를 들어 콤보 공격이 상대를 쓰러뜨리는지는 서버가 판정하고, 그 결과를 모두에게 동시에 전달합니다.
- 경제: 아이템, 재화, 상점 거래는 서버와 데이터베이스에서만 바뀝니다.
이 방식의 대가는 네트워크 지연을 잘 다뤄야 한다는 것이고, 얻는 것은 부정행위가 훨씬 어렵고 모두에게 같은 규칙이 적용되는 세계입니다.
25 Hz 월드 루프
서버는 세계를 1초에 25번 갱신합니다. 매 스텝마다 몬스터 AI, 이동, 전투, 스킬 효과, 날씨가 모두 진행됩니다. 고정 스텝은 시뮬레이션을 예측 가능하게 하고, 같은 입력이 언제나 같은 결과를 내도록 보장합니다.
모든 플레이어가 모든 것을 알 필요는 없습니다. 서버는 세계를 격자로 나누고, 각 플레이어에게는 자신의 관심 영역 안에 있는 개체(가까이 있는 것들)의 갱신만 보냅니다. 덕분에 붐비는 도시에서도 네트워크 트래픽이 적정 수준으로 유지됩니다.
가벼운 바이너리 프로토콜
클라이언트와 서버 사이에는 JSON도 REST도 없습니다. 모든 메시지는 고정된 레이아웃의 바이너리 패킷입니다. 이로써 대역폭과 CPU 시간을 모두 아낍니다. 새로운 시스템(날씨, 이동, 게임 내 계정 서비스 등)을 추가할 때는 그 시스템을 위한 별도의, 버전이 붙은 패킷 규약을 작성합니다. 그리고 공유 헤더 파일을 사용해 클라이언트와 서버가 같은 규약을 바탕으로 동작하도록 보장합니다.
영속성: 게임을 기다리게 하지 않고 저장하기
플레이어 데이터는 PostgreSQL 데이터베이스에 저장됩니다. 하지만 월드 루프가 스텝마다 데이터베이스를 기다리게 할 수는 없습니다. 그래서 저장은 write-behind 방식의 영속화로, 백그라운드에서 이루어집니다. 월드 서버가 변경 사항을 큐(Redis)에 넣으면, 백그라운드 작성기가 이를 묶어서 데이터베이스에 커밋합니다. 게임의 흐름은 데이터베이스 속도에 좌우되지 않습니다.
채널과 로그인 서버
플레이어는 먼저 로그인 서버에 접속합니다. 로그인 서버는 계정 인증, 캐릭터 선택, 세션을 처리합니다. 그런 다음 네 개의 게임 채널(CH1–CH4) 가운데 하나로 넘어갑니다. 채널의 실시간 상태는 게임 사이트의 서버 상태 페이지에서 볼 수 있습니다.
라이브로 편집하는 하나의 통합 세계
Ancient Online의 세계는 로딩 화면으로 나뉜 개별 맵이 아니라 하나로 통합된 세계로 설계되었습니다. 세계는 셀로 나뉘어 있으며 스트리밍으로 불러옵니다.
세계의 콘텐츠(지형, 오브젝트, 몬스터 배치)는 코드가 아니라 데이터베이스에 버전별로 보관됩니다. 저희는 게임 안의 월드 에디터로 이 세계를 편집할 수 있습니다. 변경 사항은 먼저 초안에서 작업하고, 검사를 거친 다음 게시합니다. 게시할 때마다 새 버전이 만들어지며, 클라이언트는 다이제스트를 비교해 자신이 가진 세계 데이터가 서버의 것과 일치하는지 확인합니다. 일치하지 않으면 데이터가 갱신되기를 기다린 뒤 게임으로 들어갑니다.
퀘스트: 스크립트 언어로서의 Lua
퀘스트와 NPC 대화는 서버에서 Lua 5.4로 작성합니다. 덕분에 서버를 다시 컴파일하지 않고도 퀘스트를 개발하고 테스트할 수 있습니다.
플레이어의 메시지는 저희 서버를 떠나지 않습니다
게임 내 채팅 번역에는 외부 번역 서비스를 쓰지 않습니다. 저희 자체 서버에서 CPU만으로 돌아가는 로컬 번역 모델을 운영하며, 플레이어의 메시지는 어떤 외부 API로도 전송되지 않습니다. 이렇게 해서 서로 다른 언어를 쓰는 플레이어들이, 메시지가 스튜디오 밖으로 나가는 일 없이 서로를 이해할 수 있습니다.
다음 편에서는
개발 일지 2편에서는 같은 세계를 마우스, 터치스크린, 컨트롤러로 플레이할 수 있게 만들면서 내린 결정들을 다룹니다. 스튜디오를 알아보려면 Astronaut Entertainment는 어떤 곳인가요? 글을, 게임 자체는 게임 페이지를 확인하세요.


