원문 캡처 · github.com
원문 캡처 · github.com

Battlezone Online은 데스크톱, 휴대전화, VR 헤드셋의 브라우저에서 실행하는 무료 3D 멀티플레이 탱크 게임이다. BZFlag 클라이언트 및 서버와 함께 플레이할 수 있고, 설치할 프로그램은 없다.

게임 시작 · 서버 목록 · 조작법과 깃발 도움말 · BZFlag Discord

스크린샷

접속과 서버 운영

공개 서버 목록에는 공개된 bzo 서버와 각 서버가 프록시하는 BZFlag 서버가 표시된다. 행을 클릭하면 해당 게임에 들어간다.

서버는 Node.js, 클라이언트는 Three.js로 만들었으며 WebSockets, WebTransport, WebRTC를 사용한다.

서버를 설치할 때는 GitHub Container Registry의 Docker 이미지, 소스 타르볼 또는 Git 체크아웃을 이용할 수 있다. 프로젝트는 대부분의 사용자에게 Docker를 설치와 업데이트를 위한 더 나은 방법으로 권한다. 설치 안내에는 두 설치 방식과 설정, 리버스 프록시 뒤에서 실행하는 방법, 서버 목록에 등록하는 방법, BZFlag 서버를 프록시하는 방법, 업데이트 방법이 담겨 있다.

통계 페이지나 서버 브라우저처럼 서버를 조회하는 사이트는 API 문서에서 조회 가능한 항목을 확인할 수 있다. /list의 Launch 링크는 설치된 BZFlag 클라이언트에서 BZFlag 서버를 연다. BZFlag 링크 설정 안내는 Linux, Windows, macOS에서 bzflag:// 링크를 설정하는 방법을 설명한다.

bzo는 BZFlag의 모든 깃발을 지원한다. WA Wide Angle은 다른 나쁜 깃발처럼 생성되고 붙었다가 떼어낼 수 있지만, 기능은 하지 않는다. 이 깃발은 시야각을 넓히며, VR에서는 헤드셋이 투영 방식을 제어한다. 자세한 설명을 포함한 나머지 내용은 깃발 안내에서 볼 수 있다.

조작법은 조작 안내에서 확인할 수 있다. 키보드와 마우스, 화면 터치 조작, 게임패드, XR 컨트롤러를 다룬다. 게임 안에서는 / 또는 ?를 누르면 키보드 조작법이 표시된다.

VR 사용 조건과 입력 방식

VR 모드는 네이티브 WebXR을 사용하며 immersive-vr를 지원하는 브라우저와 헤드셋이 필요하다. 로컬에서 검증할 때는 게임을 http://localhost:5154에서 연다. 원격으로 접속하려면 서버 자체의 https 인증서나 리버스 프록시를 통해 게임을 HTTPS로 제공해야 한다. 페이지가 HTTPS로 제공되면 클라이언트는 WebSocket 연결에 자동으로 wss://를 사용한다.

헤드셋 브라우저에서 설치된 앱을 실행하면 2D 시작 화면 없이 VR 진입을 시도한다. xr-launch.js는 나머지 클라이언트가 로드되기 전에 세션을 요청한다. 몰입형 세션에 필요한 사용자 활성화가 발생할 수 있는 각 신호, 즉 창 로드, 포커스, 페이지 표시, 가시성 변경, Launch Handler에서 다시 요청하고, 렌더러는 그 결과로 만들어진 세션을 사용한다. 어느 신호에서도 VR에 진입하지 못하면, 그리고 그 밖의 모든 환경에서는 버튼을 눌러 VR 모드를 시작한다.

저장된 이름이 있으면 바로 참가한다. 이름이 없으면 XR 메뉴에 참가 화면이 열린다. 이 화면에는 2D 진입 대화상자와 같은 이름·팀·전차 선택 항목이 있어, 플레이어가 볼 수 없는 화면에서 기다릴 필요가 없다.

XR에서 입력할 때는 헤드셋의 시스템 키보드를 사용한다. 이름 또는 MOTD 행에 포커스가 가면 키보드가 열린다. Quest Browser 26.1 이상은 시스템 키보드를 제공한다. 이를 지원하지 않는 헤드셋에서는 해당 행이 데스크톱 전용으로 표시되며, 플레이어는 서버가 지정하는 이름으로 계속 참가할 수 있다. 키보드가 열릴 때마다 새 입력이 시작되므로 첫 키 입력은 기존 내용을 뒤에 붙이지 않고 필드 전체를 바꾼다.

설정 톱니바퀴 옆의 VR 원터치 버튼은 헤드셋이 있는 기기에서만 표시된다. Android용 Chrome은 Cardboard를 통해 모든 휴대전화에서 immersive-vr 지원을 보고하기 때문이다. 해당 환경에서는 VR 모드를 설정 메뉴에서 실행할 수 있다.

배포 환경에서 제한적인 Permissions-Policy 헤더를 설정했다면 xr-spatial-tracking=(self)를 허용해야 한다. TLS는 서버 자체 설정(HTTPS 안내) 또는 리버스 프록시(리버스 프록시 안내)를 통해 제공한다.

새 브라우저나 헤드셋, 배포 환경을 확인할 때는 WebXR 검증 체크리스트를 사용한다. WebGPU 렌더링은 이 체크리스트의 범위에 포함되지 않는다.

릴리스와 개발 점검

각 태그 릴리스는 다음을 공개한다.

  • CHANGELOG.md를 바탕으로 작성한 릴리스 노트가 포함된 GitHub 릴리스
  • 소스 타르볼
  • ghcr.io/timriker/bzo:<version>-ubuntu26.04 형식의 버전별 Ubuntu 26.04 이미지
  • 업데이트되는 ubuntu26.04 태그
  • Ubuntu 26.04를 사용하는 ghcr.io/timriker/bzo:<version> 및 ghcr.io/timriker/bzo:latest 이미지

공개되는 모든 이미지는 linux/amd64와 linux/arm64 변형을 포함한다. 릴리스 태그에는 안정 버전 vX.Y.Z만 사용하며, 프리릴리스 및 빌드 메타데이터 태그는 공개하지 않는다.

Docker 이미지는 Ubuntu 26.04에서 Ubuntu 자체 nodejs 패키지인 Node.js 22로 빌드한다. CI도 Ubuntu 26.04 컨테이너에서 같은 Node.js 버전으로 실행한다.

개발 점검 명령은 다음과 같다.

text
npm run check

이 명령은 구문 및 린트 검사, 서버 부팅 검사, 테스트 스위트를 실행한다. CI도 푸시와 풀 리퀘스트에서 같은 검사를 실행한다.

릴리스 준비에 앞서 모든 변경 사항을 커밋하고 작업 트리를 깨끗하게 유지해야 한다. 태그는 특정 트리를 가리키므로, 태그를 만들 때 작업 디렉터리에 남아 있는 편집 내용은 릴리스에 포함되지 않는다. 그러면 버전이 나타내는 내용과 실제 배포 내용이 달라지고, 태그만으로 결과를 재현할 수 없게 된다. 다른 사람이 커밋하지 않은 작업도 포함해 검토하고 무엇인지 확인한 뒤 커밋해야 하며, 이를 남겨 둔 채 태그를 만들면 안 된다. 릴리스를 준비하기 전과 태그를 푸시한 뒤 모두 git status가 깨끗해야 한다. 마지막까지 변경 사항이 남아 있다면 해당 내용 없이 릴리스가 나간 것이다.

로컬에서 릴리스를 준비할 때는 다음을 실행한다.

text
npm run release:prepare -- 1.0.1

이 명령은 다음 파일을 갱신한다.

  • package.json
  • package-lock.json
  • public/version.mjs
  • CHANGELOG.md

네 파일 모두 릴리스 커밋에 포함해야 한다. public/version.mjs는 빠뜨리기 쉬운 파일이며, 이 파일을 포함하지 않고 태그를 만들면 클라이언트가 실제보다 이전 버전을 표시한다. 이어서 새 변경 로그 섹션을 편집해 사용자가 실제로 알아볼 수 있는 변경 사항을 적는다.

로컬 검증에는 다음 명령을 사용한다.

text
npm run check
npm run release:check -- v1.0.1
npm run release:check:increment -- v1.0.1

검증을 마친 뒤 변경 사항을 커밋하고 태그를 만들어 푸시한다.

text
git add package.json package-lock.json public/version.mjs CHANGELOG.md
git commit -m "Release v1.0.1 - short description. Closes #1"
git tag v1.0.1
git push
git push origin v1.0.1

커밋 제목은 릴리스에 이름표를 붙이는 대신 릴리스 내용을 설명한다. 버전은 이미 태그와 package.json, CHANGELOG.md에 있으므로, 제목만 보이는 git log --oneline에서도 변경 내용을 알 수 있도록 몇 단어로 요약한다. 릴리스가 이슈를 실제로 해결했다면 이슈 번호만 적는 (#NN) 대신 이슈를 닫는 실제 키워드를 사용한다.

릴리스 워크플로는 다음 작업을 수행한다.

  • 안정 버전 태그가 이전 릴리스보다 새 버전이며 main을 가리키는지 확인한다.
  • 의존성을 설치하고 린트, 검증, 감사, CodeQL 검사를 실행한다.
  • package.json 버전이 푸시된 태그와 다르면 실패한다.
  • CHANGELOG.md에 해당 버전의 자리표시자가 아닌 변경 로그 섹션이 없으면 실패한다.
  • Ubuntu 26.04와 Ubuntu Node.js 22를 사용해 linux/amd64, linux/arm64용 이미지를 빌드하고 스모크 테스트한다.
  • 검증된 버전별 및 이동형 Docker 태그를 GHCR로 승격한다.
  • GitHub 릴리스를 공개하고 소스 타르볼을 첨부한다.

워크플로가 실패한 릴리스는 그대로 둔다. bzo는 개발 중인 프로젝트이며 모든 태그에서 산출물이 만들어지는 것은 아니다. 원인을 해결한 뒤 다음 버전을 사용해야 한다. 공개된 태그를 삭제하거나 옮기지 말고, 빌드되지 않은 태그에 GitHub 릴리스를 나중에 만들어 채우지도 않는다. 실패한 버전의 섹션도 이미 기록하는 CHANGELOG.md가 릴리스 기록으로 남는다.

public/version.mjs는 scripts/prepare-release.mjs가 작성하고 scripts/check-release.mjs가 태그와 대조해 검증한다. 이 파일을 직접 수정하지 말고, 다른 곳에 클라이언트 버전 문자열을 하드코딩하지 않는다.

변경 이력은 사람이 읽을 수 있는 형태로 CHANGELOG.md에 보관한다. 태그가 지정된 GitHub 릴리스는 해당 변경 로그 섹션을 릴리스 노트로 사용한다.

라이선스와 포함 자산

코드는 AGPLv3 라이선스를 따른다. 포함된 자산의 라이선스는 모두 같지 않다.

bzo에는 여러 출처의 모델, 텍스처, 사운드, 맵이 들어 있다. 일부는 BZFlag 프로젝트의 자산으로 해당 프로젝트의 라이선스에 따라 사용하고, 일부는 bzo용으로 제작했으며, 일부는 기여와 함께 들어왔다. 파일이 저장소에 들어 있다는 이유만으로 저장소의 라이선스를 자동으로 따르지는 않는다. 출처를 기록하지 않은 자산은 나중에 확인할 사람이 답해야 할 문제로 남는다.

새로 추가하는 항목은 풀 리퀘스트에서 출처와 적용 조건을 밝혀야 하며, AGPLv3 배포와 호환되는 조건을 우선해야 한다. 파일이 어떤 도구로 내보내졌다는 헤더는 이용 허가를 뜻하지 않는다. 제작자가 확인하지 않았다는 메모도 마찬가지다.

현재 저장소의 모든 자산이 이 기준을 충족하는 것은 아니다. 일부는 이 기준을 세우기 전에 들어왔고, 일부는 기여 자체의 가치 때문에 포함됐다. 프로젝트는 이미 기준을 충족한다고 가장하기보다 모든 자산의 라이선스를 명확히 기록하는 것을 목표로 한다. 자산의 출처를 알고 있거나 제작자라면 이슈에 알려 달라고 프로젝트는 요청한다.

맵도 같은 문제를 다른 형태로 안고 있다. 서버에서 내려받거나 클라이언트에서 저장한 맵에는 이용 허가가 따라오지 않는다. 맵 형식은 BZW 문서에서 확인할 수 있다.

프로젝트는 GNU Affero General Public License v3.0에 따라 배포된다. 네트워크를 통해 이용하는 사람은 실행 중인 앱의 /source에서 소스 코드에 접근하거나 GitHub 저장소에서 확인할 수 있다.