스팀 멀티플레이
P2P 멀티플레이의 한계
이전에 리슨 서버(Listen Server) 멀티 플레이 구현 포스트에서 리슨 서버 멀티플레이 방식에 대해서 다루었다. 리슨 서버는 P2P 연결과 같기 때문에 두 컴퓨터가 서로 공인 IP주소를 알아야 멀티플레이가 이루어질 수 있다. 하지만 대부분 사람들의 컴퓨터의 공인 IP주소는 외부로 공개되지 않기 때문에 포트 포워딩이 필요한데, 이게 상당히 귀찮은 일이기도 하고 이 게임을 배포한다고 했을 때에도 유저에 그런 불편함을 감수하게 할 수는 없는 노릇이다.
그래서 현재까지는 로컬 멀티플레이만 구현이 되어있었다. 포트 포워딩 없이 인터넷 연결을 통해 리슨 서버 멀티플레이가 이루어지는 다른 방법은 시그널링 서버를 통한 홀 펀칭을 이용하는 것이다.
홀 펀칭(Hole Punching)

시그널링 서버는 공인 IP가 공개되어 있는 서버로 두 컴퓨터가 서로의 공인 IP:포트를 기록하는 역할을 맡는다. 두 컴퓨터가 각자의 공인 IP:포트를 시그널링 서버에서 확인하면 그 주소로 동시에 패킷을 보낸다.
패킷을 보낸 직후 상대방으로부터 온 패킷을 받기 때문에 각 컴퓨터의 공유기는 이를 정상적인 응답으로 착각하고 NAT(Network Address Translation) 매핑 테이블에 기록하여 상대 컴퓨터와의 연결을 유지한다.
각 컴퓨터의 패킷 전송은 UDP 프로토콜로 이루어진다.
Steamworks
문제는 이 시그널링 서버를 누가 운영하느냐이다. 큰 회사의 경우 자사의 리소스로 직접 운영하는 일은 버겁지 않겠지만 인디 게임을 운영하는 개인 또는 소규모 개발자는 대기업이 구축한 인프라를 이용하는 수밖에 없다. 그것이 바로 Steamworks이다.

Steam. 전 세계의 게임 유통망을 담당하고 있는 대기업 중의 대기업이다. 이 회사의 게임 개발 및 유통 지원 서비스인 Steamworks는 개발자들이 게임을 만들 때 Steam의 생태계를 이용할 수 있도록 Steam API를 제공한다.
Godot 엔진에서 Steam API는 GodotSteam이라는 애드온으로 설치해 이용할 수 있다.

Steam API를 이용하면 P2P 연결은 물론 로비 생성, 참가, 친구 초대, 채팅과 같은 추가적인 커뮤니케이션 기능까지 Steam이 제공하는 인프라에서 제한 없이 이용할 수 있다. 이를 위해서는 Steamworks에 앱 등록 비용을 결제 후 발급받는 APP ID가 필요한데, 대신 480이라는 가상 게임의 APP ID를 무료로 공개하고 있기 때문에 개발 및 테스트 단계에선 이것을 사용해도 된다.
게임의 실행과 동시에 Steam API 초기화 및 Steam 클라이언트 프로그램의 유효성을 확인한다. Steam 클라이언트가 켜져 있고, 로그인되어 있으면 Steam.getSteamID() 메서드를 통해 현재 로그인된 Steam 계정의 Steam ID 를 얻을 수 있다. Steam ID는 Steam 계정마다 존재하는 고유한 일련번호로 멀티플레이 연결 시 개별 피어를 식별하는 식별자로 쓰인다.
Lobby ID는 하나의 멀티플레이 게임에 참여할 피어들을 함께 관리하는 객체인 Lobby에 발급되는 고유 식별자이다. 로비에 참가할 때 이 Lobby ID를 알아야 하며, 초대 전송이 바로 이 Lobby ID를 전송하는 일이다. 공개 로비의 경우 누구나 Lobby ID를 알 수 있어 모르는 사람들끼리도 멀티플레이를 즐길 수 있는 공개 매치, 무작위 매치 등에서 활용할 수 있다.
Lobby ID를 알아도 보안 때문에 곧바로 로비를 생성한 호스트의 Steam ID를 알 수는 없다. 로비 참가 승인이 이루어져야만 Steam ID를 알 수 있다. 로비 참가 후 응답으로 반환하는 response 값으로 참가 승인이 이루어졌는지 확인할 수 있다. 승인이 확인되면 Steam.getLobbyOwner() 메서드로 로비 주인의 Steam ID를 얻고 로비 주인이 생성한 소켓에 연결한다.
이로써 로비 주인과 참가자는 각각 Player1, Player2가 되어 멀티플레이를 진행할 수 있게 된다. 호스트인 Player1이 서버 역할을 맡으며 동기화를 책임지게 된다.