docs: add rendered developer and player handbook
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# 파일 지도와 변경 절차
|
||||
|
||||
## 최상위 지도
|
||||
|
||||
```text
|
||||
core2026/
|
||||
├─ app/
|
||||
│ ├─ gateway-frontend/ 계정·로비·관리 UI
|
||||
│ ├─ gateway-api/ 인증·profile·operation·orchestrator
|
||||
│ ├─ game-frontend/ profile 게임 SPA
|
||||
│ ├─ game-api/ tRPC/SSE와 mutation 수신
|
||||
│ └─ game-engine/ turn daemon과 persistence orchestration
|
||||
├─ packages/
|
||||
│ ├─ common/ 공통 타입·직렬화·RNG
|
||||
│ ├─ logic/ 명령·constraint·전투·trigger·scenario
|
||||
│ ├─ infra/ Prisma·PostgreSQL·Redis
|
||||
│ └─ tools-scripts/ resource schema 도구
|
||||
├─ resources/ scenario·map·unitset·명령 profile
|
||||
├─ tools/
|
||||
│ ├─ integration-tests/ DB/Redis 및 ref 차등
|
||||
│ ├─ frontend-legacy-parity/ 실제 Chromium 비교
|
||||
│ ├─ legacy-db-migration/ 장기보존 데이터 CLI
|
||||
│ ├─ build-scripts/ profile resource 복사 기반 build 도구
|
||||
│ └─ docs/ 문서 생성 도구
|
||||
└─ docs/ VitePress 소스와 상세 설계 문서
|
||||
```
|
||||
|
||||
## 기능에서 파일로
|
||||
|
||||
| 기능 | 시작점 | 핵심 하위 경계 |
|
||||
| -------------- | --------------------------------------------- | ------------------------------------- |
|
||||
| 로그인·session | `gateway-api/src/router.ts` | `auth/*`, `account/router.ts`, Redis |
|
||||
| profile 운영 | `gateway-api/src/adminRouter.ts` | `orchestrator/*`, gateway Prisma |
|
||||
| 게임 인증 | `game-api/src/context.ts`, `router/auth` | session actor, profile·sanction |
|
||||
| 메인 턴 입력 | `game-frontend/src/stores/mainDashboard.ts` | `router/turns`, `turns/*` |
|
||||
| command 실행 | `game-engine/src/turn/reservedTurnHandler.ts` | `packages/logic/src/actions/turn` |
|
||||
| 월간 lifecycle | `game-engine/src/turn/turnDaemon.ts` | `monthly*Handler.ts`, scenario events |
|
||||
| 전투 | `actions/turn/general/che_출병.ts` | `packages/logic/src/war` |
|
||||
| DB load/flush | `game-engine/src/turn/worldLoader.ts` | `databaseHooks.ts`, `packages/infra` |
|
||||
| 공개/국가 정보 | `game-api/src/router/public`, `router/nation` | DTO와 redaction |
|
||||
| 화면 parity | `game-frontend/src/views`, `styles` | `tools/frontend-legacy-parity` |
|
||||
|
||||
## 변경 절차
|
||||
|
||||
### API나 화면
|
||||
|
||||
router의 input, auth procedure, transaction과 response를 먼저 정한 뒤 frontend 호출부와 오류·loading 상태를
|
||||
연결합니다. public prefix에서 direct navigation, asset, tRPC와 SSE URL을 확인합니다. UI를 바꾸면 실제
|
||||
Chromium에서 ref와 geometry·computed style·interaction을 비교합니다.
|
||||
|
||||
### 도메인 로직
|
||||
|
||||
ref entry point부터 SQL·로그까지 호출 순서를 찾고, `packages/logic` 계산과 engine context/persistence를 함께
|
||||
수정합니다. unit test만으로 끝내지 않고 fixed seed, 전체 state, RNG trace, 실패 side effect와 가능한
|
||||
ref 차등을 확인합니다.
|
||||
|
||||
### DB
|
||||
|
||||
기존 migration을 고치지 않고 새 migration을 만듭니다. 빈 DB 전체 적용, 기존 DB 증분 적용, 재실행 no-op,
|
||||
constraint/index와 runtime query, backup/restore 또는 rollback 경로를 확인합니다.
|
||||
|
||||
## 문서 사이트
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm docs:generate
|
||||
pnpm docs:dev
|
||||
pnpm docs:build
|
||||
pnpm docs:preview
|
||||
```
|
||||
|
||||
`docs:dev`와 `docs:build`는 먼저 커맨드 목록을 생성합니다. 정적 결과는 `docs/.vitepress/dist`에 생기며
|
||||
Git에 포함하지 않습니다. 문서만 바꿔도 Prettier, 생성 결과의 clean diff, VitePress build와 내부 링크를
|
||||
검증해 주세요.
|
||||
|
||||
## 리팩터링 체크포인트
|
||||
|
||||
- [문서 기준선](../reference-baseline.md)과 현재 commit의 diff를 먼저 봅니다.
|
||||
- 파일 이동만 했는지 소유권·transaction·호출 순서까지 바뀌었는지 구분합니다.
|
||||
- public API, DB schema, action key와 resource format은 내부 파일명보다 강한 계약입니다.
|
||||
- `rg`로 이 페이지의 이전 경로가 남았는지 확인합니다.
|
||||
- command key를 바꿨다면 저장된 예약 턴과 profile resource의 migration/호환 경로가 필요합니다.
|
||||
- 문서의 광범위한 “완료” 표현은 관련 integration·ref 차등·Chromium 증거가 있을 때만 사용합니다.
|
||||
@@ -0,0 +1,106 @@
|
||||
# 도메인 로직과 핵심 클래스
|
||||
|
||||
## 핵심 entity
|
||||
|
||||
`packages/logic/src/domain/entities.ts`가 HTTP나 Prisma row에 종속되지 않은 `General`, `City`, `Nation`,
|
||||
`Troop`, diplomacy와 trigger 상태를 정의합니다. engine 전용 `TurnGeneral`, `TurnWorldState`,
|
||||
`TurnEvent`는 `app/game-engine/src/turn/types.ts`에서 실행 시간·예약 턴·월간 상태를 더합니다.
|
||||
|
||||
| entity | 핵심 책임 |
|
||||
| ------- | -------------------------------------------------------------------------- |
|
||||
| General | 능력치, 경험·공헌, 소속·도시·부대, 병력·훈련·사기, 자원, 특기·아이템, meta |
|
||||
| City | 소유 국가, 규모, 인구·농업·상업·치안, 수비·성벽, 보급·전선 상태 |
|
||||
| Nation | 수도, 국고·군량, 등급·국가 타입, 기술과 국가 meta |
|
||||
| Troop | 부대장·구성원과 부대 상태 |
|
||||
| World | 현재 연·월, 최근 턴 시각, scenario config/meta와 전체 entity collection |
|
||||
|
||||
Prisma row를 곧바로 게임 규칙에 넘기지 않습니다. `worldLoader.ts`와 API의 row mapper가 DB 표현을 domain
|
||||
표현으로 바꾸고, flush 계층이 반대 변환을 담당합니다.
|
||||
|
||||
## 명령 정의
|
||||
|
||||
`GeneralActionDefinition`은 장수·국가 예약 명령이 공유하는 계약입니다.
|
||||
|
||||
- `key`, `name`: 저장 key와 화면 표시명
|
||||
- `parseArgs`: 외부 입력을 실행 인자로 변환
|
||||
- `buildPermissionConstraints`: 예약 입력 자체를 허용할지 판단
|
||||
- `buildMinConstraints`: command table에서 현재 가능한지 사전 판단
|
||||
- `buildConstraints`: 실행 시점의 전체 조건
|
||||
- `getPreReqTurn`, `getPostReqTurn`: 연속 실행과 재사용 대기
|
||||
- `resolve`: domain state와 effect를 계산
|
||||
|
||||
각 파일의 `commandSpec`은 category, 인자 필요 여부, schema와 definition factory를 등록합니다.
|
||||
`GENERAL_TURN_COMMAND_KEYS`, `NATION_TURN_COMMAND_KEYS`가 전체 key 집합이며, `TurnCommandProfile`이
|
||||
profile별 subset을 선택합니다.
|
||||
|
||||
## Constraint 시스템
|
||||
|
||||
`packages/logic/src/constraints`는 “무엇이 필요한가”와 “현재 view가 무엇을 알고 있는가”를 분리합니다.
|
||||
`ConstraintContext`에는 actor, city, nation, args, env와 평가 mode가 있고 `StateView`가 entity와 대상
|
||||
정보를 제공합니다.
|
||||
|
||||
평가 결과는 다음 셋입니다.
|
||||
|
||||
- `allow`: 현재 정보로 조건을 만족합니다.
|
||||
- `deny`: 이유가 확정된 실패입니다.
|
||||
- `unknown`: 대상 입력이나 추가 state가 없어 아직 판정할 수 없습니다.
|
||||
|
||||
API command table은 `unknown`의 missing requirement가 대상 입력뿐이면 `needsInput`, 그 밖이면
|
||||
`unknown`으로 보여 줍니다. 예약 뒤 실제 실행에서는 전체 context로 다시 판단합니다.
|
||||
|
||||
## 핵심 클래스와 조립 지점
|
||||
|
||||
### TurnDaemonLifecycle
|
||||
|
||||
`app/game-engine/src/lifecycle/turnDaemonLifecycle.ts`에 있습니다. clock, control queue, hook과 run budget을
|
||||
조정하며 pause/resume/manual/scheduled run의 상태 전이를 소유합니다.
|
||||
|
||||
### DatabaseTurnDaemonLease
|
||||
|
||||
`app/game-engine/src/lifecycle/databaseTurnDaemonLease.ts`에 있습니다. profile별 단일 active owner와 fencing을
|
||||
관리합니다. daemon 계산이 맞아도 lease를 잃었다면 결과를 저장하면 안 됩니다.
|
||||
|
||||
### InMemoryTurnWorld
|
||||
|
||||
`app/game-engine/src/turn/inMemoryWorld.ts`에 있습니다. entity map, dirty/create/delete set, log, message,
|
||||
event, checkpoint와 월 변경을 소유합니다. `peekDirtyState()`는 저장할 변경을 보여 주고 성공한 flush 뒤
|
||||
정리됩니다.
|
||||
|
||||
### EngineStateManager
|
||||
|
||||
`app/game-engine/src/turn/engineStateManager.ts`에 있습니다. world와 예약 턴 store 같은 mutable participant를
|
||||
등록하고 계산 단위의 capture/restore/transaction을 제공합니다. PostgreSQL transaction을 대신하지 않고
|
||||
실패한 계산의 메모리 rollback을 담당합니다.
|
||||
|
||||
### InMemoryReservedTurnStore와 ReservedTurnHandler
|
||||
|
||||
`reservedTurnStore.ts`는 장수 30칸·국가 12칸 예약 queue를 메모리에 유지합니다.
|
||||
`reservedTurnHandler.ts`는 명령 loading, constraint, action context, AI fallback, progress/cooldown,
|
||||
효과·로그와 queue rotation을 연결합니다.
|
||||
|
||||
### GeneralActionPipeline과 trigger module
|
||||
|
||||
`packages/logic/src/actions/engine.ts`, `triggers/*`는 명령 본체 전후의 특기·아이템·국가 특성 효과를
|
||||
일정한 우선순위로 적용합니다. 같은 module 목록이라도 실행 순서가 결과와 RNG 소비를 바꿀 수 있습니다.
|
||||
|
||||
### WarEngine
|
||||
|
||||
`packages/logic/src/war/engine.ts`가 전투 resolution을, `war/actions.ts`와 trigger module이 확장 효과를,
|
||||
`war/aftermath.ts`가 피해·점령·외교·후속 state를 계산합니다. `che_출병.ts`가 map, unit set, diplomacy,
|
||||
time, seed와 aftermath를 조립하는 실제 장수 명령 entry입니다.
|
||||
|
||||
### GatewayOrchestrator
|
||||
|
||||
`app/gateway-api/src/orchestrator/gatewayOrchestrator.ts`가 DB의 profile desired state를 process state에
|
||||
맞춥니다. `workspaceManager.ts`, `buildRunner.ts`, `seedProfileDatabase.ts`, `pm2ProcessManager.ts`가
|
||||
commit worktree 준비부터 build, seed, start/stop을 나눕니다.
|
||||
|
||||
## 새 명령을 추가할 때
|
||||
|
||||
1. 가장 가까운 ref command의 constraint, 실행 순서, RNG, 로그와 DB side effect를 조사합니다.
|
||||
2. `packages/logic/src/actions/turn/{general,nation}`에 definition과 `commandSpec`을 작성합니다.
|
||||
3. 해당 `*_TURN_COMMAND_KEYS`와 필요한 `resources/turn-commands` profile에 key를 등록합니다.
|
||||
4. 인자가 있으면 Zod schema와 `app/game-api/src/turns/commandInput.ts`의 화면 입력 field를 연결합니다.
|
||||
5. engine action context가 대상 entity·map·unit set·시간·seed를 완전하게 공급하는지 확인합니다.
|
||||
6. permission/min/full 실패, 성공, 연속 턴, cooldown과 persistence를 테스트합니다.
|
||||
7. `pnpm docs:generate`로 플레이어 커맨드 목록을 갱신하고 ref 매핑 문서를 함께 수정합니다.
|
||||
@@ -0,0 +1,29 @@
|
||||
# 개발자 핸드북
|
||||
|
||||
이 핸드북은 새 기능을 어디에 넣을지뿐 아니라 요청이 어떤 경계를 지나 상태로 남는지 설명합니다. 먼저
|
||||
[문서 기준선](../reference-baseline.md)을 확인하고, 변경 성격에 따라 다음 순서로 읽어 주세요.
|
||||
|
||||
| 변경하려는 것 | 먼저 읽을 문서 | 주로 확인할 코드 |
|
||||
| -------------------------- | ---------------------------------------------------- | -------------------------------------------- |
|
||||
| 화면·라우팅·조회 API | [시스템 아키텍처](./system-architecture.md) | `app/*-frontend`, `app/*-api` |
|
||||
| 턴 입력·게임 상태 mutation | [요청·턴·저장 흐름](./request-turn-persistence.md) | `app/game-api`, `app/game-engine` |
|
||||
| 명령·전투·월간 로직 | [도메인 로직과 핵심 클래스](./domain-and-classes.md) | `packages/logic`, `app/game-engine/src/turn` |
|
||||
| 새 파일 위치·검증 범위 | [파일 지도와 변경 절차](./code-map.md) | package manifest, test, docs |
|
||||
|
||||
## 읽을 때 지켜야 할 경계
|
||||
|
||||
- `packages/logic`의 순수 계산과 `app/game-engine`의 scheduling·persistence orchestration을 구분합니다.
|
||||
- game API가 mutation을 받는 것과 engine이 world mutation을 확정하는 것은 다른 단계입니다.
|
||||
- PostgreSQL `input_event`가 내구성 있는 작업 경계이며 Redis는 realtime fan-out과 일부 보조 worker
|
||||
통신에 사용됩니다.
|
||||
- 로그인한 사용자, 게임 장수, 국가 직책은 같은 개념이 아닙니다. actor와 소유권은 session에서
|
||||
서버가 결정합니다.
|
||||
- `resources/`의 scenario·map·unit set·turn-command profile이 런타임 구성을 바꿉니다. 기본 TypeScript
|
||||
목록만 보고 실제 profile을 단정하지 않습니다.
|
||||
- ref 호환 변경은 결과뿐 아니라 판정·정렬·반올림·RNG 소비·로그·저장 순서를 비교합니다.
|
||||
|
||||
## 기존 상세 문서와의 관계
|
||||
|
||||
이 핸드북은 탐색용 상위 지도입니다. 상세한 호환 근거와 테스트 절차는 `docs/architecture/*`,
|
||||
`docs/integration-tests.md`, `docs/frontend-legacy-parity.md`에 유지합니다. 상위 작업공간의
|
||||
`../docs/ref-core2026-mapping.md`는 ref entry point와 core 구현의 end-to-end 대응 인덱스입니다.
|
||||
@@ -0,0 +1,99 @@
|
||||
# 요청·턴·저장 흐름
|
||||
|
||||
## 조회 요청
|
||||
|
||||
일반 query는 다음 경로를 따릅니다.
|
||||
|
||||
```text
|
||||
Vue view/store
|
||||
-> tRPC client
|
||||
-> game-api router
|
||||
-> session actor + 입력 validation
|
||||
-> Prisma query
|
||||
-> 권한에 맞춘 DTO/redaction
|
||||
-> 화면 상태
|
||||
```
|
||||
|
||||
조회는 engine의 in-memory object를 직접 공유하지 않습니다. 따라서 daemon이 transaction을 commit하기 전의
|
||||
중간 계산은 API query에 노출되지 않습니다.
|
||||
|
||||
## API가 직접 끝내는 mutation
|
||||
|
||||
예약 턴, 메시지처럼 API가 DB에서 완결할 수 있는 변경도 `input_event`를 사용합니다.
|
||||
|
||||
1. `Idempotency-Key`와 tRPC path로 요청 identity를 만듭니다.
|
||||
2. `app/game-api/src/inputEventBoundary.ts`가 중복·처리 상태를 확인합니다.
|
||||
3. 같은 PostgreSQL transaction에서 대상 row와 input event 결과를 저장합니다.
|
||||
4. commit 뒤 응답하고 필요한 realtime 갱신을 알립니다.
|
||||
|
||||
동일 revision을 전제로 한 예약 턴 수정은 다른 탭이나 요청이 먼저 갱신했으면 충돌합니다. frontend는 최신
|
||||
목록을 다시 불러와 사용자의 변경을 덮어쓰지 않게 해야 합니다.
|
||||
|
||||
## engine이 처리하는 mutation
|
||||
|
||||
```text
|
||||
game-api mutation
|
||||
-> input_event PENDING
|
||||
-> daemon claim (FOR UPDATE SKIP LOCKED)
|
||||
-> lease/fencing 확인
|
||||
-> EngineStateManager savepoint
|
||||
-> command/turn/monthly handler가 InMemoryTurnWorld 변경
|
||||
-> world + 예약 턴 + log + message + event 결과 flush
|
||||
-> input_event COMPLETED를 같은 DB transaction으로 commit
|
||||
-> commit 이후 realtime 신호
|
||||
```
|
||||
|
||||
계산이나 DB 쓰기가 실패하면 `EngineStateManager`가 등록된 in-memory participant를 savepoint로 되돌립니다.
|
||||
DB transaction도 commit되지 않아 메모리와 DB의 부분 진행을 피합니다. lease를 잃은 worker는 stale 결과를
|
||||
commit할 수 없어야 합니다.
|
||||
|
||||
## 한 tick의 처리
|
||||
|
||||
`TurnDaemonLifecycle`은 clock과 schedule에서 다음 실행 시점을 구합니다. 턴을 시작하면
|
||||
`InMemoryTurnProcessor`와 `InMemoryTurnWorld`가 `turnTime`, 그다음 `general.id` 순서로 실행 대상을
|
||||
결정합니다. checkpoint는 재시작 시 이미 처리한 동일 시점의 장수를 건너뛰는 기준입니다.
|
||||
|
||||
장수 한 명의 예약 명령은 대략 다음 순서입니다.
|
||||
|
||||
1. `InMemoryReservedTurnStore`에서 첫 예약 명령을 읽습니다.
|
||||
2. 명령 key를 `GeneralTurnCommandLoader` 또는 `NationTurnCommandLoader`로 불러옵니다.
|
||||
3. `actionContextBuilder`가 대상 도시·국가·장수, map, unit set, 외교, 시간과 RNG를 구성합니다.
|
||||
4. permission/min/full constraint를 목적에 맞게 평가합니다.
|
||||
5. 선행 턴이 있으면 진행 상태를 쌓고, 완성된 시점에 `resolve()`를 실행합니다.
|
||||
6. effect와 직접 변경을 world에 반영하고 로그·메시지·후속 턴 시간을 기록합니다.
|
||||
7. 실행된 queue를 당기고 끝에 기본 `휴식`을 채웁니다.
|
||||
|
||||
예약 시 통과와 실행 시 성공은 같지 않습니다. 그 사이 자원, 도시 소유, 외교, 직책이 바뀔 수 있으므로 full
|
||||
constraint는 실행 순간 다시 평가됩니다.
|
||||
|
||||
## 월 변경 경계
|
||||
|
||||
`InMemoryTurnWorld.advanceMonth()`는 다음 순서를 보존합니다.
|
||||
|
||||
1. 다음 연·월을 계산합니다.
|
||||
2. `beforeMonthChanged` handler를 등록 순서대로 실행합니다.
|
||||
3. world의 현재 연·월을 바꿉니다.
|
||||
4. `onMonthChanged` handler를 등록 순서대로 실행합니다.
|
||||
5. 연도가 바뀌었으면 `onYearChanged`를 실행합니다.
|
||||
|
||||
`turnDaemon.ts`의 `composeCalendarHandlers()` 순서에는 월간 event, 수입, 연감, PRE_MONTH 상태 정리,
|
||||
국가 명령, 국가 통계, 외교, 전쟁 설정, 방랑, 국가 수, 통일, 토너먼트, 경매, 전선 상태가 포함됩니다.
|
||||
이 순서는 ref의 관찰 가능한 결과와 RNG·persistence에 영향을 주므로 리팩터링 시 단순 정렬하지 않습니다.
|
||||
|
||||
## RNG 경계
|
||||
|
||||
게임 결과에 영향을 주는 난수는 `LiteHashDRBG`와 `RandUtil`을 사용합니다. seed에는 hidden seed와
|
||||
action/month/general 같은 context가 직렬화됩니다. main RNG의 소비 순서를 유지해야 하는 로직과 독립된
|
||||
재현 가능 substream을 써야 하는 fallback을 구분합니다. authoritative path에 `Math.random()`을 넣지
|
||||
않습니다.
|
||||
|
||||
## 장애를 추적할 위치
|
||||
|
||||
| 증상 | 우선 확인 |
|
||||
| ----------------------------- | --------------------------------------------------------------------- |
|
||||
| 같은 mutation이 두 번 보임 | idempotency key, `input_event` 상태·attempt |
|
||||
| 요청은 성공했지만 화면이 늦음 | DB commit 결과, Redis/SSE fan-out |
|
||||
| daemon이 처리하지 않음 | profile gate, pause 상태, lease owner, PENDING claim |
|
||||
| 재시작 뒤 일부 턴 반복 | checkpoint와 general turn ordering |
|
||||
| DB와 메모리가 다름 | `EngineStateManager`, `databaseHooks`, flush 대상 누락 |
|
||||
| ref와 결과가 다름 | constraint 순서, action context, RNG trace, rounding, log/effect 순서 |
|
||||
@@ -0,0 +1,99 @@
|
||||
# 시스템 아키텍처
|
||||
|
||||
## 런타임 구성
|
||||
|
||||
```text
|
||||
브라우저
|
||||
├─ /gateway/ ─ gateway-frontend ─ tRPC ─ gateway-api
|
||||
│ ├─ PostgreSQL public schema
|
||||
│ ├─ Redis session
|
||||
│ └─ orchestrator ─ Git worktree / build / PM2
|
||||
│
|
||||
└─ /{profile}/ ─ game-frontend ─ tRPC/SSE ─ game-api
|
||||
├─ profile별 PostgreSQL schema
|
||||
├─ Redis realtime/battle worker
|
||||
└─ input_event ─ game-engine
|
||||
├─ in-memory world
|
||||
└─ transactional flush
|
||||
```
|
||||
|
||||
Gateway는 계정·session·profile lifecycle을, game 계층은 한 profile의 플레이와 턴 진행을 소유합니다.
|
||||
`/gateway`, `/che`, `/hwe` 같은 외부 prefix와 `/image/*`의 Caddy 소유권은 애플리케이션 밖의 배포
|
||||
계약입니다.
|
||||
|
||||
## 애플리케이션
|
||||
|
||||
### gateway-frontend
|
||||
|
||||
`app/gateway-frontend/src/main.ts`가 Vue 앱과 router를 시작합니다. 가입·로그인·계정·로비·관리자 화면은
|
||||
`src/views`에 있고, profile 선택 뒤 game frontend로 이동합니다. 브라우저에 보이는 `VITE_*` 값은
|
||||
공개 설정이며 secret이 아닙니다.
|
||||
|
||||
### gateway-api
|
||||
|
||||
`app/gateway-api/src/server.ts`와 `router.ts`가 HTTP/tRPC 경계입니다.
|
||||
|
||||
- `auth/*`, `account/router.ts`: Kakao·로컬 계정, session 발급·폐기, 사용자 저장소
|
||||
- `lobby/profileStatusService.ts`: 사용자에게 보여 줄 profile 상태
|
||||
- `adminRouter.ts`, `adminAuth.ts`: 관리자 권한과 operation 입력
|
||||
- `orchestrator/*`: 원하는 profile 상태를 Git worktree, build, seed, PM2 프로세스에 반영
|
||||
|
||||
Gateway DB는 기본 `public` schema를 사용합니다. game profile DB를 직접 게임 로직의 source of truth로
|
||||
대체하지 않습니다.
|
||||
|
||||
### game-frontend
|
||||
|
||||
`app/game-frontend/src/main.ts`가 profile base path 아래 Vue SPA를 시작합니다. `src/views`가 공개 정보,
|
||||
메인 턴 입력, 국가 운영, 경매·토너먼트·기록 화면을 나누고 `src/stores/mainDashboard.ts`가 메인 화면의
|
||||
query, 예약 턴 revision, mutation과 realtime refresh를 조정합니다.
|
||||
|
||||
UI는 서버가 반환한 command table의 `available`, `blocked`, `needsInput`, `unknown` 상태를 사용합니다.
|
||||
클라이언트가 장수 ID나 직책을 보냈다는 이유만으로 권한이 생기지 않습니다.
|
||||
|
||||
### game-api
|
||||
|
||||
`app/game-api/src/server.ts`와 `router.ts`가 query/mutation을 공개합니다. router는 기능 단위로
|
||||
`src/router/*`에 나뉩니다.
|
||||
|
||||
- 읽기: world, public, directory, ranking, yearbook, dynasty 등은 권한·redaction을 거쳐 DB에서 조회합니다.
|
||||
- 플레이: turns, join, nation, troop, diplomacy, messages, auction, betting, tournament 등이 있습니다.
|
||||
- mutation: `inputEventBoundary.ts`가 idempotency와 PostgreSQL 작업 경계를 만듭니다.
|
||||
- realtime: SSE와 Redis 알림은 commit 이후 화면 갱신 신호입니다.
|
||||
|
||||
### game-engine
|
||||
|
||||
`app/game-engine/src/index.ts`가 daemon entry point이고 `src/turn/turnDaemon.ts`가 profile resource,
|
||||
world snapshot, command registry, calendar handler, persistence hook과 lease를 조립합니다.
|
||||
|
||||
한 daemon owner가 `TurnDaemonLifecycle`을 통해 정해진 tick과 durable inbox를 처리합니다. 실제 장수·도시·국가
|
||||
상태는 `InMemoryTurnWorld`에서 계산하고, 성공한 작업만 `databaseHooks.ts`를 통해 PostgreSQL에 flush합니다.
|
||||
lease/fencing은 오래된 daemon owner의 commit을 막습니다.
|
||||
|
||||
## 공유 package
|
||||
|
||||
| package | 책임 | 넣지 말아야 할 것 |
|
||||
| ------------------------ | ------------------------------------------------------------------------------- | ---------------------------------- |
|
||||
| `packages/common` | 공통 type, 직렬화, `LiteHashDRBG`, `RandUtil`, session/sanction 유틸리티 | profile DB orchestration |
|
||||
| `packages/logic` | entity, constraint, command, battle, trigger, scenario parsing 같은 도메인 계산 | HTTP·Vue·Prisma transaction 소유권 |
|
||||
| `packages/infra` | Prisma client, PostgreSQL·Redis connector, log와 turn-engine DB adapter | 게임 규칙 결정 |
|
||||
| `packages/tools-scripts` | resource schema 생성·검증 | 런타임 요청 처리 |
|
||||
|
||||
## 데이터와 구성
|
||||
|
||||
- `packages/infra/prisma/schema.gateway.prisma`: 계정·profile·operation 같은 gateway 모델
|
||||
- `packages/infra/prisma/schema.game.prisma`: profile별 world·general·city·nation·turn·event·log 모델
|
||||
- `resources/scenario`: 시작 연도, 상수, 월간 event 등 scenario 정의
|
||||
- `resources/map`, `resources/unitset`: 지형과 병종 정의
|
||||
- `resources/turn-commands`: profile별 허용 명령 목록
|
||||
|
||||
새 persistence field는 schema와 새 migration만으로 끝나지 않습니다. domain type, loader, in-memory dirty
|
||||
tracking, transaction flush, reload 검증까지 연결해야 합니다.
|
||||
|
||||
## 인증·권한 모델
|
||||
|
||||
Gateway session은 사용자 identity를, game token은 profile과 게임 역할을 전달합니다. game API는 session으로
|
||||
내 장수를 조회한 뒤 그 장수의 국가·직책·sanction과 대상 resource의 관계를 판단합니다. 공개 endpoint도
|
||||
비공개 국가 정보, 타 사용자 archive, 비밀 명령을 DTO에서 제거해야 합니다.
|
||||
|
||||
권한 변경을 검증할 때는 무인증, 일반 사용자, 본인, 같은 국가, 다른 국가, NPC, 직책 보유자, sanction
|
||||
적용자를 필요한 범위에서 나눕니다. 거부된 mutation은 world와 queue에 side effect를 남기지 않아야 합니다.
|
||||
Reference in New Issue
Block a user