Prepare game updates and single-process PM2 deployment

This commit is contained in:
2026-10-14 19:13:36 +09:00
parent 4f24129e29
commit 22792bc2b4
50 changed files with 1546 additions and 197 deletions
+14 -4
View File
@@ -4,6 +4,8 @@
## 실행
운영 서버의 Git clone·PM2·HTTPS 설정은 [배포 안내](docs/deployment.md)를 따른다.
Node.js 24 이상, MongoDB Standalone이 필요하다.
```powershell
@@ -13,7 +15,7 @@ npm run build
npm start
```
`.env`의 `MONGODB_URI`, `MONGODB_DATABASE`, `HOST`, `PORT`를 설정한다. 기본 접속은 `http://localhost:3000`이다. 외부 브라우저에서는 **HTTPS**가 필요하다. 관리자 인증과 Web Crypto를 포함한 기능을 보호하는 프록시 뒤에 배치한다.
`.env`의 `MONGODB_URI`, `MONGODB_DATABASE`, `HOST`, `PORT`를 설정한다. 기본 접속은 `http://localhost:2100`이다. 외부 브라우저에서는 **HTTPS**가 필요하다. 관리자 인증과 Web Crypto를 포함한 기능을 보호하는 프록시 뒤에 배치한다.
Fastify HTTP API·페이지·Socket.IO `/signal`은 한 앱 포트와 MongoDB 연결 풀을 공유한다. 앱 다중 인스턴스의 시그널링 공유는 구현하지 않았다. 개발 중 클라이언트는 다시 빌드하고, 서버는 `npm run dev`로 감시 실행한다. 실행 중인 배포 폴더를 덮어쓰지 않고 새 빌드를 준비한 뒤 교체한다.
@@ -29,11 +31,19 @@ Fastify HTTP API·페이지·Socket.IO `/signal`은 한 앱 포트와 MongoDB
| 에네르기파 | 3 | 으·파·막기를 맞힘 |
| 순간이동 | 1 | 파·에네르기파 회피 |
준비 3초 → 선택 4초 → 공개 1.2초. 기 0으로 시작하며 명중 한 번으로 종료한다. 첫 유효 선택을 잠그고 마감 전에 상대 선택을 공개하지 않는다. 같은 공격은 상쇄되며 공격·회피 비용은 실패해도 소모한다. 봇은 턴 시작 시점의 양쪽 기만 보고 미리 선택한다.
준비 3초 → 선택 최대 3초(기본값) → 공개 1.2초. 봇 대전은 내가 선택하면, 친구·랭킹 대전은 양쪽이 선택하면 즉시 진행한다. 기 0으로 시작하며 명중 한 번으로 종료한다. 첫 유효 선택을 잠그고 양쪽 선택이 확정되기 전에는 상대 선택을 공개하지 않는다. 같은 공격은 상쇄되며 공격·회피 비용은 실패해도 소모한다. 봇은 턴 시작 시점의 양쪽 기만 보고 미리 선택한다.
화면에는 내 기만 표시하고 행동 버튼은 내 캐릭터 아래에 배치한다. 상대 기는 화면에서만 숨긴다. P2P 판정·복원을 위해 양쪽 기가 브라우저에 보관되므로 개발자도구에서의 조회나 공개 행동을 통한 기 추적을 차단하지 않는다.
친구 대전은 마감 후 선택 해시를 교환하고 양쪽 해시가 모이면 선택과 nonce를 공개한다. 양쪽 브라우저가 독립 계산한 결과 해시와 확인 메시지가 일치해야 공개·기록한다. 한쪽 미입력은 패배, 양쪽 미입력은 무승부다. 명시적 나가기를 상대가 확인하면 기권 처리한다.
친구 대전은 선택 즉시 선택 해시를 교환하고 양쪽 해시가 모이면 선택과 nonce를 공개한다. 미선택 상태로 마감되면 미입력 해시를 전송한다. 양쪽 브라우저가 독립 계산한 결과 해시와 확인 메시지가 일치해야 공개·기록한다. 한쪽 미입력은 패배, 양쪽 미입력은 무승부다. 명시적 나가기를 상대가 확인하면 기권 처리한다.
### 글로벌 설정
MongoDB `global_settings`의 `_id: "global"` 문서에 `revision`, `turnDurationMs`(기본 3000), `finishCutsceneSpeed`(기본 1), `matchingDelayMs`(기본 1000), `matchSuccessDelayMs`(기본 1000), `matchPreparationDelayMs`(기본 3000)를 저장한다. 랭킹은 매칭 중 → 성공 → 준비 중, 친구전은 성공 → 준비 중 순서로 표시한다. 준비 화면은 `matchPreparationDelayMs`만큼 표시하며 안내 지연이 첫 턴 선택 시간을 줄이지 않는다. 컷씬 배속 2는 장면 전환과 애니메이션을 두 배 빠르게 재생한다. 컷씬 종료 후 결과 화면으로 자동 전환하며, 최소 결과 공개 시간(1.2초)을 지킨다.
기존 관리자 Basic 인증으로 `GET /api/admin/global-settings`를 조회하고, 현재 `revision`과 설정값 전체을 `POST /api/admin/global-settings`에 보낸다. 예: `{"revision":1,"turnDurationMs":3000,"finishCutsceneSpeed":2,"matchingDelayMs":1000,"matchSuccessDelayMs":1000,"matchPreparationDelayMs":3000}`. 저장 시 리비전이 자동 증가하며, 다른 관리자가 먼저 변경했다면 409를 반환한다. 선택 시간은 500~60000ms 정수, 컷씬 배속은 0.25~4 범위다. 매칭 중·성공·준비 지연은 각각 0~10000ms 정수다. 기존 문서의 누락 필드는 서버 시작 시 기본값으로 추가된다. DB에서 직접 변경할 때도 값의 범위를 지키고 같은 원자적 업데이트에서 `revision`을 1 증가시킨다.
설정 변경은 서버 재시작 없이 다음 대전에 적용된다. 친구전은 페어링·재대전 시 같은 설정을 방에 저장하고, 봇·랭킹 대전도 생성 시 설정을 확정한다. 진행 중 대전과 새로고침 복구는 기존 대전의 설정을 유지한다. 브라우저는 최초 로딩·창 포커스/가시성 복귀·대전 시작 시 `GET /api/global-settings?revision=...`로 확인하고, 변경된 경우만 `localStorage` 캐시를 갱신한다. 조회 실패 시 유효한 캐시 또는 기본값을 유지하며 실제 대전에는 서버가 확정한 설정을 사용한다.
Socket.IO는 방 대기와 SDP/ICE 협상에만 사용하고 DataChannel 준비 확인 후 닫는다. 정상 대전 중 서버는 게임 객체·행동·판정 타이머를 유지하지 않는다. 서버 재시작 중에도 이미 연결된 대전은 진행하며 HTTP 결과 저장은 복구 후 재시도한다.
@@ -101,4 +111,4 @@ npm run build
## 후속 범위
퀵매치·공개 랭킹·MMR·관전·채팅은 포함하지 않는다. 다중 앱 인스턴스, 분산 브라우저 1,500쌍, 운영 TURN 비용·실제 모바일 네트워크 검증은 별도 환경에서 확인한다.
친구전은 P2P, 랭킹 매칭·판정은 단일 서버 프로세스에서 실행한다. 서버 재시작 시 진행 중 랭킹 경기는 복원하지 않는다. MMR·관전·채팅은 포함하지 않는다. 다중 앱 인스턴스, 분산 브라우저 1,500쌍, 운영 TURN 비용·실제 모바일 네트워크 검증은 별도 환경에서 확인한다.