본문 바로가기

블로그 / 개발일지

웹 게임 포털 일곱 곳에 맞추기 — SDK, 오프라인 빌드, 초대 링크

Salmonholic을 CrazyGames·itch.io·Poki와 오프라인 포털 네 곳에 올리려고 빌드를 일곱 갈래로 나눈 기록. 포털별 요구사항, SDK 이벤트, 초대 링크 버그를 정리했다.

Salmonholic ·· 7분 읽기

포털 검증 스크립트가 찍은 Salmonholic 경기 화면과 결과 화면 네 장

Salmonholic은 내 서버에서 도는 브라우저 게임이다. 그런데 브라우저 게임은 내 주소로 사람을 모으기가 어렵다. 그래서 처음부터 웹 게임 포털에 올릴 생각을 했고, 막상 해 보니 포털마다 규칙이 달라서 빌드가 일곱 갈래로 나뉘었다. 이 글은 그 과정을 적은 기록이다.

포털 문서를 먼저 읽었다

처음 한 일은 코드를 고치는 게 아니라 각 포털의 공식 요구사항 문서를 읽고 우리 빌드 상태와 한 줄씩 대조하는 것이었다. 9월 21일에 itch.io, CrazyGames, Poki 세 곳의 문서를 옮겨 적은 제출 노트를 만들었고, 다음 날 한 번 더 읽고 고쳤다. 문서에서 확인하지 못한 값은 “제출 시 확인”이라고 남겨 두었다. 그때 대조한 핵심만 추리면 이렇다.

항목 itch.io CrazyGames Poki
용량 제한 압축 해제 500MB, 파일 1,000개 전체 250MB, 파일 1,500개, 초기 다운로드 50MB 초기 다운로드 최소화 권장
외부 요청 HTTPS면 허용 게임 서버는 직접 준비 기본 차단, 승인 후 허용
필수 SDK 호출 없음 gameplayStart/Stop 첫 입력 때 gameplayStart, 중단마다 gameplayStop
광고 없음 Basic 단계는 광고 없음 Poki 광고가 수익 모델
기타 상대 경로만 자체 전체화면 버튼 금지, 첫 플레이까지 1클릭 이하 로딩 10초를 넘기면 이탈이 늘어난다

우리 빌드는 Godot 4.7.2 웹 export에 스레드 지원을 끈 상태라 SharedArrayBuffer나 특수 헤더가 필요 없었다. 이건 포털 어디에 올려도 편했다. 반대로 곤란했던 건 두 가지다. 하나는 내 서버용 빌드가 index.wasm을 브로틀리 압축본으로만 두고 있었다는 점이다. 포털은 파일을 직접 호스팅하고 압축도 자기 방식으로 하니, 포털 zip에는 원본 39.5MB짜리 wasm을 넣어야 했다. 다른 하나는 셸이 version.json이나 통계 전송 같은 내 서버 경로를 부르고 있었다는 점이다. 포털 도메인에는 그런 경로가 없으니, 셸이 지금 어느 호스트에서 떠 있는지 확인하고 그 기능들을 꺼야 했다.

셸 하나, 빌드 변형 여러 개

Godot 쪽 게임 코드는 하나로 두고, 차이는 HTML 셸과 export 프리셋에서 흡수하기로 했다. 프리셋들은 custom_features 한 줄만 다르고, 스테이징 스크립트가 셸 안의 __PORTAL__ 자리를 포털 이름으로 바꿔 끼운다. 셸은 포털 이름을 보고 어떤 SDK를 불러올지, 엔진에 어떤 인자를 넘길지 정한다. 예를 들어 Poki 변형은 --offline을 넘겨서 외부 요청을 0건으로 만들고, 빈자리는 봇으로 채운 10인 경기를 한다. CrazyGames와 itch 변형은 온라인 매치를 시도하다가 실패하면 오프라인 봇 경기로 넘어간다.

SDK도 해당 포털 도메인에서 떠 있을 때만 불러온다. 로컬에서 테스트할 때는 가짜 SDK 스텁을 끼워서 호출 순서만 확인했다.

처음 세 곳(Poki, CrazyGames, itch)에 이어 9월 25일 커밋 9c7047c에서 GameDistribution, GamePix, GameMonetize, Y8 네 곳을 추가했다. 이 네 곳은 광고 SDK는 붙지만 온라인 매치는 하지 않는 오프라인 빌드다. 검증 스크립트는 실제 슬림 wasm을 GPU 크롬에서 돌리고 포털 SDK 스크립트만 스텁으로 바꾼다. 그 상태에서 광고 버튼, 광고 중 일시정지와 음소거, 탭 숨김, 네 가지 화면 크기, SDK 다운로드가 실패했을 때의 플레이까지 확인한다.

포털 검증에서 찍은 640×480, 800×600, 390×844 화면 같은 빌드를 640×480, 800×600, 세로 390×844 iframe에 띄운 검증 캡처. 세로 화면에서는 안내 문구가 DRAG TO MOVE로 바뀐다.

그날 보고서의 결과표는 이랬다.

포털 엔진 로드 외부 요청 소켓
Poki 3,888ms 0 0
CrazyGames 4,803ms 6 1
itch 913ms 5 1
GameDistribution 1,236ms 1 0
GamePix 1,472ms 1 0
GameMonetize 1,816ms 1 0
Y8 1,219ms 1 0

오프라인 포털 네 곳의 외부 요청 1건은 각 포털의 공식 SDK 스크립트다. 그 밖의 요청은 하나도 나가지 않는다. 포털마다 문서를 따로 읽어야 했던 것도 이때 알게 됐다. Y8은 최신 문서대로 adBreak가 아니라 showAd를 써야 했고, GamePix SDK의 loaded()는 Promise를 돌려준다.

광고는 준비만 하고 꺼 두었다

처음 컨셉 문서에는 “광고 없음”이 적혀 있었다. 그런데 Poki는 자기 광고가 수익 모델이라, Poki에 올린다는 건 광고를 받아들인다는 뜻이었다. 9월 22일에 Poki 테스트를 먼저 해 보자고 정하면서 광고를 수용했다. 광고 위치는 일시정지에서 게임으로 돌아올 때와 결과 화면에서 PLAY AGAIN을 누를 때 두 곳뿐이다. 광고가 도는 동안에는 셸이 오디오를 멈춘다.

CrazyGames는 결국 광고가 없는 Basic Launch로 먼저 올렸다. 9월 26일 0.16.0 커밋 bfd4c5c에서 CrazyGames용 재경기 광고 코드를 넣긴 했지만 기본값은 꺼짐이다. 실제 호스트에서는 주소 뒤에 쿼리를 붙여도 켤 수 없고, 정식 출시 초청을 받은 뒤 셸의 상수 한 줄을 바꿔야 켜진다. 기본 설정에서 광고 요청이 0건인지, 광고 중에 입력과 소리가 정말 멈추는지는 13개 검사로 확인했다.

같은 날 0.15.1 커밋 bfbe4be에서는 CrazyGames 전용 저장소를 붙였다. 프로필과 설정, 언어를 SDK Data 모듈에 저장해서 로그인한 사용자가 다른 기기에서도 진행을 이어 가게 하는 기능이다. 원격 값을 처음 읽을 때 로컬에 있던 진행을 옮기고, 계정이 바뀌면 쓰기를 막은 뒤 새로 불러온다. 이전 계정의 데이터가 새 계정에 덮어써지는 사고를 막으려는 장치다.

초대 링크가 안 불린다는 문제

CrazyGames는 멀티플레이 게임에 추가 요구가 있다. 방 정보를 SDK로 알려야 하고, 친구가 바로 들어올 수 있게 초대 링크를 만들어야 한다. 그런데 CrazyGames QA가 우리 게임의 초대 기능을 감지하지 못했다. 원인은 단순했다. inviteLink()를 사용자가 INVITE 버튼을 눌렀을 때만 부르고 있었다. 9월 26일 56c4bf6에서 참가 가능한 방에 들어가자마자 링크를 준비하도록 바꿨다.

이 문제는 한 번에 끝나지 않았다. 0.19.0(b9e9378)에서는 파티 요청이 429로 거절되면 링크 생성까지 멈추던 연결을 끊었다. 그래도 0.20.0에서 링크가 빠지는 세션이 남았다. 원인을 따라가 보니, 엔진 팩을 받는 사이 로비가 카운트다운으로 넘어가면 첫 방 보고가 “참가 불가”로 나가고, 링크 호출 두 군데가 모두 그 조건에 묶여 있었다.

0.20.1(2af700a)에서는 SDK 초기화가 끝나면 곧바로 세션 링크부터 만들도록 바꿨다. 그 뒤로는 방 정보가 갱신될 때마다 링크를 다시 요청한다. 서버 선택, 사전 입장 성공과 실패, 파티 응답 200·429·시간 초과, 팩 로딩 지연을 조합한 24가지 조건으로 전후를 비교했다.

수정 전 수정 후
링크를 한 번도 안 부른 세션 24개 중 4개 0개
3초 안에 부른 세션 24개 중 12개 24개 모두(23~481ms)

공식 SDK를 그대로 로컬에서 돌린 6번의 실행에서도 실제 초대 주소가 35~59ms 안에 만들어졌다.

돌아보면

포털 대응 작업은 게임을 재미있게 만드는 일과 거리가 멀다. 대신 한 번 틀리면 심사에서 바로 걸린다. 첫 CrazyGames 심사에서도 한 번 반려를 받았다. 그때 배운 건 포털 문서를 표로 옮겨 두고, 변경할 때마다 일곱 개 zip을 전부 자동으로 검증해야 한다는 것이다. 지금은 버전을 올릴 때마다 verify_portals 7/7 통과를 확인한다.

용량 문제는 Godot 웹 export에서 겪은 문제들에, 포털 등록용 이미지는 트레일러·프레스킷·커버 만들기에 따로 적었다. 게임은 Salmonholic 소개 페이지에서 바로 해 볼 수 있다.

#웹게임 포털#CrazyGames#itch.io#Poki#Godot#HTML5

함께 읽으면 좋은 글

전체 글 →