diff --git a/.gitignore b/.gitignore index 6e3c953..8d86eef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ dist/ .vite/ - +config.json +package-lock.json diff --git a/CONTEXT.md b/CONTEXT.md index bd685be..edfbd06 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -4,25 +4,52 @@ ### [Core Engine] - **`src/main.js`**: Phaser 게임의 전역 설정(Physics, Scale, Canvas Parent)을 담당하며, `ArenaScene`을 인스턴스화합니다. + - 앱 로드 시 `trackVisitor()`를 호출해 방문자 체크 API와 연동합니다. - **`src/constants.js`**: 게임 내 모든 튜닝 수치를 관리합니다. + - `ATTACK_DAMAGE_MIN`, `ATTACK_DAMAGE_MAX`: 일반 공격 1회 적중 시 적용되는 랜덤 피해량 범위. + - `FIGHTER_HITBOX_*`: 100x100 캐릭터 프레임 안에서 실제 충돌 판정이 놓이는 위치와 크기. + - `KILL_HEALTH_RECOVERY_RATIO`, `KILL_GROWTH_MULTIPLIER`: 처치 후 회복량과 크기/공격속도/이동속도 성장 배율. + - `SELECTED_FIGHTER_OUTLINE_GAP`, `SELECTED_FIGHTER_OUTLINE_WIDTH`, `SELECTED_FIGHTER_OUTLINE_ALPHA`: 선택 실루엣의 캐릭터 이격 거리, 두께, 투명도. - `SPECTATOR_CAMERA_LERP`: 카메라 추적의 부드러움 정도. - `MINIMAP_VIEWPORT_SIZE`: 미니맵의 고정 픽셀 크기. - `ARENA_SIZE`: 경기장 전체 크기 (GRID * TILE). +### [Server/API - server/] +- **`server/index.js`**: + - Fastify 서버 진입점입니다. + - 개발 모드에서는 `@fastify/middie`로 Vite 미들웨어를 붙이고, `/api/*` 요청은 Vite SPA fallback이 가로채지 않도록 Fastify 라우트로 통과시킵니다. + - 운영 모드(`npm start`)에서는 `@fastify/static`으로 `dist/`를 서빙하고, HTML 요청은 `index.html`로 fallback합니다. +- **`server/config.js`**: + - `config.json`을 읽어 서버 포트, MongoDB host/port/db/user/pass, 쿠키 보안 옵션을 정규화합니다. + - `MONGODB_URI`가 직접 있으면 우선 사용하고, 없으면 `MONGODB_HOST`/`MONGODB_PORT` 기반으로 URI를 조립합니다. +- **`server/db.js`**: + - `MongoClient`를 한 번 생성한 뒤 재사용하여 MongoDB 커넥션 풀을 유지합니다. + - 종료 시 `closeMongoConnection()`으로 커넥션을 닫습니다. +- **`server/visitors.js`**: + - `POST /api/visitors/check`: `arena_visitor_id` `HttpOnly` 쿠키를 확인하고 없으면 UUID를 발급합니다. + - MongoDB `visitors` 컬렉션에 `_id = visitorId`로 upsert해 방문자 1명당 1개 문서를 유지합니다. + - `GET /api/visitors/stats`: 전체 유니크 방문자 수를 반환합니다. + ### [Game Logic - src/game/] - **`ArenaScene.js`**: - `update()`: 매 프레임 생존 팀을 체크하고 스코어보드를 갱신합니다. - `observeCombat()`: 캐릭터가 공격할 때 카메라가 주목할 "관전 대상"을 설정합니다. + - `selectFighter()`, `focusSelectedFighter()`: 캐릭터 클릭 시 선택 상태를 설정하고 해당 캐릭터의 히트박스 중심으로 카메라를 고정합니다. - `updateMinimapViewportFrame()`: 주 카메라의 이동에 맞춰 미니맵 가이드 사각형을 렌더링합니다. - **`matchSetup.js`**: - 입력된 닉네임을 순회하여 `team` 객체를 생성하고, 요청된 인원만큼 캐릭터 데이터를 복제 배치합니다. - **`combat.js`**: - `updateFighter()`: 가장 가까운 적을 찾아 이동하거나 공격하는 유닛 AI의 핵심입니다. + - `applyHit()`: 일반 공격 피해량은 `ATTACK_DAMAGE_MIN/MAX` 범위에서 계산합니다. + - `applyKillReward()`: 처치한 캐릭터의 체력 회복, 크기 증가, 공격속도/이동속도 배율 증가를 처리합니다. - `projectilePathHitsDefender()`: 투사체가 대상을 스쳐 지나가지 않도록 궤적 검사를 수행합니다. ### [Assets & UI] +- **`fighterAssets.js`**: 원본 캐릭터 스프라이트의 alpha 값을 읽어 선택용 노란 실루엣 spritesheet를 런타임에 생성합니다. 원본 주변 1px은 비워두고 그 바깥 1px만 칠해 선택 윤곽이 캐릭터에 붙어 보이지 않도록 합니다. +- **`fighterFactory.js`**: 캐릭터 히트박스, 이름표, 체력바, 선택 실루엣 sprite를 생성하고 매 프레임 위치/스케일/방향을 동기화합니다. 이름표는 스프라이트 중심이 아니라 실제 히트박스 하단에 고정됩니다. - **`fighterManifest.js`**: 20여 종의 캐릭터 스킨 정보가 담긴 딕셔너리입니다. `type` (melee/projectile/instant-spell)에 따라 전투 메커니즘이 결정됩니다. - **`matchForm.js`**: `index.html`의 입력을 읽어 `ArenaScene`에 매치 구성을 전달합니다. +- **`visitorCounter.js`**: `POST /api/visitors/check`를 호출하고, 응답의 `uniqueVisitors` 값을 `#visitor-count`에 표시합니다. ## 2. 주요 로직 구현 세부 사항 @@ -38,7 +65,34 @@ this.cameras.main.scrollX += (targetX - this.cameras.main.midPoint.x) * SPECTATO 미니맵은 전장 전체를 축소하여 보여주는 독립된 카메라입니다. 주 카메라가 비추는 영역을 계산하여 미니맵 위에 사각형(`graphics`)을 그려줍니다. - `camera.displayWidth / zoom` 등을 이용하여 현재 월드에서 보이는 실제 영역 크기를 계산합니다. +### 캐릭터 선택 실루엣 +선택 표시는 히트박스 사각형이 아니라 캐릭터 모양을 따라가는 별도 spritesheet입니다. + +1. `fighterAssets.js`가 로드된 원본 스프라이트시트의 alpha 데이터를 캔버스에서 읽습니다. +2. 원본 alpha 픽셀 주변 `SELECTED_FIGHTER_OUTLINE_GAP` 범위는 공백 마스크로 남깁니다. +3. 그 바깥 `SELECTED_FIGHTER_OUTLINE_WIDTH` 범위에만 노란색 outline을 칠합니다. +4. `fighterFactory.js`가 선택된 캐릭터 뒤에 outline sprite를 배치하고, 현재 texture frame, flip 방향, scale, 위치를 원본 캐릭터와 동기화합니다. + +이 방식은 별도 에셋 없이도 캐릭터 모양에 맞춘 선택 윤곽을 만들 수 있으며, 캐릭터가 처치 보상으로 커져도 윤곽이 같은 배율로 따라갑니다. + +### 유니크 방문자 체크 +브라우저가 직접 MongoDB에 연결하지 않고, Fastify API가 MongoDB 커넥션 풀을 유지합니다. + +1. 프론트엔드가 앱 로드 시 `POST /api/visitors/check`를 호출합니다. +2. 서버가 `arena_visitor_id` 쿠키를 검사합니다. +3. 쿠키가 없거나 유효하지 않으면 `crypto.randomUUID()`로 새 방문자 ID를 만들고 `HttpOnly` 쿠키로 내려줍니다. +4. MongoDB에는 `_id`, `firstSeenAt`, `lastSeenAt`, `visits`, `firstUserAgent`, `lastUserAgent`를 저장합니다. +5. `countDocuments()`로 전체 유니크 방문자 수를 계산해 반환합니다. + +방문자 체크는 인증 기능이 아니며, 브라우저/쿠키 단위의 단순 유니크 카운트입니다. + ## 3. 개발 및 유지보수 규칙 - **신규 캐릭터 추가**: `public/assets/characters/`에 에셋 배치 후 `fighterManifest.js`에 정의를 추가하면 즉시 게임에 반영됩니다. - **물리 수치 조정**: 캐릭터의 속도나 사거리 등은 `src/constants.js` 또는 `fighterManifest.js` 내 개별 설정을 통해 변경하십시오. +- **공격력 조정**: 기본 피해량은 `src/constants.js`의 `ATTACK_DAMAGE_MIN`, `ATTACK_DAMAGE_MAX`를 수정합니다. 캐릭터별 특수 공격 방식은 `fighterManifest.js`의 `combat` 설정을 우선 확인합니다. - **DOM 접근**: 성능을 위해 `ArenaScene`은 상단 스코어보드 등 필요한 시점에만 최소한으로 DOM에 접근합니다. +- **서버 설정**: `.env` 대신 `config.json`을 사용합니다. `config.json`은 로컬 전용 파일이며, 저장소에는 `config.json.sample`만 공유합니다. +- **패키지 락 파일**: 이 프로젝트는 `package-lock.json`을 저장소에서 제외합니다. 의존성 변경 시 `package.json`을 기준으로 관리합니다. +- **기본 포트**: `SERVER_PORT` 기본값은 `9736`입니다. +- **MongoDB 연결**: DB 접속 정보는 `config.json`의 `MONGODB_HOST`, `MONGODB_PORT`, `MONGODB_DB`, 선택적 `MONGODB_USER`, `MONGODB_PASS`로 관리합니다. +- **API 변경**: `/api/*` 경로는 Fastify 라우트가 담당합니다. 개발 모드에서 Vite 미들웨어가 API 요청을 SPA HTML로 처리하지 않도록 서버 라우팅 순서를 유지해야 합니다. diff --git a/agent.md b/agent.md index 85c0cf8..a4b9b25 100644 --- a/agent.md +++ b/agent.md @@ -4,14 +4,23 @@ **Arena Picker**는 Phaser 3 게임 엔진과 Vite 번들러를 기반으로 구축된 **대규모 팀 전투 시뮬레이션 웹 애플리케이션**입니다. 사용자가 입력한 여러 명의 참가자(닉네임)를 바탕으로 각 참가자를 하나의 팀으로 설정하고, 지정된 인원만큼의 캐릭터를 생성하여 자동 전투를 시뮬레이션합니다. +서버 런타임은 Fastify를 사용하며, MongoDB 커넥션 풀을 유지해 유니크 방문자 수를 기록하는 간단한 방문자 통계 API를 제공합니다. + ## 2. 프로젝트 전체 구조 (Directory Tree) ```text ├── index.html # 메인 HTML 진입점 및 UI 레이아웃 -├── package.json # 프로젝트 의존성 및 스크립트 정의 (Phaser, Vite) +├── package.json # 프로젝트 의존성 및 스크립트 정의 (Phaser, Vite, Fastify, MongoDB) +├── config.json # 로컬 서버/MongoDB 설정 (git ignore) +├── config.json.sample # 공유용 서버/MongoDB 설정 예시 ├── agent.md # 프로젝트 개요 및 기능 정의 (본 문서) ├── CONTEXT.md # 상세 개발 가이드 및 로직 설명 ├── todo.md # 작업 내역 및 잔여 이슈 관리 +├── server/ # Fastify API 서버 및 MongoDB 연결 관리 +│ ├── index.js # Fastify 서버 진입점, Vite 개발 미들웨어, 정적 배포 서빙 +│ ├── config.js # config.json 로드 및 MongoDB URI 조립 +│ ├── db.js # MongoClient 커넥션 풀 생성/재사용/종료 +│ └── visitors.js # 유니크 방문자 체크 및 통계 API ├── public/ # 정적 리소스 (게임 에셋) │ └── assets/ │ └── characters/ # 20종 이상의 캐릭터 스킨 및 투사체 에셋 @@ -19,20 +28,21 @@ │ └── wizard/ # 각 폴더 내 애니메이션 시트 및 이펙트 포함 └── src/ # 소스 코드 root ├── main.js # Phaser 게임 인스턴스 생성 및 초기화 - ├── constants.js # 전역 물리/UI 상수 통합 관리 (줌, 카메라 속도 등) + ├── constants.js # 전역 물리/UI 상수 통합 관리 (공격력, 체력, 줌, 카메라 속도 등) ├── styles.css # UI 스타일링 (스코어보드, 승리 배너 애니메이션) ├── game/ # 게임 로직 모듈 │ ├── ArenaScene.js # 핵심 게임 씬 (카메라 추적, 승리 판정, 스코어보드 제어) │ ├── arenaRenderer.js# 경기장 바닥 및 격자 렌더링 │ ├── combat.js # 전투 AI 및 피격 판정 로직 │ ├── combatSettings.js# 전투 속도 및 이동 배율 관리 - │ ├── fighterAssets.js# 스프라이트 시트 로드 및 애니메이션 생성 - │ ├── fighterFactory.js# 캐릭터 객체 및 HUD 생성 + │ ├── fighterAssets.js# 스프라이트 시트 로드, 애니메이션 및 선택 실루엣 생성 + │ ├── fighterFactory.js# 캐릭터 객체, 히트박스, HUD 및 선택 윤곽 동기화 │ ├── fighterManifest.js# 캐릭터 스킨 데이터 정의 (20종 캐릭터 상세 설정) │ ├── fighterSelection.js# 무작위 캐릭터 스킨 선택 로직 │ └── matchSetup.js # 닉네임 기반 팀 구성 및 스폰 좌표 계산 └── ui/ - └── matchForm.js # 참가자 입력 폼 및 팀 설정 UI 제어 + ├── matchForm.js # 참가자 입력 폼 및 팀 설정 UI 제어 + └── visitorCounter.js# 방문자 체크 API 호출 및 UI 갱신 ``` ## 3. 핵심 기능 @@ -43,16 +53,30 @@ - **미니맵 연동**: 줌 인 상태에서 전장 전체 상황과 현재 뷰포트 위치를 미니맵에 가이드라인으로 표시합니다. - **역동적인 전투 연출**: - 캐릭터별 고유 공격 방식(근접, 투사체, 마법) 및 애니메이션. - - 치명타(Critical) 발생 시 화면 흔들림 효과 및 대미지 가중치 적용. + - `ATTACK_DAMAGE_MIN/MAX`로 기본 공격력 범위를 관리하고, 치명타(Critical) 발생 시 즉시 처치 및 화면 흔들림 효과를 적용합니다. + - 상대를 처치한 캐릭터는 체력을 현재 체력 기준 30% 회복하고, 처치 누적 배율에 따라 크기, 공격속도, 이동속도가 함께 상승합니다. +- **캐릭터 선택 관전**: 캐릭터를 클릭하면 해당 캐릭터에 카메라가 고정되며, 원본 스프라이트 알파 마스크를 바탕으로 1px 공백을 둔 노란 실루엣 윤곽이 표시됩니다. - **실시간 경기 중계 UI**: 상단 좌/우 영역에 팀별 현재 생존 인원을 실시간으로 표시하며, 승리 시 대형 배너로 결과를 알립니다. +- **유니크 방문자 체크**: 접속 시 `POST /api/visitors/check`를 호출하고, 서버가 `HttpOnly` 쿠키 기반 UUID로 MongoDB에 방문자 1명당 1개 문서를 유지합니다. ## 4. 기술 사양 - **Framework**: Phaser 3.90.0 (Arcade Physics 기반) - **Build Tool**: Vite 7.1.12 +- **Server**: Fastify 5.x (`@fastify/static`, `@fastify/middie`) +- **Database**: MongoDB 7.x Node Driver - **UI Logic**: Vanilla JS & CSS (Flexbox/Grid 활용) -## 5. 관련 문서 +## 5. 서버/API 설정 + +- 개발/운영 서버는 `npm run dev` 또는 `npm start`로 실행하며 기본 포트는 `config.json`의 `SERVER_PORT` 값인 `9736`입니다. +- `config.json`은 로컬 설정 파일이므로 저장소에 커밋하지 않습니다. 새 환경에서는 `config.json.sample`을 복사해 사용합니다. +- 기본 API: + - `GET /api/health`: 서버 및 MongoDB 설정 여부 확인. + - `POST /api/visitors/check`: 현재 브라우저 방문자를 체크하고 유니크 방문자 수를 반환. + - `GET /api/visitors/stats`: 전체 유니크 방문자 수 조회. + +## 6. 관련 문서 - [CONTEXT.md](./CONTEXT.md): 상세 개발 가이드 및 핵심 로직 설명 (필독) - [todo.md](./todo.md): 작업 내역 및 잔여 이슈 관리 diff --git a/config.json.sample b/config.json.sample new file mode 100644 index 0000000..63c51e3 --- /dev/null +++ b/config.json.sample @@ -0,0 +1,13 @@ +{ + "SERVER_HOST": "0.0.0.0", + "SERVER_PORT": 9736, + "MONGODB_HOST": "172.16.0.7", + "MONGODB_PORT": 27017, + "MONGODB_DB": "arena", + "MONGODB_USER": "", + "MONGODB_PASS": "", + "MONGODB_VISITOR_COLLECTION": "visitors", + "MONGODB_MAX_POOL_SIZE": 10, + "MONGODB_SERVER_SELECTION_TIMEOUT_MS": 5000, + "COOKIE_SECURE": false +} diff --git a/index.html b/index.html index 4106757..e8f4ff7 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@
Arena Picker
방문자 확인 중