독자 수준별 구조 문서와 게이머 입문·전술 안내를 재정비한다
This commit is contained in:
@@ -5,6 +5,13 @@
|
|||||||
비교하고, 명시적인 신규 기능·밸런스·UX는 Core 계약으로 검증합니다.
|
비교하고, 명시적인 신규 기능·밸런스·UX는 Core 계약으로 검증합니다.
|
||||||
작업 규칙과 분야별 지침의 시작점은 [AGENTS.md](AGENTS.md)입니다.
|
작업 규칙과 분야별 지침의 시작점은 [AGENTS.md](AGENTS.md)입니다.
|
||||||
|
|
||||||
|
## 독자별 시작점
|
||||||
|
|
||||||
|
- 게임이 처음인 플레이어: [삼국지 모의전투 첫 안내](docs/user/index.md)
|
||||||
|
- 프로그래밍 기초만 아는 독자: [화면·서버·저장부터 배우기](docs/developer/first-steps.md)
|
||||||
|
- 개발 경험이 있는 독자: [요청·실행·운영의 구조 읽기](docs/developer/system-walkthrough.md)
|
||||||
|
- 전체 문서 탐색: [핸드북](docs/index.md), [게시판 용어 사전](docs/user/glossary.md)
|
||||||
|
|
||||||
## 저장소 구성
|
## 저장소 구성
|
||||||
|
|
||||||
| 경로 | 책임 |
|
| 경로 | 책임 |
|
||||||
|
|||||||
@@ -17,11 +17,30 @@ export default defineConfig({
|
|||||||
{ text: '릴리스 운영', link: '/release-operations' },
|
{ text: '릴리스 운영', link: '/release-operations' },
|
||||||
],
|
],
|
||||||
sidebar: {
|
sidebar: {
|
||||||
|
'/architecture/': [
|
||||||
|
{
|
||||||
|
text: '구조와 학습 경로',
|
||||||
|
items: [
|
||||||
|
{ text: '독자별 읽기 순서', link: '/developer/' },
|
||||||
|
{ text: '아키텍처 개요', link: '/architecture/overview' },
|
||||||
|
{ text: '런타임', link: '/architecture/runtime' },
|
||||||
|
{ text: '패키지 경계', link: '/architecture/package-boundaries' },
|
||||||
|
{ text: '요청·턴·저장', link: '/developer/request-turn-persistence' },
|
||||||
|
{ text: '게임 시계', link: '/architecture/game-clock' },
|
||||||
|
{ text: '시나리오 합성', link: '/architecture/scenario-composition' },
|
||||||
|
{ text: '행동 모듈', link: '/architecture/action-module-protocol' },
|
||||||
|
{ text: '실시간 변경 알림', link: '/architecture/realtime-change-journal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
'/developer/': [
|
'/developer/': [
|
||||||
{
|
{
|
||||||
text: '개발자 핸드북',
|
text: '개발자 핸드북',
|
||||||
items: [
|
items: [
|
||||||
{ text: '시작하기', link: '/developer/' },
|
{ text: '독자별 읽기 순서', link: '/developer/' },
|
||||||
|
{ text: '기초 구조 안내', link: '/developer/first-steps' },
|
||||||
|
{ text: '경험자를 위한 시스템 읽기', link: '/developer/system-walkthrough' },
|
||||||
|
{ text: '구조 문서 찾아보기', link: '/developer/reference-map' },
|
||||||
{ text: '아키텍처 개요', link: '/architecture/overview' },
|
{ text: '아키텍처 개요', link: '/architecture/overview' },
|
||||||
{ text: '런타임 아키텍처', link: '/architecture/runtime' },
|
{ text: '런타임 아키텍처', link: '/architecture/runtime' },
|
||||||
{ text: '관리자 콘솔', link: '/admin-console' },
|
{ text: '관리자 콘솔', link: '/admin-console' },
|
||||||
@@ -37,7 +56,13 @@ export default defineConfig({
|
|||||||
text: '플레이어 가이드',
|
text: '플레이어 가이드',
|
||||||
items: [
|
items: [
|
||||||
{ text: '시작하기', link: '/user/' },
|
{ text: '시작하기', link: '/user/' },
|
||||||
|
{ text: '장수와 도시, 내정', link: '/user/general-and-city' },
|
||||||
{ text: '시간과 턴', link: '/user/time-and-turns' },
|
{ text: '시간과 턴', link: '/user/time-and-turns' },
|
||||||
|
{ text: '전쟁과 예턴 조합', link: '/user/war-and-orders' },
|
||||||
|
{ text: '보급·정찰·땅따', link: '/user/map-and-supply' },
|
||||||
|
{ text: '국가 재정과 외교', link: '/user/economy-and-diplomacy' },
|
||||||
|
{ text: '용어 사전', link: '/user/glossary' },
|
||||||
|
{ text: '자료와 확인 범위', link: '/user/sources' },
|
||||||
{ text: '커맨드와 실행 시기', link: '/user/commands-and-timing' },
|
{ text: '커맨드와 실행 시기', link: '/user/commands-and-timing' },
|
||||||
{ text: '커맨드 전체 목록', link: '/user/command-catalog.generated' },
|
{ text: '커맨드 전체 목록', link: '/user/command-catalog.generated' },
|
||||||
{ text: '국가 운영과 주요 기능', link: '/user/nation-and-features' },
|
{ text: '국가 운영과 주요 기능', link: '/user/nation-and-features' },
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
# 장수 행동 모듈 프로토콜
|
# 장수 행동 모듈 프로토콜
|
||||||
|
|
||||||
`packages/logic/src/actionModules/`는 core2026 실행 경계를 타입으로
|
명령 하나의 효과는 장수의 특기·병종·장비 등 여러 요소를 거쳐 계산됩니다.
|
||||||
표현합니다. 계산
|
**행동 모듈**은 이 추가 규칙들을 조립하는 단위입니다. 먼저
|
||||||
hook, 우선순위 trigger, 의미 이벤트는 서로 다른 실행 계약입니다.
|
[도메인과 조립](../developer/domain-and-classes.md)을 읽으면 아래 타입 이름을 따라가기 쉽습니다.
|
||||||
|
|
||||||
|
`packages/logic/src/actionModules/`는 세 종류의 실행 경계를 구분합니다.
|
||||||
|
**fold**는 앞의 계산 결과를 다음 계산에 넘기는 순차 처리, **trigger**는 정한 조건과
|
||||||
|
우선순위로 발동하는 효과, **의미 이벤트**는 도시 점령처럼 이름 붙인 게임 사건입니다.
|
||||||
|
예를 들어 값을 두 배로 한 뒤 10을 더하는 것과 10을 더한 뒤 두 배로 하는 것은
|
||||||
|
다릅니다. 아래 조립 순서를 보존하는 이유입니다.
|
||||||
|
|
||||||
## 세 가지 실행 계약
|
## 세 가지 실행 계약
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# API input-event 재실행·복구 계약
|
# API input-event 재실행·복구 계약
|
||||||
|
|
||||||
|
요청을 처리했지만 응답을 받지 못한 사용자가 다시 시도할 때, 상태를 두 번 바꾸지
|
||||||
|
않으면서 원래 결과를 돌려주기 위한 계약입니다. 여기서 **replay**는 저장한 API
|
||||||
|
응답의 재사용이며 NPC 판단이나 게임 전체의 재실행과는 다릅니다.
|
||||||
|
전체 흐름은 [요청·턴·저장](../developer/request-turn-persistence.md)에 있습니다.
|
||||||
|
|
||||||
## 목적과 범위
|
## 목적과 범위
|
||||||
|
|
||||||
game-api mutation은 HTTP `Idempotency-Key`를 profile·인증 actor와 함께 scope한 base ID,
|
game-api mutation은 HTTP `Idempotency-Key`를 profile·인증 actor와 함께 scope한 base ID,
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# 전투 시뮬레이터 브라우저 실행 경계
|
# 전투 시뮬레이터 브라우저 실행 경계
|
||||||
|
|
||||||
|
**Web Worker**는 브라우저 화면과 별도로 계산을 실행하는 기능입니다. 긴 전투 반복
|
||||||
|
계산으로 화면이 멎지 않도록 사용합니다. 이 문서는 시뮬레이션의 경계이며 실제
|
||||||
|
게임 턴의 전투를 브라우저가 확정한다는 뜻이 아닙니다. 실제 상태 저장은
|
||||||
|
[요청·턴·저장](../developer/request-turn-persistence.md)의 엔진 경계를 따릅니다.
|
||||||
|
|
||||||
## 요청과 권위 데이터
|
## 요청과 권위 데이터
|
||||||
|
|
||||||
`BattleSimulatorView.vue`는 화면을 열 때 `battle.getSimulatorContext`에서 form
|
`BattleSimulatorView.vue`는 화면을 열 때 `battle.getSimulatorContext`에서 form
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# game-api 턴 데몬 procedure/transaction inventory
|
# game-api 턴 데몬 procedure/transaction inventory
|
||||||
|
|
||||||
|
이 문서는 당시 조사한 API와 저장 책임의 검토 목록입니다. 아래 route 개수와 줄 번호는
|
||||||
|
조사 기준선의 탐색 자료이며, 현재 전체 개수·위치는 router와 관련 검사로 다시
|
||||||
|
확인합니다. 처음 읽을 때는 [요청·턴·저장](../developer/request-turn-persistence.md)을
|
||||||
|
먼저 읽고 API transaction과 ENGINE transaction의 차이를 익히세요.
|
||||||
|
|
||||||
## 범위와 판정 기준
|
## 범위와 판정 기준
|
||||||
|
|
||||||
`app/game-api/src/router/**`의 mutation에서 직접 또는 router 전용 helper를 거쳐
|
`app/game-api/src/router/**`의 mutation에서 직접 또는 router 전용 helper를 거쳐
|
||||||
@@ -36,25 +41,25 @@ selection-pool create/reselect는 client request ID가 있을 때
|
|||||||
|
|
||||||
## ENGINE procedure로 전환된 route
|
## ENGINE procedure로 전환된 route
|
||||||
|
|
||||||
| route | 현재 procedure / API-side 작업 | ENGINE 소유 근거 |
|
| route | 현재 procedure / API-side 작업 | ENGINE 소유 근거 |
|
||||||
| --- | --- | --- |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `general.vacation`, `general.setMySetting`, `general.dropItem` | `engineAuthedProcedure`/`accessEngineAuthedInputProcedure`; session-owned general 조회만 수행 (`app/game-api/src/router/general/index.ts:767-815`) | ENGINE이 general 존재, 현재 설정과 item 보유를 다시 검증하고 변경 (`app/game-engine/src/turn/worldCommandHandler.ts:1712-1841`) |
|
| `general.vacation`, `general.setMySetting`, `general.dropItem` | `engineAuthedProcedure`/`accessEngineAuthedInputProcedure`; session-owned general 조회만 수행 (`app/game-api/src/router/general/index.ts:767-815`) | ENGINE이 general 존재, 현재 설정과 item 보유를 다시 검증하고 변경 (`app/game-engine/src/turn/worldCommandHandler.ts:1712-1841`) |
|
||||||
| `nation.appoint`, `nation.changePermission`, `nation.kick` | `engineAuthedProcedure`; session actor 조회만 수행 (`app/game-api/src/router/nation/endpoints/appoint.ts:7-32`, `changePermission.ts:7-35`, `kick.ts:7-24`) | ENGINE이 actor 직위, 국가, 대상/도시를 다시 검증 (`app/game-engine/src/turn/worldCommandHandler.ts:1894-2291`) |
|
| `nation.appoint`, `nation.changePermission`, `nation.kick` | `engineAuthedProcedure`; session actor 조회만 수행 (`app/game-api/src/router/nation/endpoints/appoint.ts:7-32`, `changePermission.ts:7-35`, `kick.ts:7-24`) | ENGINE이 actor 직위, 국가, 대상/도시를 다시 검증 (`app/game-engine/src/turn/worldCommandHandler.ts:1894-2291`) |
|
||||||
| `troop.create`, `troop.join`, `troop.exit`, `troop.kick`, `troop.rename` | `engineAuthedProcedure`; actor 및 조기 권한/대상 조회, API DB write 없음 (`app/game-api/src/router/troop/index.ts:429-577`) | membership/leader/nation/name 검증과 mutation을 ENGINE이 소유 (`app/game-engine/src/turn/worldCommandHandler.ts:1131-1430`) |
|
| `troop.create`, `troop.join`, `troop.exit`, `troop.kick`, `troop.rename` | `engineAuthedProcedure`; actor 및 조기 권한/대상 조회, API DB write 없음 (`app/game-api/src/router/troop/index.ts:429-577`) | membership/leader/nation/name 검증과 mutation을 ENGINE이 소유 (`app/game-engine/src/turn/worldCommandHandler.ts:1131-1430`) |
|
||||||
| `auction.openBuyRice`, `auction.openSellRice`, `auction.openUnique`, `auction.bidBuyRice`, `auction.bidSellRice`, `auction.bidUnique` | `engineAuthedProcedure`; auction/general/world 조기 validation과 commit 뒤 Redis timer index 갱신 (`app/game-api/src/router/auction/index.ts:337-662`) | open/bid DB mutation과 경합/resource 재검증은 ENGINE transaction (`app/game-engine/src/turn/worldCommandHandler.ts:1859-1885`, `app/game-engine/src/auction/bidder.ts`). Redis zset은 durable auction row에서 재구성하는 scheduler index다. |
|
| `auction.openBuyRice`, `auction.openSellRice`, `auction.openUnique`, `auction.bidBuyRice`, `auction.bidSellRice`, `auction.bidUnique` | `engineAuthedProcedure`; auction/general/world 조기 validation과 commit 뒤 Redis timer index 갱신 (`app/game-api/src/router/auction/index.ts:337-662`) | open/bid DB mutation과 경합/resource 재검증은 ENGINE transaction (`app/game-engine/src/turn/worldCommandHandler.ts:1859-1885`, `app/game-engine/src/auction/bidder.ts`). Redis zset은 durable auction row에서 재구성하는 scheduler index다. |
|
||||||
| `inherit.openUniqueAuction` | `engineAuthedProcedure`; world/general/minimum bid 조기 validation (`app/game-api/src/router/inherit/index.ts:389-428`) | 공통 `auctionOpen` ENGINE handler가 mutation을 소유하고 API는 Redis timer index만 갱신 (`app/game-api/src/auction/open.ts:22-51`) |
|
| `inherit.openUniqueAuction` | `engineAuthedProcedure`; world/general/minimum bid 조기 validation (`app/game-api/src/router/inherit/index.ts:389-428`) | 공통 `auctionOpen` ENGINE handler가 mutation을 소유하고 API는 Redis timer index만 갱신 (`app/game-api/src/auction/open.ts:22-51`) |
|
||||||
| `join.getSelectionPool` | `engineAuthedProcedure`; actor와 accepted game time만 전달 (`app/game-api/src/router/join/index.ts:390-406`) | `selectPoolReserve` ENGINE handler가 world/DB 상태에서 예약을 재검증하고 저장 (`app/game-engine/src/turn/worldCommandHandler.ts:401-437`) |
|
| `join.getSelectionPool` | `engineAuthedProcedure`; actor와 accepted game time만 전달 (`app/game-api/src/router/join/index.ts:390-406`) | `selectPoolReserve` ENGINE handler가 world/DB 상태에서 예약을 재검증하고 저장 (`app/game-engine/src/turn/worldCommandHandler.ts:401-437`) |
|
||||||
| `inherit.buyHiddenBuff`, `inherit.setNextSpecialWar`, `inherit.resetSpecialWar`, `inherit.resetTurnTime`, `inherit.resetStat`, `inherit.buyRandomUnique`, `inherit.checkOwner` | 모두 `engineAuthedProcedure`; 인증 user ID와 action 입력만 전달 (`app/game-api/src/router/inherit/index.ts:107-124`, `:301-388`, `:429-445`) | `inheritanceAction` ENGINE handler가 general/user 소유권, 통일 상태, 잔액, 대상, RNG, general patch, inheritance point/log/message를 한 transaction에서 처리 (`app/game-engine/src/turn/worldCommandHandler.ts:807-818`, `app/game-engine/src/turn/inheritanceActionService.ts:313-669`) |
|
| `inherit.buyHiddenBuff`, `inherit.setNextSpecialWar`, `inherit.resetSpecialWar`, `inherit.resetTurnTime`, `inherit.resetStat`, `inherit.buyRandomUnique`, `inherit.checkOwner` | 모두 `engineAuthedProcedure`; 인증 user ID와 action 입력만 전달 (`app/game-api/src/router/inherit/index.ts:107-124`, `:301-388`, `:429-445`) | `inheritanceAction` ENGINE handler가 general/user 소유권, 통일 상태, 잔액, 대상, RNG, general patch, inheritance point/log/message를 한 transaction에서 처리 (`app/game-engine/src/turn/worldCommandHandler.ts:807-818`, `app/game-engine/src/turn/inheritanceActionService.ts:313-669`) |
|
||||||
| `nation.setNotice`, `nation.setScoutMsg`, `nation.setSecretLimit`, `nation.setRate`, `nation.setBlockWar`, `nation.setBill`, `nation.setBlockScout` | `engineAuthedProcedure`; API는 인증 actor, 현재 국가와 조기 권한만 읽고 semantic mutation을 전달 (`app/game-api/src/router/nation/endpoints/setNotice.ts`, `setScoutMsg.ts`, `setSecretLimit.ts`, `setRate.ts`, `setBlockWar.ts`, `setBill.ts`, `setBlockScout.ts`) | `setNationSetting`이 실행 시점 owner/nation/직책·permission을 다시 검사한다. 전쟁 설정 잔여 횟수 차감과 임관 잠금 검사를 현재 ENGINE state에서 수행하고, 공지는 logical game time과 author snapshot을 국가 meta와 같은 transaction에 저장한다 (`app/game-engine/src/turn/nationSettingMutation.ts`, `worldCommandHandler.ts`). |
|
| `nation.setNotice`, `nation.setScoutMsg`, `nation.setSecretLimit`, `nation.setRate`, `nation.setBlockWar`, `nation.setBill`, `nation.setBlockScout` | `engineAuthedProcedure`; API는 인증 actor, 현재 국가와 조기 권한만 읽고 semantic mutation을 전달 (`app/game-api/src/router/nation/endpoints/setNotice.ts`, `setScoutMsg.ts`, `setSecretLimit.ts`, `setRate.ts`, `setBlockWar.ts`, `setBill.ts`, `setBlockScout.ts`) | `setNationSetting`이 실행 시점 owner/nation/직책·permission을 다시 검사한다. 전쟁 설정 잔여 횟수 차감과 임관 잠금 검사를 현재 ENGINE state에서 수행하고, 공지는 logical game time과 author snapshot을 국가 meta와 같은 transaction에 저장한다 (`app/game-engine/src/turn/nationSettingMutation.ts`, `worldCommandHandler.ts`). |
|
||||||
| `npc.setNationPolicy`, `npc.setNationPriority`, `npc.setGeneralPriority` | `accessEngineAuthedInputProcedure`; API는 현재 화면값과 unit-set 기반 입력 보조만 수행하고 actor-bound semantic delta와 명시적 nullable revision을 전달 (`app/game-api/src/router/npc/index.ts`) | `setNpcPolicy`가 실행 시점 owner/nation/permission, strict CAS, 현재 troop/city membership, priority와 numeric policy를 검증하고 logical setter snapshot과 delta만 반영한다 (`app/game-engine/src/turn/npcPolicyMutation.ts`, `worldCommandHandler.ts`). |
|
| `npc.setNationPolicy`, `npc.setNationPriority`, `npc.setGeneralPriority` | `accessEngineAuthedInputProcedure`; API는 현재 화면값과 unit-set 기반 입력 보조만 수행하고 actor-bound semantic delta와 명시적 nullable revision을 전달 (`app/game-api/src/router/npc/index.ts`) | `setNpcPolicy`가 실행 시점 owner/nation/permission, strict CAS, 현재 troop/city membership, priority와 numeric policy를 검증하고 logical setter snapshot과 delta만 반영한다 (`app/game-engine/src/turn/npcPolicyMutation.ts`, `worldCommandHandler.ts`). |
|
||||||
|
|
||||||
이 표의 **36개 route**와 아래 설문 전환 **1개 route**를 합쳐 **37개 route**다.
|
이 표의 **36개 route**와 아래 설문 전환 **1개 route**를 합쳐 **37개 route**다.
|
||||||
|
|
||||||
## 혼합 또는 validation 이관이 먼저 필요한 route
|
## 혼합 또는 validation 이관이 먼저 필요한 route
|
||||||
|
|
||||||
| route | 현재 procedure / outer transaction | 보류 근거 |
|
| route | 현재 procedure / outer transaction | 보류 근거 |
|
||||||
| --- | --- | --- |
|
| ---------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `messages.respond` | `authedProcedure`, action별 분기 (`app/game-api/src/router/messages/index.ts:318-372`) | `scout`/`raiseInvader`는 `messageRespond` ENGINE command가 처리하지만 `noAggression`/`cancelNA`/`stopWar`는 API transaction의 `respondToDiplomaticMessage` 경로가 처리한다. route 전체를 ENGINE-owned로 보지 않는다. |
|
| `messages.respond` | `authedProcedure`, action별 분기 (`app/game-api/src/router/messages/index.ts:318-372`) | `scout`/`raiseInvader`는 `messageRespond` ENGINE command가 처리하지만 `noAggression`/`cancelNA`/`stopWar`는 API transaction의 `respondToDiplomaticMessage` 경로가 처리한다. route 전체를 ENGINE-owned로 보지 않는다. |
|
||||||
| `tournament.join`, `tournament.placeBet` | `engineAuthedProcedure`, API outer 없음 (`app/game-api/src/router/tournament/index.ts`) | PostgreSQL ENGINE resource/meta 명령과 Redis-owned participants/bets를 결합하고 실패 시 보상 ENGINE 명령을 보낸다. API clock advisory lock을 잡은 채 child ENGINE transaction을 기다리지 않으며, command에는 HTTP request-scoped step ID를 전달한다. 여전히 하나의 DB transaction이 아니므로 durable saga/reconciliation이 필요하다. |
|
| `tournament.join`, `tournament.placeBet` | `engineAuthedProcedure`, API outer 없음 (`app/game-api/src/router/tournament/index.ts`) | PostgreSQL ENGINE resource/meta 명령과 Redis-owned participants/bets를 결합하고 실패 시 보상 ENGINE 명령을 보낸다. API clock advisory lock을 잡은 채 child ENGINE transaction을 기다리지 않으며, command에는 HTTP request-scoped step ID를 전달한다. 여전히 하나의 DB transaction이 아니므로 durable saga/reconciliation이 필요하다. |
|
||||||
|
|
||||||
합계 **3개 route**다.
|
합계 **3개 route**다.
|
||||||
@@ -77,11 +82,11 @@ mutation lock → API input-event/DB clock fence → 상태 변경 → commit
|
|||||||
|
|
||||||
## 기존에 API outer transaction이 없던 ENGINE route
|
## 기존에 API outer transaction이 없던 ENGINE route
|
||||||
|
|
||||||
| route | 근거 |
|
| route | 근거 |
|
||||||
| --- | --- |
|
| ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `general.adjustIcon` | `engineAuthedProcedure`; helper가 stable account-icon request ID로 ENGINE command를 보냄 (`app/game-api/src/router/general/index.ts:694-720`, `app/game-api/src/services/accountIconSync.ts:40-75`) |
|
| `general.adjustIcon` | `engineAuthedProcedure`; helper가 stable account-icon request ID로 ENGINE command를 보냄 (`app/game-api/src/router/general/index.ts:694-720`, `app/game-api/src/services/accountIconSync.ts:40-75`) |
|
||||||
| `general.ensureDieOnPrestartStatus`, `general.dieOnPrestart`, `general.buildNationCandidate`, `general.instantRetreat` | `accessEngineAuthedProcedure`/`accessEngineAuthedInputProcedure`; user/general 조회는 outer transaction 밖이고 command마다 stable request ID가 있음 (`app/game-api/src/router/general/index.ts:104-144`, `:722-766`) |
|
| `general.ensureDieOnPrestartStatus`, `general.dieOnPrestart`, `general.buildNationCandidate`, `general.instantRetreat` | `accessEngineAuthedProcedure`/`accessEngineAuthedInputProcedure`; user/general 조회는 outer transaction 밖이고 command마다 stable request ID가 있음 (`app/game-api/src/router/general/index.ts:104-144`, `:722-766`) |
|
||||||
| `join.selectPoolGeneral`, `join.reselectPoolGeneral`, `join.createGeneral`, `join.possessGeneral` | `engineAuthedProcedure`; client request ID가 있으면 user-scoped durable identity를 사용 (`app/game-api/src/router/join/index.ts:407-564`, `:608-642`) |
|
| `join.selectPoolGeneral`, `join.reselectPoolGeneral`, `join.createGeneral`, `join.possessGeneral` | `engineAuthedProcedure`; client request ID가 있으면 user-scoped durable identity를 사용 (`app/game-api/src/router/join/index.ts:407-564`, `:608-642`) |
|
||||||
|
|
||||||
합계 **9개 route**다.
|
합계 **9개 route**다.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# Game clock reconciliation
|
# Game clock reconciliation
|
||||||
|
|
||||||
|
게임을 멈춘 뒤 다시 시작할 때 여러 기능의 남은 시간과 순서를 함께 맞추는 계약입니다.
|
||||||
|
**재정렬(reconciliation)**은 화면 날짜만 고치는 일이 아니라 DB 일정·worker 기한·
|
||||||
|
메모리 상태를 같은 세대로 전환하는 과정입니다. 개념은 [게임 시계](./game-clock.md),
|
||||||
|
실패 대응은 [복구 안내](../developer/game-clock-recovery.md)를 먼저 읽으세요.
|
||||||
|
아래 `EXACT`는 역사적 정책이고 현재 maintenance·장애 복구는 `RECOVER_TURNS`입니다.
|
||||||
|
|
||||||
## Product contract
|
## Product contract
|
||||||
|
|
||||||
Gameplay time is an integer `GameTick`; one turn is permanently `36,000,000`
|
Gameplay time is an integer `GameTick`; one turn is permanently `36,000,000`
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# 게임 시계
|
# 게임 시계
|
||||||
|
|
||||||
|
예약된 행동까지 얼마나 남았는지와 로그인·소유권이 현실에서 언제 만료되는지는
|
||||||
|
다른 질문입니다. 그래서 게임을 멈출 때 모든 시간을 한꺼번에 멈추지 않습니다.
|
||||||
|
이 문서는 그 구분을 설명하고, 상세 필드 목록은 아래 시간 도메인 문서로 연결합니다.
|
||||||
|
|
||||||
시간 규칙은 `GAME_TIME`, `WALL_TIME`, `MONOTONIC_ELAPSED_TIME`로
|
시간 규칙은 `GAME_TIME`, `WALL_TIME`, `MONOTONIC_ELAPSED_TIME`로
|
||||||
나뉩니다. 게임 진행의 권위는 `world_state.clock_tick`, 영속 wall
|
나뉩니다. 게임 진행의 권위는 `world_state.clock_tick`, 영속 wall
|
||||||
판정의 권위는 PostgreSQL UTC 시계, 프로세스 내부 경과시간의 권위는
|
판정의 권위는 PostgreSQL UTC 시계, 프로세스 내부 경과시간의 권위는
|
||||||
@@ -67,14 +71,20 @@ column을 9시간 미래로 쓰지 않습니다.
|
|||||||
|
|
||||||
## 중단 후 재개
|
## 중단 후 재개
|
||||||
|
|
||||||
realtime daemon은 재개할 때 Ref `checkDelay()`와 같은 한도를 적용합니다.
|
현재 maintenance·장애 복구는 `RECOVER_TURNS` 정책을 사용합니다. 전체 중단이
|
||||||
밀린 완전 턴 수가 턴 간격 20분 이상이면 1턴, 10분 이상이면 3턴, 그보다
|
현실 시간 10분 이내면 기존 실행 순서와 budget으로 밀린 턴을 처리합니다.
|
||||||
짧으면 6턴을 초과할 때 장기 중단으로 봅니다. 한도 이내의 짧은 중단은 턴을
|
그보다 길면 밀린 시간 중 완전한 12턴 묶음을 일정 이동으로 건너뛰고, 나머지
|
||||||
순서대로 실행해 따라잡고, 한도를 넘으면 밀린 완전 턴만큼 `last_turn_tick`,
|
지연은 월 경계에 맞춘 복구 구간에서 두 배 속도로 실행해 정상 시간표에 합류합니다.
|
||||||
전 장수의 `turn_tick`, 미완료 경매의 `close_tick`과 각각의 DateTime 투영값을
|
설정된 기본 턴 간격 자체를 절반으로 변경하지 않습니다.
|
||||||
한 transaction에서 옮깁니다. 이 보정은 명령이나 월 이벤트를 실행하지 않으므로
|
|
||||||
건너뛴 기간의 RNG를 소비하지 않습니다. 이미 처리 중 anchor 갱신으로 표시
|
건너뛴 구간에는 게임 행동·월 이벤트를 실행하지 않으며, 따라잡는 구간에는 실제
|
||||||
시각이 늦어진 상태도 현재 wall time에 맞추되 game tick은 되감지 않습니다.
|
행동을 순서대로 실행합니다. DB에 복구 구간을 보존하므로 프로세스 교체가 새 복구를
|
||||||
|
중복 적용해서는 안 됩니다. 가오픈·통일 대기와 명시적 정지는 별도 상태 검사를 거칩니다.
|
||||||
|
|
||||||
|
기준 구현은 `packages/common/src/time/TurnRecovery.ts`,
|
||||||
|
`app/game-engine/src/turn/prepareRealtimeRecovery.ts`, `clockReconciliation.ts`입니다.
|
||||||
|
계산식·참여자·Redis 투영까지의 절차는 [시계 재정렬](./game-clock-reconciliation.md),
|
||||||
|
실패 후 관찰과 재시도는 [복구 안내](../developer/game-clock-recovery.md)에 있습니다.
|
||||||
|
|
||||||
운영자가 명시적으로 일정을 지연하거나 가속하려면 Gateway 작업을 사용합니다.
|
운영자가 명시적으로 일정을 지연하거나 가속하려면 Gateway 작업을 사용합니다.
|
||||||
이 작업은 `clock_base_time`과 DateTime 투영값을 같이 이동하고, game tick 및
|
이 작업은 `clock_base_time`과 DateTime 투영값을 같이 이동하고, game tick 및
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# core2026 아키텍처
|
# core2026 아키텍처
|
||||||
|
|
||||||
|
이 문서는 프로그램들의 책임을 한눈에 보는 지도입니다. 웹 서버가 처음이라면
|
||||||
|
[기초 구조 안내](../developer/first-steps.md), 개발 경험이 있다면
|
||||||
|
[시스템 읽기](../developer/system-walkthrough.md)를 먼저 읽어도 좋습니다.
|
||||||
|
세부 운영 절차는 [런타임](./runtime.md)에 있습니다.
|
||||||
|
|
||||||
## 시스템 경계
|
## 시스템 경계
|
||||||
|
|
||||||
`core2026`은 계정·profile 운영을 담당하는 gateway와 profile별 게임 런타임을
|
`core2026`은 계정·profile 운영을 담당하는 gateway와 profile별 게임 런타임을
|
||||||
@@ -35,6 +40,10 @@ PostgreSQL 상태와 결합해 서버에서 결정합니다.
|
|||||||
- `app/gateway-api/src/orchestrator/`는 DB operation queue, commit별 worktree,
|
- `app/gateway-api/src/orchestrator/`는 DB operation queue, commit별 worktree,
|
||||||
build, PM2 process와 예약 상태를 조정합니다.
|
build, PM2 process와 예약 상태를 조정합니다.
|
||||||
|
|
||||||
|
Gateway 자체를 업데이트하는 `app/release-controller`는 Gateway 밖에서 실행합니다.
|
||||||
|
게임 profile 작업을 담당하는 orchestrator와 Gateway 전체 릴리스 담당을 나누어,
|
||||||
|
업데이트 대상 프로세스가 자신의 종료 이후 단계까지 직접 책임지지 않도록 합니다.
|
||||||
|
|
||||||
### Game
|
### Game
|
||||||
|
|
||||||
- `app/game-frontend`는 profile base path에서 실행하는 Vue SPA입니다.
|
- `app/game-frontend`는 profile base path에서 실행하는 Vue SPA입니다.
|
||||||
@@ -70,6 +79,14 @@ Command는 `GeneralActionDefinition` 또는 국가 command module로 args,
|
|||||||
constraint, turn metadata, 결과와 로그를 선언합니다. 런타임 context와
|
constraint, turn metadata, 결과와 로그를 선언합니다. 런타임 context와
|
||||||
persistence는 `app/game-engine`이 제공합니다.
|
persistence는 `app/game-engine`이 제공합니다.
|
||||||
|
|
||||||
|
### 자주 나오는 용어
|
||||||
|
|
||||||
|
- **registry**: 이름으로 사용할 명령·효과를 찾아 주는 등록 목록입니다.
|
||||||
|
- **loader**: 파일이나 DB 값을 읽어 실행에 필요한 형태로 만드는 코드입니다.
|
||||||
|
- **context**: 한 행동을 계산할 때 필요한 장수·도시·시각·난수 등의 입력 묶음입니다.
|
||||||
|
- **hook·trigger**: 정해진 계산 또는 사건 지점에 연결하는 추가 효과입니다.
|
||||||
|
- **persistence**: 재시작해도 남도록 저장하는 일입니다.
|
||||||
|
|
||||||
### `packages/infra`
|
### `packages/infra`
|
||||||
|
|
||||||
`prisma/gateway.prisma`와 `prisma/game.prisma`가 영속 schema의 기준입니다.
|
`prisma/gateway.prisma`와 `prisma/game.prisma`가 영속 schema의 기준입니다.
|
||||||
@@ -99,7 +116,9 @@ DB commit의 대체 조건으로 사용하지 않습니다.
|
|||||||
|
|
||||||
## 시나리오와 profile
|
## 시나리오와 profile
|
||||||
|
|
||||||
`profile`은 규칙·자산 계열이고 `scenario`는 게임 초기 데이터입니다.
|
운영 화면의 프로필은 독립적으로 실행·배포·진행 상태를 관리하는 게임 대상입니다.
|
||||||
|
설정에서 `profile`은 규칙·자산 계열을, `scenario`는 선택한 시나리오를 나타냅니다.
|
||||||
|
서로 다른 곳에서 쓰는 같은 이름을 하나의 값으로 혼동하지 마세요.
|
||||||
런타임 식별자는 `${profile}:${scenario}`입니다. Scenario JSON은
|
런타임 식별자는 `${profile}:${scenario}`입니다. Scenario JSON은
|
||||||
`resources/scenario`에서 합성되고 zod parser를 거쳐 seeder와 runtime에
|
`resources/scenario`에서 합성되고 zod parser를 거쳐 seeder와 runtime에
|
||||||
전달됩니다. map, unit set, turn-command profile도 resource loader를 통해
|
전달됩니다. map, unit set, turn-command profile도 resource loader를 통해
|
||||||
|
|||||||
@@ -2,21 +2,23 @@
|
|||||||
|
|
||||||
## 의존 방향
|
## 의존 방향
|
||||||
|
|
||||||
제품 소스의 의존 방향은 다음과 같습니다.
|
패키지는 함께 재사용할 코드의 묶음입니다. **의존 방향**은 어느 묶음이 다른
|
||||||
|
묶음의 코드를 가져오는지 뜻하며, 서버 사이의 네트워크 통신 방향과는 다릅니다.
|
||||||
|
아래는 `tools/check-package-boundaries.mjs`가 허용하는 workspace 의존입니다.
|
||||||
|
허용 목록은 모든 파일이 실제로 전부 import한다는 뜻은 아닙니다.
|
||||||
|
|
||||||
```text
|
| 가져오는 쪽 | 가져올 수 있는 workspace 코드 |
|
||||||
packages/common
|
| -------------------- | ---------------------------------------------------------- |
|
||||||
↑
|
| common | 다른 workspace package 없음 |
|
||||||
packages/logic ← packages/infra
|
| logic | common |
|
||||||
↑ ↑
|
| infra | common, logic |
|
||||||
└──── app/game-engine ────┐
|
| game-engine | common, logic, infra |
|
||||||
↑ │
|
| game-api·gateway-api | common, logic, infra, game-engine의 공개 subpath |
|
||||||
app/game-api app/gateway-api
|
| release-controller | gateway-api, infra |
|
||||||
↑ ↑
|
| game-frontend | common, logic의 브라우저용 값; game-api·gateway-api의 타입 |
|
||||||
game-frontend gateway-frontend
|
| gateway-frontend | common의 브라우저용 값; game-api·gateway-api의 타입 |
|
||||||
```
|
|
||||||
|
|
||||||
화살표의 시작점이 끝점을 import합니다. `packages/logic`은 DB, Redis, 파일,
|
`packages/logic`은 DB, Redis, 파일,
|
||||||
네트워크, 환경 변수와 stdout을 직접 사용하지 않습니다. 런타임 관찰이 필요한
|
네트워크, 환경 변수와 stdout을 직접 사용하지 않습니다. 런타임 관찰이 필요한
|
||||||
경우 `packages/logic/src/ports/`에 포트를 선언하고 app 계층에서 구현을
|
경우 `packages/logic/src/ports/`에 포트를 선언하고 app 계층에서 구현을
|
||||||
주입합니다. Prisma 생성 타입과 connector는 `packages/infra`가 소유하며,
|
주입합니다. Prisma 생성 타입과 connector는 `packages/infra`가 소유하며,
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
# 실시간 read-model change journal과 revision-first 조회 설계
|
# 실시간 read-model change journal과 revision-first 조회 설계
|
||||||
|
|
||||||
|
## 읽기 안내
|
||||||
|
|
||||||
|
**read model**은 화면 조회에 맞춰 구성한 데이터, **revision**은 변경 세대를 나타내는
|
||||||
|
번호, **journal**은 어떤 조회 결과가 바뀌었는지 남기는 기록입니다. 서버는 DB 저장을
|
||||||
|
확정한 뒤 필요한 화면에 변경을 알립니다. 브라우저는 알림 자체를 최종 상태로 삼지
|
||||||
|
않고 권한에 맞는 데이터를 다시 조회합니다.
|
||||||
|
|
||||||
|
이 문서는 설계 당시의 병목과 이후 구현·측정을 함께 보존한 상세 자료입니다.
|
||||||
|
아래의 “설계 출발점”은 개선 전 경로이며 현재 전체 호출 흐름으로 읽지 마세요.
|
||||||
|
구현 단계 표와 측정 결과도 해당 날짜·workload의 증거입니다. 운영 서버의 현재 수용량을
|
||||||
|
보장하지 않습니다. 먼저 [요청·턴·저장](../developer/request-turn-persistence.md)을 읽고,
|
||||||
|
변경 생산은 `app/game-engine/src/turn/databaseHooks.ts`, API 전달은
|
||||||
|
`app/game-api/src/realtime/`, 브라우저 반영은
|
||||||
|
`app/game-frontend/src/stores/mainDashboard.ts`에서 확인합니다.
|
||||||
|
|
||||||
## 목적
|
## 목적
|
||||||
|
|
||||||
메인 화면의 실시간 갱신이 실제 화면 변화가 없는 경우에도 viewer별 PostgreSQL
|
메인 화면의 실시간 갱신이 실제 화면 변화가 없는 경우에도 viewer별 PostgreSQL
|
||||||
@@ -19,9 +34,9 @@ read model을 다시 구성한 뒤 `unchanged`를 판정하는 비용을 제거
|
|||||||
추산이 아니라 아래 workload를 실제 PostgreSQL·Redis·HTTP/SSE 경계에서 실행한
|
추산이 아니라 아래 workload를 실제 PostgreSQL·Redis·HTTP/SSE 경계에서 실행한
|
||||||
결과로 판정한다.
|
결과로 판정한다.
|
||||||
|
|
||||||
## 현재 상태와 병목
|
## 설계 출발점: 개선 전 상태와 병목
|
||||||
|
|
||||||
현재 turn daemon은 `InMemoryTurnWorld`의 dirty general/city/nation 후보를 daemon
|
설계 출발점의 turn daemon은 `InMemoryTurnWorld`의 dirty general/city/nation 후보를 daemon
|
||||||
수명의 in-memory baseline과 비교한다. `databaseHooks`가 `content`, `map`,
|
수명의 in-memory baseline과 비교한다. `databaseHooks`가 `content`, `map`,
|
||||||
`contacts`, `frontStatus`, `lobby` canonical projection을 나누어 비교한 뒤
|
`contacts`, `frontStatus`, `lobby` canonical projection을 나누어 비교한 뒤
|
||||||
`RealtimeReadModelChanges`를 만든다. 이 단계는 dirty entity만 직렬화하며 일반
|
`RealtimeReadModelChanges`를 만든다. 이 단계는 dirty entity만 직렬화하며 일반
|
||||||
@@ -46,7 +61,7 @@ readModelInvalidated
|
|||||||
보통 소속 장수의 context-only 자동 갱신은 access gate를 포함해 약 13 SQL이고,
|
보통 소속 장수의 context-only 자동 갱신은 access gate를 포함해 약 13 SQL이고,
|
||||||
command table까지 포함하면 약 21 SQL이다. 300 viewer가 global 변화 burst를 1초
|
command table까지 포함하면 약 21 SQL이다. 300 viewer가 global 변화 burst를 1초
|
||||||
간격으로 받으면 bundle만 이론상 약 3,900~6,300 statement/s까지 커질 수 있다.
|
간격으로 받으면 bundle만 이론상 약 3,900~6,300 statement/s까지 커질 수 있다.
|
||||||
이는 실제 운영 측정값이 아니라 현재 호출 그래프의 상한식이며, 구현 뒤 실제
|
이는 실제 운영 측정값이 아니라 설계 당시 호출 그래프의 상한식이며, 구현 뒤 실제
|
||||||
statement rate로 대체한다.
|
statement rate로 대체한다.
|
||||||
|
|
||||||
## 보존할 계약
|
## 보존할 계약
|
||||||
@@ -162,29 +177,29 @@ statement 수와 row lock 시간을 제한한다. 없는 key의 revision은 0으
|
|||||||
|
|
||||||
초기 domain은 다음과 같다.
|
초기 domain은 다음과 같다.
|
||||||
|
|
||||||
| domain | entity ID | 의미 |
|
| domain | entity ID | 의미 |
|
||||||
| --- | ---: | --- |
|
| ------------------ | ---------: | ----------------------------------------------------------------------------------- |
|
||||||
| `general.content` | general ID | 현재 장수 context/command/board dependency |
|
| `general.content` | general ID | 현재 장수 context/command/board dependency |
|
||||||
| `city.content` | city ID | 현재 도시 context/command dependency |
|
| `city.content` | city ID | 현재 도시 context/command dependency |
|
||||||
| `nation.content` | nation ID | 현재 국가 context/command/board dependency |
|
| `nation.content` | nation ID | 현재 국가 context/command/board dependency |
|
||||||
| `dashboard.global` | 0 | 부대·예약턴 및 전체 general/city/nation aggregate를 포괄하는 source-only dependency |
|
| `dashboard.global` | 0 | 부대·예약턴 및 전체 general/city/nation aggregate를 포괄하는 source-only dependency |
|
||||||
| `world.content` | 0 | 연월, scenario/config/catalog 성격의 dependency |
|
| `world.content` | 0 | 연월, scenario/config/catalog 성격의 dependency |
|
||||||
| `map.world` | 0 | shared base map projection |
|
| `map.world` | 0 | shared base map projection |
|
||||||
| `map.general` | general ID | 현재 장수 이동처럼 actor별 map wake-up에 필요한 변화 |
|
| `map.general` | general ID | 현재 장수 이동처럼 actor별 map wake-up에 필요한 변화 |
|
||||||
| `records.general` | general ID | 개인 최근 기록 |
|
| `records.general` | general ID | 개인 최근 기록 |
|
||||||
| `records.global` | 0 | 장수 동향 |
|
| `records.global` | 0 | 장수 동향 |
|
||||||
| `records.history` | 0 | 중원 정세 |
|
| `records.history` | 0 | 중원 정세 |
|
||||||
| `front.general` | general ID | actor별 front status |
|
| `front.general` | general ID | actor별 front status |
|
||||||
| `front.nation` | nation ID | 국가 공지 등 viewer별 front status |
|
| `front.nation` | nation ID | 국가 공지 등 viewer별 front status |
|
||||||
| `front.global` | 0 | 설문과 global front status |
|
| `front.global` | 0 | 설문과 global front status |
|
||||||
| `access.general` | general ID | 접속 점수와 제한 상태, public fan-out 없음 |
|
| `access.general` | general ID | 접속 점수와 제한 상태, public fan-out 없음 |
|
||||||
| `lobby.world` | 0 | NPC/국가 수, 공용 lobby projection |
|
| `lobby.world` | 0 | NPC/국가 수, 공용 lobby projection |
|
||||||
| `lobby.general` | general ID | 본인 lobby에 보이는 이름·아이콘 projection |
|
| `lobby.general` | general ID | 본인 lobby에 보이는 이름·아이콘 projection |
|
||||||
| `contacts.world` | 0 | 장수 목록·외교 연락처 공용 projection |
|
| `contacts.world` | 0 | 장수 목록·외교 연락처 공용 projection |
|
||||||
| `reserved.general` | general ID | 장수 예약 명령 projection |
|
| `reserved.general` | general ID | 장수 예약 명령 projection |
|
||||||
| `messages.mailbox` | mailbox ID | commit된 메시지 mailbox wake-up; public SSE에는 ID 미노출 |
|
| `messages.mailbox` | mailbox ID | commit된 메시지 mailbox wake-up; public SSE에는 ID 미노출 |
|
||||||
| `tournament` | 0 | 토너먼트 stage/state |
|
| `tournament` | 0 | 토너먼트 stage/state |
|
||||||
| `betting` | 0 | 국가/토너먼트 베팅 목록·상태 |
|
| `betting` | 0 | 국가/토너먼트 베팅 목록·상태 |
|
||||||
|
|
||||||
projection 세부 ID 목록은 Redis wake-up의 viewer filtering에 사용하고, DB revision
|
projection 세부 ID 목록은 Redis wake-up의 viewer filtering에 사용하고, DB revision
|
||||||
row는 API가 작은 dependency vector를 읽는 authoritative 경계로 사용한다.
|
row는 API가 작은 dependency vector를 읽는 authoritative 경계로 사용한다.
|
||||||
@@ -376,12 +391,12 @@ BroadcastChannel 계약을 유지한다. 변경 ID set/boolean은 drop하지 않
|
|||||||
|
|
||||||
초기 cadence 목표는 다음과 같다.
|
초기 cadence 목표는 다음과 같다.
|
||||||
|
|
||||||
| 종류 | 최대 시작 빈도 | 이유 |
|
| 종류 | 최대 시작 빈도 | 이유 |
|
||||||
| --- | ---: | --- |
|
| ------------------------------ | -------------: | -------------------------------- |
|
||||||
| 자기 context/commands/board | 1초 1회 | 자기 명령 결과의 빠른 반영 |
|
| 자기 context/commands/board | 1초 1회 | 자기 명령 결과의 빠른 반영 |
|
||||||
| records/front status | 2초 1회 | global burst 합치기 |
|
| records/front status | 2초 1회 | global burst 합치기 |
|
||||||
| map/lobby/tournament/betting | 5초 1회 | 300 viewer의 shared fan-out 제한 |
|
| map/lobby/tournament/betting | 5초 1회 | 300 viewer의 shared fan-out 제한 |
|
||||||
| access-only non-limited 재확인 | 5초 1회 | 제한 DB gate fan-out 제한 |
|
| access-only non-limited 재확인 | 5초 1회 | 제한 DB gate fan-out 제한 |
|
||||||
|
|
||||||
manual refresh, visible 복귀와 realtime 재활성화는 cadence를 기다리지 않고 fresh
|
manual refresh, visible 복귀와 realtime 재활성화는 cadence를 기다리지 않고 fresh
|
||||||
snapshot을 한 번 읽는다. 숫자는 실제 혼합 부하 결과에 따라 조정하며, 부하만 낮추기
|
snapshot을 한 번 읽는다. 숫자는 실제 혼합 부하 결과에 따라 조정하며, 부하만 낮추기
|
||||||
@@ -444,12 +459,12 @@ visible 복귀는 leader가 fresh 3-slice snapshot 한 번을 읽어 pub/sub gap
|
|||||||
|
|
||||||
### 2026-08-17 구현 상태
|
### 2026-08-17 구현 상태
|
||||||
|
|
||||||
| Phase | 상태 | 현재 근거 |
|
| Phase | 상태 | 현재 근거 |
|
||||||
| --- | --- | --- |
|
| ----- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| A | 완료 | all-false access gate, frontend 강제 context 제거, Chromium realtime trace |
|
| A | 완료 | all-false access gate, frontend 강제 context 제거, Chromium realtime trace |
|
||||||
| B | 완료 | typed journal, PostgreSQL revision/outbox/meta, engine/API 원자 writer, retry dispatcher, 86 mutation inventory |
|
| B | 완료 | typed journal, PostgreSQL revision/outbox/meta, engine/API 원자 writer, retry dispatcher, 86 mutation inventory |
|
||||||
| C | 완료 | dashboard revision-first, auth/global dependency, durable map cache, 모든 tournament Redis writer 원자화, stage-only main realtime invalidation, coverage v1 activation/rollback integration |
|
| C | 완료 | dashboard revision-first, auth/global dependency, durable map cache, 모든 tournament Redis writer 원자화, stage-only main realtime invalidation, coverage v1 activation/rollback integration |
|
||||||
| D | 부분 완료 | E1 1,200장수 1개월 deterministic profile과 300 SSE/HTTP 짧은 calibration 완료. E2 actual daemon DB flush 및 30분 M1/R1은 미실행 |
|
| D | 부분 완료 | E1 1,200장수 1개월 deterministic profile과 300 SSE/HTTP 짧은 calibration 완료. E2 actual daemon DB flush 및 30분 M1/R1은 미실행 |
|
||||||
|
|
||||||
`부분 완료`는 capacity 합격을 뜻하지 않는다. 이 작업의 수용 추산은 아래 실제 짧은
|
`부분 완료`는 capacity 합격을 뜻하지 않는다. 이 작업의 수용 추산은 아래 실제 짧은
|
||||||
calibration과 E1 결과를 함께 사용하되, 장기 soak/daemon flush 경계는 별도 admission
|
calibration과 E1 결과를 함께 사용하되, 장기 soak/daemon flush 경계는 별도 admission
|
||||||
@@ -472,15 +487,15 @@ API와 process 경합을 제외하므로 production 수용 근거로 단독 사
|
|||||||
|
|
||||||
### workload
|
### workload
|
||||||
|
|
||||||
| ID | workload | 필수 관찰값 |
|
| ID | workload | 필수 관찰값 |
|
||||||
| --- | --- | --- |
|
| --- | --------------------------------------------------- | -------------------------------------------------------------------- |
|
||||||
| E1 | 5분 턴, 900 NPC 및 총 1,200장수 DB-free 고정 seed | turns/actions/s, command p95/p99, memory high-water, 최종 state hash |
|
| E1 | 5분 턴, 900 NPC 및 총 1,200장수 DB-free 고정 seed | turns/actions/s, command p95/p99, memory high-water, 최종 state hash |
|
||||||
| E2 | 실제 DB flush를 포함한 12 turns/s와 1,200 동시 경계 | 계산/flush/publish, schedule lag, rows/statements, rollback 0 |
|
| E2 | 실제 DB flush를 포함한 12 turns/s와 1,200 동시 경계 | 계산/flush/publish, schedule lag, rows/statements, rollback 0 |
|
||||||
| A1 | 300 SSE idle 30분 | 연결 성공/유지율, ping, runtime RSS, event-loop lag |
|
| A1 | 300 SSE idle 30분 | 연결 성공/유지율, ping, runtime RSS, event-loop lag |
|
||||||
| A2 | 300 viewer own context 변화 5분 주기 | HTTP p95/p99, fast unchanged 비율, DB statements/s |
|
| A2 | 300 viewer own context 변화 5분 주기 | HTTP p95/p99, fast unchanged 비율, DB statements/s |
|
||||||
| A3 | 초당 3회 map/record 후보 10분 | coalesced request rate, shared cache hit, stale/missed revision 0 |
|
| A3 | 초당 3회 map/record 후보 10분 | coalesced request rate, shared cache hit, stale/missed revision 0 |
|
||||||
| M1 | E2 + A1 + A2 + A3 혼합 30분 | CPU/RSS/DB/Redis/HTTP/SSE와 daemon lag 전체 |
|
| M1 | E2 + A1 + A2 + A3 혼합 30분 | CPU/RSS/DB/Redis/HTTP/SSE와 daemon lag 전체 |
|
||||||
| R1 | Redis publish/cache 장애와 API/daemon restart | durable revision 수렴, full snapshot 1회, 유실/중복 표시 0 |
|
| R1 | Redis publish/cache 장애와 API/daemon restart | durable revision 수렴, full snapshot 1회, 유실/중복 표시 0 |
|
||||||
|
|
||||||
### provisional pass gate
|
### provisional pass gate
|
||||||
|
|
||||||
@@ -515,12 +530,12 @@ API와 process 경합을 제외하므로 production 수용 근거로 단독 사
|
|||||||
1개월/1,200 general turn을 두 번 실행했다. 두 실행의 최종 state SHA-256은 모두
|
1개월/1,200 general turn을 두 번 실행했다. 두 실행의 최종 state SHA-256은 모두
|
||||||
`d14a5f451385095bb56f9459928f100bf81864178bac9861686e06001a7f02a0`이었다.
|
`d14a5f451385095bb56f9459928f100bf81864178bac9861686e06001a7f02a0`이었다.
|
||||||
|
|
||||||
| 관찰값 | 결과 |
|
| 관찰값 | 결과 |
|
||||||
| --- | ---: |
|
| ---------------------- | ------------------------: |
|
||||||
| 처리량 | 1,283.986 general turns/s |
|
| 처리량 | 1,283.986 general turns/s |
|
||||||
| general turn p95 / p99 | 2.793 / 3.393 ms |
|
| general turn p95 / p99 | 2.793 / 3.393 ms |
|
||||||
| month wall p95 | 934.590 ms |
|
| month wall p95 | 934.590 ms |
|
||||||
| max RSS | 681,316,352 bytes |
|
| max RSS | 681,316,352 bytes |
|
||||||
|
|
||||||
이는 평균 필요량 4 turns/s와 설계 burst 12 turns/s보다 계산량 자체가 충분히 작다는
|
이는 평균 필요량 4 turns/s와 설계 burst 12 turns/s보다 계산량 자체가 충분히 작다는
|
||||||
근거다. 그러나 DB-free in-memory profile이며 local CPU는 운영 후보 host와 다르다.
|
근거다. 그러나 DB-free in-memory profile이며 local CPU는 운영 후보 host와 다르다.
|
||||||
@@ -541,19 +556,19 @@ Ryzen 7 5800X와 동급이라고 간주하지 않는다. 각 run은 idle 5초, o
|
|||||||
모두 성공했다. 3,607 dashboard 응답, 즉 10,821개 slice에서 전송한 `knownSource`가
|
모두 성공했다. 3,607 dashboard 응답, 즉 10,821개 slice에서 전송한 `knownSource`가
|
||||||
응답 source와 같았고 전부 payload loader 이전 `unchanged`로 반환됐다.
|
응답 source와 같았고 전부 payload loader 이전 `unchanged`로 반환됐다.
|
||||||
|
|
||||||
| 지표 | coverage 0 | coverage 1 | 변화 |
|
| 지표 | coverage 0 | coverage 1 | 변화 |
|
||||||
| --- | ---: | ---: | ---: |
|
| ----------------------- | -------------------: | -------------------: | --------------: |
|
||||||
| own dashboard 요청 | 1,347 | 2,454 | +82.2% |
|
| own dashboard 요청 | 1,347 | 2,454 | +82.2% |
|
||||||
| mixed dashboard 요청 | 1,222 | 1,753 | +43.5% |
|
| mixed dashboard 요청 | 1,222 | 1,753 | +43.5% |
|
||||||
| unchanged slice | 5,907 | 10,821 | +83.2% |
|
| unchanged slice | 5,907 | 10,821 | +83.2% |
|
||||||
| API CPU time / 약 35초 | 27.377 s | 15.505 s | -43.4% |
|
| API CPU time / 약 35초 | 27.377 s | 15.505 s | -43.4% |
|
||||||
| API MemoryPeak | 728.1 MB | 682.5 MB | -6.3% |
|
| API MemoryPeak | 728.1 MB | 682.5 MB | -6.3% |
|
||||||
| DB transaction delta | 52,367 | 34,237 | -34.6% |
|
| DB transaction delta | 52,367 | 34,237 | -34.6% |
|
||||||
| DB tuple returned delta | 5,026,942 | 2,053,957 | -59.1% |
|
| DB tuple returned delta | 5,026,942 | 2,053,957 | -59.1% |
|
||||||
| own mean / p50 | 1,107.4 / 1,113.0 ms | 214.2 / 4.7 ms | -80.7% / -99.6% |
|
| own mean / p50 | 1,107.4 / 1,113.0 ms | 214.2 / 4.7 ms | -80.7% / -99.6% |
|
||||||
| own p95 / p99 | 1,460.6 / 1,564.7 ms | 1,566.1 / 1,869.2 ms | +7.2% / +19.5% |
|
| own p95 / p99 | 1,460.6 / 1,564.7 ms | 1,566.1 / 1,869.2 ms | +7.2% / +19.5% |
|
||||||
| mixed own mean / p50 | 498.0 / 493.1 ms | 76.5 / 4.5 ms | -84.6% / -99.1% |
|
| mixed own mean / p50 | 498.0 / 493.1 ms | 76.5 / 4.5 ms | -84.6% / -99.1% |
|
||||||
| mixed own p95 / p99 | 653.7 / 738.9 ms | 583.1 / 782.9 ms | -10.8% / +6.0% |
|
| mixed own p95 / p99 | 653.7 / 738.9 ms | 583.1 / 782.9 ms | -10.8% / +6.0% |
|
||||||
|
|
||||||
coverage 1 run에서 API CPU는 wall time 대비 약 한 core의 44.1%, 할당 4 CPU의 11.0%였다.
|
coverage 1 run에서 API CPU는 wall time 대비 약 한 core의 44.1%, 할당 4 CPU의 11.0%였다.
|
||||||
global-only front/lobby/records p95는 각각 6.7/6.4/5.7ms였다. source-fast-path가 반복
|
global-only front/lobby/records p95는 각각 6.7/6.4/5.7ms였다. source-fast-path가 반복
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# 런타임 아키텍처
|
# 런타임 아키텍처
|
||||||
|
|
||||||
|
이 문서는 개발 경험자를 위한 상세 참고서입니다. 처음부터 모든 운영 설정을 외울
|
||||||
|
필요는 없습니다. [전체 구조](./overview.md)와 [요청·턴·저장](../developer/request-turn-persistence.md)을
|
||||||
|
읽고, 실행 문제에 해당하는 절을 찾아보세요. **프로세스**는 실행 중인 프로그램,
|
||||||
|
**worker**는 특정 작업 담당, **daemon**은 계속 실행하며 일을 기다리는 프로그램입니다.
|
||||||
|
아래 구성은 코드의 배포 정의이며 특정 운영 서버의 현재 실행 상태를 증명하지는 않습니다.
|
||||||
|
|
||||||
## 프로세스
|
## 프로세스
|
||||||
|
|
||||||
| 프로세스 | 시작점 | 책임 |
|
| 프로세스 | 시작점 | 책임 |
|
||||||
@@ -204,7 +210,7 @@ Mutation은 두 형태입니다.
|
|||||||
- API transaction으로 끝나는 mutation은 `executeInputEvent()`가
|
- API transaction으로 끝나는 mutation은 `executeInputEvent()`가
|
||||||
`target=API` event를 만들고 결과와 event 상태를 같은 transaction에서
|
`target=API` event를 만들고 결과와 event 상태를 같은 transaction에서
|
||||||
commit합니다.
|
commit합니다.
|
||||||
- turn world가 필요한 mutation은 daemon transport가 `target=DAEMON`
|
- turn world가 필요한 mutation은 daemon transport가 `target=ENGINE`
|
||||||
event를 만들고 turn daemon의 처리 대상으로 전달합니다.
|
event를 만들고 turn daemon의 처리 대상으로 전달합니다.
|
||||||
|
|
||||||
같은 `requestId`의 완료·처리 중 event는 중복 수락하지 않습니다. 실패 event는
|
같은 `requestId`의 완료·처리 중 event는 중복 수락하지 않습니다. 실패 event는
|
||||||
@@ -219,7 +225,8 @@ claim 가능한 상태에서 attempts를 증가시켜 재처리합니다.
|
|||||||
event와 resource snapshot을 읽습니다.
|
event와 resource snapshot을 읽습니다.
|
||||||
3. scenario, map, unit set과 command profile을 적재합니다.
|
3. scenario, map, unit set과 command profile을 적재합니다.
|
||||||
4. action-module bundle, AI, command registry, 월간 event action을 조립합니다.
|
4. action-module bundle, AI, command registry, 월간 event action을 조립합니다.
|
||||||
5. `EngineStateManager`가 in-memory mutation과 transaction flush를 묶습니다.
|
5. `EngineStateManager`가 메모리 상태의 snapshot·실패 복원을 맡고,
|
||||||
|
`databaseHooks`가 실제 PostgreSQL transaction과 flush를 맡습니다.
|
||||||
6. `TurnDaemonLifecycle`이 control queue와 schedule을 실행합니다.
|
6. `TurnDaemonLifecycle`이 control queue와 schedule을 실행합니다.
|
||||||
|
|
||||||
`turn_daemon_lease`는 운영 WALL_TIME입니다. 모든 write와 active 비교는 DB
|
`turn_daemon_lease`는 운영 WALL_TIME입니다. 모든 write와 active 비교는 DB
|
||||||
@@ -237,7 +244,7 @@ lease/fencing 확인
|
|||||||
-> input_event claim
|
-> input_event claim
|
||||||
-> in-memory command 또는 calendar action
|
-> in-memory command 또는 calendar action
|
||||||
-> world dirty state와 side effect 수집
|
-> world dirty state와 side effect 수집
|
||||||
-> EngineStateManager transaction
|
-> EngineStateManager의 메모리 복원 경계 안에서 DB transaction
|
||||||
-> world/general/nation/city/turn/log flush
|
-> world/general/nation/city/turn/log flush
|
||||||
-> input_event result/status 갱신
|
-> input_event result/status 갱신
|
||||||
-> in-memory world checkpoint 갱신
|
-> in-memory world checkpoint 갱신
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# 시나리오 리소스 합성
|
# 시나리오 리소스 합성
|
||||||
|
|
||||||
|
시나리오는 게임 시작 조건과 사용할 지도·인물·병종·사건 등을 정하는 설정입니다.
|
||||||
|
**합성**은 여러 설정 파일을 정해진 순서로 읽어 최종 설정 하나를 만드는 과정입니다.
|
||||||
|
공통 규칙을 복사하지 않고 재사용하되, 어떤 값이 최종 적용되는지 명확해야 합니다.
|
||||||
|
|
||||||
`resources/scenario/scenario_*.json`은 공통 이벤트, 규칙과 아이템 구성을
|
`resources/scenario/scenario_*.json`은 공통 이벤트, 규칙과 아이템 구성을
|
||||||
`extends`로 조합할 수 있습니다. 시나리오마다 같은 배열과 아이템 표를 복사하지
|
`extends`로 조합할 수 있습니다. 시나리오마다 같은 배열과 아이템 표를 복사하지
|
||||||
말고, 독립적으로 켜고 끌 수 있는 기능은 `resources/scenario/extensions/`
|
말고, 독립적으로 켜고 끌 수 있는 기능은 `resources/scenario/extensions/`
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Time-domain inventory
|
# Time-domain inventory
|
||||||
|
|
||||||
|
이 문서는 시간을 필드별로 분류하는 개발자 참고표입니다. 먼저 [게임 시계](./game-clock.md)를
|
||||||
|
읽으세요. `GAME_TIME`은 게임 진행, `WALL_TIME`은 현실의 만료·기록,
|
||||||
|
`MONOTONIC_ELAPSED_TIME`은 프로세스 내부 경과시간입니다. 아래 `SHIFT`는 일정 이동,
|
||||||
|
`KEEP`는 값 보존, `REBUILD`는 새 기준으로 다시 만드는 처리를 뜻합니다.
|
||||||
|
|
||||||
This document is the authoritative classification of persistent timestamps,
|
This document is the authoritative classification of persistent timestamps,
|
||||||
deadlines, cooldowns, and process-local elapsed-time rules. Classification is
|
deadlines, cooldowns, and process-local elapsed-time rules. Classification is
|
||||||
per rule, not per table. A feature may record both a wall occurrence and a game
|
per rule, not per table. A feature may record both a wall occurrence and a game
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# 파일 지도
|
# 파일 지도
|
||||||
|
|
||||||
|
먼저 증상이 보이는 화면이나 동작 하나를 고르고, 아래 시작점에서 호출을 따라가세요.
|
||||||
|
폴더 이름만으로 저장 책임이나 권한을 추측하지 않습니다. 읽는 순서는
|
||||||
|
[기초 안내](./first-steps.md)와 [경험자 안내](./system-walkthrough.md)에 있습니다.
|
||||||
|
|
||||||
## 최상위
|
## 최상위
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -9,7 +13,8 @@ core2026/
|
|||||||
│ ├─ gateway-api/
|
│ ├─ gateway-api/
|
||||||
│ ├─ game-frontend/
|
│ ├─ game-frontend/
|
||||||
│ ├─ game-api/
|
│ ├─ game-api/
|
||||||
│ └─ game-engine/
|
│ ├─ game-engine/
|
||||||
|
│ └─ release-controller/
|
||||||
├─ packages/
|
├─ packages/
|
||||||
│ ├─ common/
|
│ ├─ common/
|
||||||
│ ├─ logic/
|
│ ├─ logic/
|
||||||
|
|||||||
@@ -1,16 +1,33 @@
|
|||||||
# 도메인과 조립 지점
|
# 도메인과 조립 지점
|
||||||
|
|
||||||
|
**도메인**은 장수·도시·전투처럼 이 게임이 다루는 개념과 규칙입니다. 이 문서는
|
||||||
|
객체 지향 문법을 설명하기보다 “규칙을 누가 계산하고 결과를 누가 적용하는가”를
|
||||||
|
따라갑니다. [기초 안내](./first-steps.md)의 모병 예시를 떠올리면 좋습니다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
저장된 장수·도시 → loader → 메모리 세계
|
||||||
|
예약한 명령 → 입력 해석 → 조건 검사 → 규칙 계산 → 변경 결과
|
||||||
|
변경 결과 → 메모리 세계에 적용 → DB 저장 또는 실패 시 복원
|
||||||
|
```
|
||||||
|
|
||||||
## World entity
|
## World entity
|
||||||
|
|
||||||
`packages/logic/src/domain/entities.ts`와 `world/types.ts`가 장수, 국가, 도시,
|
`packages/logic/src/domain/entities.ts`와 `world/types.ts`가 장수, 국가, 도시,
|
||||||
부대, 외교와 trigger state의 런타임 타입을 정의합니다. Prisma row는
|
부대, 외교와 trigger state의 런타임 타입을 정의합니다. Prisma row는
|
||||||
`app/game-engine/src/turn/worldLoader.ts`가 이 타입으로 변환합니다.
|
`app/game-engine/src/turn/worldLoader.ts`가 이 타입으로 변환합니다.
|
||||||
`InMemoryTurnWorld`가 조회와 mutation을 제공하고 `EngineStateManager`가
|
`InMemoryTurnWorld`가 조회와 mutation을 제공하고 `EngineStateManager`가
|
||||||
transaction snapshot과 dirty state를 관리합니다.
|
메모리 snapshot과 실패 복원을 관리합니다. 실제 DB transaction은
|
||||||
|
`databaseHooks.ts`의 책임입니다. **dirty state**는 마지막 저장 이후 바뀌어 다시
|
||||||
|
저장해야 하는 상태를 뜻합니다.
|
||||||
|
|
||||||
## Command
|
## Command
|
||||||
|
|
||||||
장수 command는 `GeneralActionDefinition`으로 다음 계약을 가집니다.
|
명령(command)은 “모병” 같은 행동 한 종류입니다. 입력(args)은 병종·수량처럼
|
||||||
|
그 행동에 필요한 값입니다. **constraint**는 실행에 필요한 조건이고,
|
||||||
|
**state patch**는 병력·자원 등 바꿀 값의 묶음입니다. 실행 결과에는 patch뿐 아니라
|
||||||
|
플레이어에게 보일 로그와 후속 효과도 포함됩니다.
|
||||||
|
|
||||||
|
장수 command는 definition·command spec·resolver를 통해 다음 계약을 연결합니다.
|
||||||
|
|
||||||
- key와 사용자 표시 이름
|
- key와 사용자 표시 이름
|
||||||
- raw args parser
|
- raw args parser
|
||||||
@@ -38,25 +55,26 @@ turn 소비와 side effect 계약을 보존합니다.
|
|||||||
priority trigger와 의미 event는 각각 다른 interface를 사용합니다.
|
priority trigger와 의미 event는 각각 다른 interface를 사용합니다.
|
||||||
[행동 모듈 프로토콜](../architecture/action-module-protocol.md)을 따라 주세요.
|
[행동 모듈 프로토콜](../architecture/action-module-protocol.md)을 따라 주세요.
|
||||||
|
|
||||||
## 주요 클래스
|
## 주요 클래스와 함수
|
||||||
|
|
||||||
| 클래스·함수 | 책임 |
|
| 클래스·함수 | 책임 |
|
||||||
| ------------------------- | -------------------------------------------------- |
|
| ----------------------------- | -------------------------------------------------- |
|
||||||
| `GatewayOrchestrator` | profile operation과 process reconciliation |
|
| `GatewayOrchestrator` | profile operation과 process reconciliation |
|
||||||
| `createGameApiServer` | game transport, context, router와 worker lifecycle |
|
| `createGameApiServer` | game transport, context, router와 worker lifecycle |
|
||||||
| `DatabaseTurnDaemonLease` | profile별 lease, heartbeat와 fencing |
|
| `DatabaseTurnDaemonLease` | profile별 lease, heartbeat와 fencing |
|
||||||
| `TurnDaemonLifecycle` | schedule, pause/resume/run/shutdown loop |
|
| `TurnDaemonLifecycle` | schedule, pause/resume/run/shutdown loop |
|
||||||
| `InMemoryTurnWorld` | turn 실행 중 world state와 dirty tracking |
|
| `InMemoryTurnWorld` | turn 실행 중 world state와 dirty tracking |
|
||||||
| `EngineStateManager` | snapshot, transaction flush와 rollback restore |
|
| `EngineStateManager` | 메모리 snapshot, 실패 시 restore |
|
||||||
| `ReservedTurnHandler` | revision·lease 기반 예약 명령 claim과 실행 |
|
| `createReservedTurnHandler()` | revision·lease 기반 예약 명령 claim과 실행 |
|
||||||
| `GeneralActionPipeline` | action module 계산·trigger·event 실행 |
|
| `GeneralActionPipeline` | action module 계산·trigger·event 실행 |
|
||||||
| `WarEngine` | 전투 phase, RNG, 상태와 log 결과 |
|
| `resolveWarBattle()` | 전투 phase, RNG, 상태와 log 결과 |
|
||||||
|
|
||||||
## 명령 추가
|
## 명령 추가
|
||||||
|
|
||||||
1. ref command의 예약·실행 constraint, args, RNG, log와 DB mutation을 찾습니다.
|
1. 계승 명령이면 Ref의 예약·실행 조건, 입력, RNG, 로그와 DB 변경을 찾습니다.
|
||||||
|
Core 전용 명령이면 요구사항과 의도한 결과를 먼저 정합니다.
|
||||||
2. command definition과 필요한 domain helper를 추가합니다.
|
2. command definition과 필요한 domain helper를 추가합니다.
|
||||||
3. engine registry, profile resource와 frontend args UI를 연결합니다.
|
3. engine registry, profile resource와 frontend args UI를 연결합니다.
|
||||||
4. state patch와 dirty field가 flush·reload되는지 확인합니다.
|
4. state patch와 dirty field가 flush·reload되는지 확인합니다.
|
||||||
5. 정상·실패·경계 fixed-seed test와 ref 차등 fixture를 추가합니다.
|
5. 정상·실패·경계 fixed-seed test를 추가하고, 계승 계약은 Ref 차등 fixture로 확인합니다.
|
||||||
6. 생성 command catalog, 상위 mapping과 report를 갱신합니다.
|
6. 생성 command catalog, 상위 mapping과 report를 갱신합니다.
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# 코드가 처음인 사람을 위한 구조 안내
|
||||||
|
|
||||||
|
함수, 변수, 조건문은 알지만 웹 서버나 큰 프로젝트는 처음인 독자를 위한 글입니다.
|
||||||
|
이 문서를 읽고 나면 화면을 바꿀 파일, 게임 규칙을 바꿀 파일, 결과를 저장할 파일을
|
||||||
|
구분할 수 있습니다. 설치 전에 읽어도 됩니다.
|
||||||
|
|
||||||
|
## 먼저 게임을 한 문장으로 이해하기
|
||||||
|
|
||||||
|
플레이어는 장수 한 명의 행동을 미리 예약합니다. 서버는 장수의 차례가 되면
|
||||||
|
행동을 실행하고, 자원·도시·전투 결과를 저장합니다. 여러 플레이어와 컴퓨터 장수가
|
||||||
|
같은 세계에 참여하므로, 브라우저를 닫아도 세계는 계속 진행될 수 있습니다.
|
||||||
|
게임 자체가 낯설다면 [플레이어 첫 안내](../user/index.md)를 먼저 읽으세요.
|
||||||
|
|
||||||
|
## 화면과 서버는 다른 프로그램입니다
|
||||||
|
|
||||||
|
**프론트엔드**는 브라우저에서 실행되어 화면을 그리고 버튼 입력을 받는 프로그램입니다.
|
||||||
|
**백엔드**는 서버에서 실행되어 로그인과 권한을 확인하고 데이터를 읽거나 바꾸는
|
||||||
|
프로그램입니다. 버튼을 눌렀다고 브라우저가 직접 병력을 늘리는 것은 아닙니다.
|
||||||
|
브라우저는 서버에 요청하고, 서버가 허용한 결과를 받아 표시합니다.
|
||||||
|
|
||||||
|
**API**는 두 프로그램이 요청과 응답을 주고받는 약속입니다. 이 프로젝트는
|
||||||
|
TypeScript 타입으로 호출 형태를 연결하는 **tRPC**를 사용합니다. 타입이 맞더라도
|
||||||
|
사용자 권한과 실제 자원은 서버가 다시 검사해야 합니다.
|
||||||
|
|
||||||
|
예를 들어 “모병을 예약”하는 과정은 다음과 같습니다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
브라우저: 병종과 수량을 고른다
|
||||||
|
↓ 저장 요청
|
||||||
|
게임 API: 로그인과 입력을 검사하고 예약 변경을 처리한다
|
||||||
|
↓ 예약 목록에 반영
|
||||||
|
턴 데몬: 내 차례가 되면 모병 조건을 다시 검사한다
|
||||||
|
↓ 실행 결과
|
||||||
|
데이터베이스: 병력, 자원, 로그와 남은 예약을 저장한다
|
||||||
|
↓ 알림을 받고 다시 조회
|
||||||
|
브라우저: 실제 결과를 보여 준다
|
||||||
|
```
|
||||||
|
|
||||||
|
예약 저장과 모병 실행은 서로 다른 일입니다. 예약 이후 돈을 쓰거나 도시를
|
||||||
|
잃었다면 실행에 실패할 수 있습니다. API를 읽을 때도 “요청을 받았다”와
|
||||||
|
“게임 행동이 끝났다”를 구분해야 합니다.
|
||||||
|
|
||||||
|
## 다섯 곳을 먼저 익히세요
|
||||||
|
|
||||||
|
| 위치 | 역할 | 찾아볼 때 |
|
||||||
|
| ------------------- | ------------------------------------- | -------------------------------------------- |
|
||||||
|
| `app/game-frontend` | Vue 화면과 사용자 입력 | 예턴 입력창이나 도시 화면을 이해하고 싶을 때 |
|
||||||
|
| `app/game-api` | 로그인·권한 검사, 조회·변경 요청 접수 | 누가 어떤 정보를 볼 수 있는지 궁금할 때 |
|
||||||
|
| `app/game-engine` | 시간에 맞춘 실행, 세계 상태와 저장 | 예약한 행동이 언제 실행되는지 궁금할 때 |
|
||||||
|
| `packages/logic` | 모병·내정·전투 등 게임 규칙 | 비용이나 성공 조건을 찾을 때 |
|
||||||
|
| `packages/infra` | 데이터베이스 구조와 연결 | 무엇을 영구히 저장하는지 찾을 때 |
|
||||||
|
|
||||||
|
`app`은 실행하는 프로그램, `packages`는 여러 프로그램이 함께 쓰는 코드 묶음입니다.
|
||||||
|
한 저장소에 여러 프로그램과 묶음이 있는 구성을 **모노레포**라고 합니다.
|
||||||
|
`pnpm`은 이 묶음들의 설치와 명령 실행을 관리합니다.
|
||||||
|
|
||||||
|
`resources`에는 지도, 병종, 시나리오 설정이 있습니다. 규칙 코드가 같아도 선택한
|
||||||
|
설정이 다르면 다른 게임이 됩니다. `tools`에는 검사·변환 같은 개발 도구가,
|
||||||
|
`docs`에는 지금 읽고 있는 문서가 있습니다.
|
||||||
|
|
||||||
|
## 로비와 게임을 나누는 이유
|
||||||
|
|
||||||
|
**Gateway**는 회원 계정과 로비를 담당합니다. 하나의 계정으로 여러 게임 서버에
|
||||||
|
들어갈 수 있지만, 각 게임의 장수·국가·진행 시점은 다릅니다. 운영 대상 게임을
|
||||||
|
코드에서는 **프로필(profile)**이라고 부릅니다. 프로필은 개인 프로필 사진과 다른
|
||||||
|
뜻입니다. **시나리오**는 그 게임에 적용할 시작 조건·지도·규칙 묶음입니다.
|
||||||
|
|
||||||
|
Gateway의 화면과 API는 `app/gateway-frontend`, `app/gateway-api`에 있습니다.
|
||||||
|
게임 업데이트와 실행 관리도 필요하므로 운영용 프로그램이 추가로 있습니다.
|
||||||
|
처음부터 이들을 모두 읽을 필요는 없습니다. [전체 구조](../architecture/overview.md)를
|
||||||
|
읽을 때 게임 진행 경로와 운영 경로를 나눠 보세요.
|
||||||
|
|
||||||
|
## 데이터는 어디에 남나요?
|
||||||
|
|
||||||
|
**메모리**는 실행 중인 프로그램이 빠르게 사용하는 작업 공간입니다. 프로그램을
|
||||||
|
다시 시작하면 사라질 수 있습니다. **PostgreSQL**은 세계 상태와 기록을 영구히
|
||||||
|
보관하는 데이터베이스입니다. **Prisma**는 코드에서 데이터베이스를 다루기 위한
|
||||||
|
도구이며, `packages/infra/prisma/game.prisma`는 게임 데이터 구조를 정의합니다.
|
||||||
|
|
||||||
|
턴 데몬은 세계를 메모리에 읽어 와 계산합니다. 계산이 끝나면 바뀐 값과 로그를
|
||||||
|
**트랜잭션(transaction)**으로 묶어 저장합니다. 모병으로 돈만 줄고 병력은 늘지
|
||||||
|
않는 식의 반쪽 저장을 막기 위한 경계입니다. 저장에 실패하면 메모리 상태도
|
||||||
|
되돌려야 다음 행동이 잘못된 세계에서 실행되지 않습니다.
|
||||||
|
|
||||||
|
**Redis**는 세션이나 작업 전달·알림 등에 쓰는 별도의 저장·통신 도구입니다.
|
||||||
|
게임 진행이 확정됐는지는 PostgreSQL 저장 결과를 기준으로 판단합니다.
|
||||||
|
**SSE**는 서버가 열린 브라우저로 변경 알림을 보내는 연결입니다. 알림이 늦었다고
|
||||||
|
곧바로 행동이 실패한 것은 아니므로 최신 데이터를 다시 조회합니다.
|
||||||
|
|
||||||
|
## 파일 하나에서 시작하는 읽기 연습
|
||||||
|
|
||||||
|
1. `packages/logic/src/actions/turn/general/che_모병.ts`를 엽니다.
|
||||||
|
입력값, 조건, 결과를 나눠 봅니다. 지금은 모든 계산을 외우지 않아도 됩니다.
|
||||||
|
2. `app/game-engine/src/turn/reservedTurnHandler.ts`를 읽으며 예약 행동이
|
||||||
|
실행되는 곳을 찾습니다. 규칙 파일과 실행 담당 파일이 다른 이유를 확인합니다.
|
||||||
|
3. `app/game-api/src/router/turns/`에서 예약을 바꾸는 요청을 찾습니다.
|
||||||
|
예약과 실행 사이에 시간이 흐른다는 점을 다시 확인합니다.
|
||||||
|
4. [파일 지도](./code-map.md)로 화면과 저장 담당 위치를 이어 봅니다.
|
||||||
|
|
||||||
|
파일명 뒤 `.ts`는 TypeScript 코드, `.vue`는 Vue 화면 구성요소, `.json`은 설정
|
||||||
|
데이터, `.prisma`는 데이터베이스 구조, `.test.ts`는 검증 코드입니다.
|
||||||
|
`import`는 다른 파일의 기능을 가져오는 선언입니다. 함수를 읽다가 모르는 이름을
|
||||||
|
만나면 선언 위치로 이동해 입력과 반환값부터 확인하세요.
|
||||||
|
|
||||||
|
다음은 [개발자 핸드북](./index.md)의 경험자 경로입니다. 실행 환경을 준비할 때는
|
||||||
|
저장소 `README.md`의 개발 환경과 [테스트 정책](../testing-policy.md)을 따릅니다.
|
||||||
@@ -4,9 +4,11 @@ Baseline: `main@b91dcbcaaac5acd4c7349cd3ed0996c547f58756`
|
|||||||
|
|
||||||
Branch: `test/game-clock-reconciliation-20260903`
|
Branch: `test/game-clock-reconciliation-20260903`
|
||||||
|
|
||||||
This plan is the status source for the long-running user test branch. A checked
|
이 문서는 위 기준선·branch에서 진행한 구현 계획의 기록입니다. 체크 표시는 당시
|
||||||
item means code and focused automated evidence exist on this branch; it does not
|
코드와 집중 검증이 있었다는 뜻이며, 현재 main·배포·운영 검증 상태를 나타내지 않습니다.
|
||||||
mean deployment or production validation.
|
현재 동작은 [게임 시계](../architecture/game-clock.md)와
|
||||||
|
[재정렬 계약](../architecture/game-clock-reconciliation.md), 장애 처리는
|
||||||
|
[복구 안내](./game-clock-recovery.md)를 읽으세요.
|
||||||
|
|
||||||
## Milestone 1 - authority and inventory
|
## Milestone 1 - authority and inventory
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
# Game clock reconciliation recovery
|
# 게임 시계 재정렬 실패 복구
|
||||||
|
|
||||||
This runbook is intentionally fail-closed. Do not force a profile to `RUNNING`
|
이 문서는 운영 경험이 있는 개발자를 위한 절차입니다. 개념부터 읽으려면
|
||||||
or delete an outbox row merely because its process is alive.
|
[게임 시계](../architecture/game-clock.md), 계산·저장 계약은
|
||||||
|
[시계 재정렬](../architecture/game-clock-reconciliation.md)을 참고하세요.
|
||||||
|
프로세스가 살아 있다는 이유만으로 `RUNNING`을 강제하거나 outbox 행을 지우지 않습니다.
|
||||||
|
|
||||||
## Observe
|
## 먼저 관찰하기
|
||||||
|
|
||||||
Read only the target game schema. Record `world_state.clock_phase`,
|
대상 게임 schema에서 `world_state.clock_phase`, `clock_revision`,
|
||||||
`clock_revision`, `deadline_generation`, the latest `clock_suspension`, all its
|
`deadline_generation`, 최신 `clock_suspension`, 참여자 checksum과 대응하는
|
||||||
participant checksums, and the matching `clock_projection_outbox`. Compare that
|
`clock_projection_outbox`를 읽습니다. Redis의
|
||||||
target revision with `sammo:{profile}:clock:active-revision`. Never print DB or
|
`sammo:{profile}:clock:active-revision`과 목표 revision을 비교합니다.
|
||||||
Redis credentials.
|
DB·Redis 접속 비밀은 출력하지 않습니다.
|
||||||
|
|
||||||
## Status meaning
|
**outbox**는 DB에서 확정했지만 다른 저장소에 전달해야 할 일을 보관한 원장입니다.
|
||||||
|
**checksum**은 재정렬 대상이 예상한 상태인지 비교하기 위한 요약값입니다.
|
||||||
|
DB와 Redis를 하나의 트랜잭션으로 묶었다고 가정하지 말고 두 단계의 상태를 봅니다.
|
||||||
|
|
||||||
- `SUSPENDED`: the cut is durable; no alignment DB transaction has committed.
|
## 상태를 해석하기
|
||||||
- `RECONCILING` with `PENDING`/`FAILED` outbox: DB schedules moved, Redis is not
|
|
||||||
authoritative yet, and gameplay must remain stopped.
|
|
||||||
- `RECONCILING` with `APPLIED` outbox: verify Redis active revision and all
|
|
||||||
participant checksums before finalizing.
|
|
||||||
- `RUNNING`: DB revision, deadline generation, and Redis active revision must
|
|
||||||
agree. A mismatch is an incident and workers must not dequeue.
|
|
||||||
|
|
||||||
## Retry
|
| 상태 | 의미와 확인할 일 |
|
||||||
|
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| `SUSPENDED` | 중단 지점은 저장됐지만 일정 정렬 DB transaction은 아직 확정되지 않음 |
|
||||||
|
| `RECONCILING`, outbox `PENDING`·`FAILED` | DB 일정은 옮겼지만 Redis 전환이 끝나지 않음. 게임 진행을 열지 않음 |
|
||||||
|
| `RECONCILING`, outbox `APPLIED` | Redis 활성 revision과 모든 참여자 checksum을 확인하고 최종 확정 |
|
||||||
|
| `RUNNING` | DB revision·deadline generation·Redis 활성 revision이 일치해야 함. 불일치하면 worker가 작업을 꺼내서는 안 됨 |
|
||||||
|
|
||||||
Retry the same suspension ID and target revision through the clock-operation
|
## 같은 작업을 재시도하기
|
||||||
service. The service must re-read participant checksums and either return the
|
|
||||||
already-applied result or resume the pending outbox. Never create a replacement
|
|
||||||
revision to hide a failed target revision.
|
|
||||||
|
|
||||||
For `UNIFICATION_WAIT`, never rerun invader creation as a separate repair.
|
시계 작업 서비스를 통해 **같은 suspension ID와 목표 revision**으로 재시도합니다.
|
||||||
The input event, aligned schedules, optional rate, deterministic invader IDs,
|
서비스는 checksum을 다시 확인하고, 이미 적용됐다면 그 결과를 사용하거나 남은
|
||||||
reserved turns, and outbox committed together. A committed command with a
|
outbox 처리를 이어갑니다. 실패를 숨기려고 새 revision을 만들지 않습니다.
|
||||||
`RECONCILING` world therefore needs only the same outbox retry. If the command
|
|
||||||
transaction rolled back, the original prompt and source revision remain and
|
|
||||||
the same response can be retried without changing IDs or RNG results.
|
|
||||||
|
|
||||||
When an outbox row is `FAILED`, the profile must remain `RECONCILING`. A retry
|
`UNIFICATION_WAIT`에서는 이민족 생성을 별도 복구로 다시 실행하지 않습니다.
|
||||||
is safe in both crash locations:
|
입력 이벤트, 정렬된 일정, 선택적 간격 변경, 결정적인 이민족 ID, 예약 턴과 outbox가
|
||||||
|
함께 확정됩니다. 명령이 확정됐고 세계가 `RECONCILING`이라면 같은 outbox만 이어
|
||||||
|
처리합니다. transaction이 rollback됐다면 원래 질문·source revision이 남으므로
|
||||||
|
ID나 RNG 결과를 바꾸지 않고 같은 응답을 재시도할 수 있습니다.
|
||||||
|
|
||||||
- before Redis commit, the Lua operation reapplies from the source revision;
|
실패 위치에 따른 처리는 다음과 같습니다.
|
||||||
- after Redis commit but before DB finalization, the Lua operation returns the
|
|
||||||
already-active target result and DB finalization resumes without shifting any
|
|
||||||
tournament deadline twice.
|
|
||||||
|
|
||||||
`lastError` naming a legacy tournament deadline means the tournament lacks its
|
- Redis 반영 전: Lua 작업이 원래 revision에서 적용합니다.
|
||||||
tick dual-write. Do not force the active revision; migrate or prove the
|
- Redis 반영 후·DB 최종 확정 전: Lua가 이미 활성화한 결과를 돌려주고 DB 확정을
|
||||||
tournament inactive, then retry the same outbox.
|
이어갑니다. 토너먼트 기한을 두 번 이동하지 않습니다.
|
||||||
|
|
||||||
## Rollback
|
`lastError`가 구형 토너먼트 기한을 지목하면 tick 기록이 갖춰졌는지 확인합니다.
|
||||||
|
활성 revision을 강제하지 말고 migration이나 비활성 상태의 근거를 확인한 뒤 같은
|
||||||
|
outbox를 재시도합니다.
|
||||||
|
|
||||||
There is no blind inverse update. Before enabling exact reconciliation in an
|
## 되돌리기와 검증 범위
|
||||||
environment, keep the normal database backup required for schema migrations.
|
|
||||||
If participant verification shows an unexpected mutation, stop the profile,
|
|
||||||
retain the ledger/outbox evidence, and restore the whole game schema from that
|
|
||||||
backup. Redis projections are then rebuilt from the restored DB revision.
|
|
||||||
|
|
||||||
The conditional clock suite exercises `SUSPENDED`, `RECONCILING/PENDING`,
|
모든 값을 반대로 이동하는 일괄 역연산은 복구 절차가 아닙니다. 운영 변경 전
|
||||||
`RECONCILING/FAILED` before and after Redis commit, recovered `APPLIED`, and
|
정상 백업을 준비하고, 예상하지 못한 mutation이 확인되면 대상 profile을 멈춰
|
||||||
final `RUNNING`. The admin status endpoint exposes the phase, revision,
|
원장·outbox 증거를 보존합니다. 백업 복원이 필요하면 해당 시점 이후 데이터 손실
|
||||||
participant checksums, and outbox error needed to choose the matching step.
|
범위를 평가하고 승인된 운영 복구 절차로 game schema를 복원합니다. Redis 투영은
|
||||||
|
복원한 DB revision에서 다시 구성합니다.
|
||||||
|
|
||||||
|
조건부 시계 통합 suite는 `SUSPENDED`, Redis 적용 전·후의 실패, `APPLIED` 복구와
|
||||||
|
최종 `RUNNING`을 검사합니다. 실행에 필요한 DB·Redis가 없어 skip된 결과는 복구
|
||||||
|
검증 성공이 아닙니다. [테스트 정책](../testing-policy.md)과 해당 suite 설정을 확인하세요.
|
||||||
|
|||||||
+13
-3
@@ -2,9 +2,16 @@
|
|||||||
|
|
||||||
## 읽기 순서
|
## 읽기 순서
|
||||||
|
|
||||||
신규 관리자 플레이 감사의 확정 범위, 수집·조회 비용과 후속 구현 체크리스트는
|
프로그래밍 기초만 안다면 [코드가 처음인 사람을 위한 구조 안내](./first-steps.md)에서
|
||||||
[프로필별 플레이 감사 설계](../design/play-audit.md)에 있습니다. 해당 기능은
|
시작하세요. 화면·서버·저장을 익힌 뒤 아키텍처 개요와 파일 지도로 넘어갑니다.
|
||||||
아직 미구현이며 아래의 현재 구조 설명과 상태를 구분합니다.
|
|
||||||
|
개발 경험이 있다면 [경험자를 위한 시스템 읽기](./system-walkthrough.md) →
|
||||||
|
[아키텍처 개요](../architecture/overview.md) → [요청·턴·저장](./request-turn-persistence.md)
|
||||||
|
순서가 좋습니다. 이후 아래 표에서 맡은 기능을 고르세요. 런타임 문서는 배포·worker까지
|
||||||
|
포함하는 상세 참고서입니다.
|
||||||
|
|
||||||
|
관리자 감사는 [운영 안내](../play-audit-operations.md)에서 현재 기능을,
|
||||||
|
[설계](../design/play-audit.md)에서 요구사항과 배경을 확인합니다.
|
||||||
|
|
||||||
| 작업 | 문서 | 코드 시작점 |
|
| 작업 | 문서 | 코드 시작점 |
|
||||||
| --------------------- | --------------------------------------------------------------- | ------------------------------ |
|
| --------------------- | --------------------------------------------------------------- | ------------------------------ |
|
||||||
@@ -18,6 +25,9 @@
|
|||||||
| action module | [행동 모듈 프로토콜](../architecture/action-module-protocol.md) | `actionModules/` |
|
| action module | [행동 모듈 프로토콜](../architecture/action-module-protocol.md) | `actionModules/` |
|
||||||
| ref 비교 | [차등 검증](../architecture/turn-state-differential-testing.md) | `tools/integration-tests` |
|
| ref 비교 | [차등 검증](../architecture/turn-state-differential-testing.md) | `tools/integration-tests` |
|
||||||
|
|
||||||
|
[구조 문서 찾아보기](./reference-map.md)에는 시계·재시도·실시간 갱신·측정·운영 자료를
|
||||||
|
질문별로 모았습니다. 과거 계획과 현재 계약의 구분도 여기서 확인할 수 있습니다.
|
||||||
|
|
||||||
## 경계
|
## 경계
|
||||||
|
|
||||||
- `packages/logic`은 계산과 규칙을 소유합니다.
|
- `packages/logic`은 계산과 규칙을 소유합니다.
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 구조 문서 찾아보기
|
||||||
|
|
||||||
|
[기초 안내](./first-steps.md)와 [경험자 안내](./system-walkthrough.md)가 학습 경로를,
|
||||||
|
아래 문서들은 세부 계약을 제공합니다. 목록·벤치마크·과거 계획을 처음부터 모두
|
||||||
|
읽기보다 현재 질문에 해당하는 문서를 고르세요.
|
||||||
|
|
||||||
|
## 현재 구조와 구현 계약
|
||||||
|
|
||||||
|
| 질문 | 문서 |
|
||||||
|
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| 무엇이 어디서 실행되나요? | [개요](../architecture/overview.md), [런타임](../architecture/runtime.md) |
|
||||||
|
| 어느 파일부터 읽나요? | [파일 지도](./code-map.md), [패키지 경계](../architecture/package-boundaries.md) |
|
||||||
|
| 한 행동이 어떻게 계산되나요? | [도메인과 조립](./domain-and-classes.md), [행동 모듈](../architecture/action-module-protocol.md) |
|
||||||
|
| 언제 저장되고 실패는 어떻게 처리하나요? | [요청·턴·저장](./request-turn-persistence.md), [API 재시도](../architecture/api-input-event-replay.md) |
|
||||||
|
| 시간 정지·재개는 어떻게 되나요? | [게임 시계](../architecture/game-clock.md), [시간 도메인 목록](../architecture/time-domains.md), [재정렬 계약](../architecture/game-clock-reconciliation.md) |
|
||||||
|
| 설정을 여러 파일에서 조합하나요? | [시나리오 합성](../architecture/scenario-composition.md) |
|
||||||
|
| 화면에 변경이 어떻게 전달되나요? | [실시간 변경 원장](../architecture/realtime-change-journal.md) |
|
||||||
|
| 전투 시뮬레이터가 어디서 계산하나요? | [브라우저 Worker](../architecture/battle-simulator-browser-worker.md) |
|
||||||
|
| Ref와 무엇을 비교하나요? | [차등 검증](../architecture/turn-state-differential-testing.md) |
|
||||||
|
| TypeScript 버전이 왜 둘인가요? | [도구 체인 정책](../architecture/typescript-version.md) |
|
||||||
|
|
||||||
|
## 변경 범위를 빠짐없이 확인하는 목록
|
||||||
|
|
||||||
|
[엔진 호출 procedure 목록](../architecture/game-api-daemon-procedure-inventory.md)과
|
||||||
|
[직접 변경·journal 목록](../architecture/game-api-direct-mutation-journal-inventory.md)은
|
||||||
|
읽기 교재보다 변경 누락을 찾는 검토 자료입니다. 표의 조사 날짜·기준선과 현재
|
||||||
|
router·검사 코드를 함께 확인하세요. 새 API가 늘어도 과거 조사 숫자가 자동으로
|
||||||
|
현재 개수가 되지는 않습니다.
|
||||||
|
|
||||||
|
시계 참여자 JSON·mutation evidence TSV는 기계가 사용하는 자료입니다. 형식과
|
||||||
|
검사 계약을 유지하며 해당 기능을 바꿀 때 갱신합니다.
|
||||||
|
|
||||||
|
## 측정·계획·운영 자료
|
||||||
|
|
||||||
|
| 자료 | 해석 범위 |
|
||||||
|
| ----------------------------------------------------------------------------------- | ----------------------------------------------------------- |
|
||||||
|
| [NPC 메모리 측정](../architecture/npc-lifecycle-memory-profile.md) | 지정 fixture의 메모리·생성/사망 부하. 운영 DB 처리량과 다름 |
|
||||||
|
| [NPC 천통 시간 측정](../architecture/npc-unification-timing-benchmark.md) | 인메모리 계산 시간. 실제 배포 성능 보장이 아님 |
|
||||||
|
| [시계 구현 계획 기록](./game-clock-reconciliation-plan.md) | 당시 branch의 milestone. 현재 운영 상태의 기준이 아님 |
|
||||||
|
| [시계 복구](./game-clock-recovery.md) | 상태·원장 확인과 같은 작업 재시도 |
|
||||||
|
| [릴리스 운영](../release-operations.md) | 실제 배포 작업과 준비 확인 |
|
||||||
|
| [관리자 콘솔](../admin-console.md), [플레이 감사 운영](../play-audit-operations.md) | 관리자 권한과 현재 조사 기능 |
|
||||||
|
| [테스트 정책](../testing-policy.md) | 실행 준비·검증 종류·skip의 해석 |
|
||||||
|
| [프론트엔드 CSS 구조](../frontend-css-architecture.md) | 화면 스타일의 소유권과 배치 계약 |
|
||||||
|
|
||||||
|
기능 설계의 목표, 코드에 있는 구현, 테스트로 확인한 범위, 실제 배포 상태는 서로
|
||||||
|
다릅니다. 문서의 “완료”는 함께 적힌 날짜와 검증 범위로 해석하세요.
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
# 요청·턴·저장 흐름
|
# 요청·턴·저장 흐름
|
||||||
|
|
||||||
|
이 문서는 데이터베이스와 트랜잭션을 아는 독자를 위한 실행 흐름입니다. 처음이라면
|
||||||
|
[기초 구조 안내](./first-steps.md)를 먼저 읽으세요. **mutation**은 상태 변경,
|
||||||
|
**flush**는 메모리에서 바뀐 내용을 저장소에 반영하는 작업입니다.
|
||||||
|
조회·즉시 변경·예약 수정·시간에 따른 턴 실행을 구분해서 읽습니다.
|
||||||
|
|
||||||
## 조회
|
## 조회
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -13,31 +18,35 @@ endpoint DTO에서 공개 field를 선택합니다.
|
|||||||
## API transaction mutation
|
## API transaction mutation
|
||||||
|
|
||||||
```text
|
```text
|
||||||
request
|
request → requestId·입력·actor·권한 검증
|
||||||
-> requestId와 input 검증
|
→ PostgreSQL transaction
|
||||||
-> session actor·권한 검사
|
→ API InputEvent 생성 또는 기존 row 잠금
|
||||||
-> InputEvent(target=API, PROCESSING)
|
→ identity 확인 → PROCESSING(attempts + 1)
|
||||||
-> Prisma transaction
|
→ savepoint 이후 업무 변경
|
||||||
-> domain row mutation
|
→ SUCCEEDED + 실제 result → commit
|
||||||
-> InputEvent(SUCCEEDED)
|
→ notification
|
||||||
-> commit
|
|
||||||
-> notification
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`app/game-api/src/inputEventBoundary.ts`의 `executeInputEvent()`가 이 경계를
|
`app/game-api/src/inputEventBoundary.ts`의 `executeInputEvent()`가 이 경계를
|
||||||
제공합니다. 중복 request ID는 완료·처리 중 event를 다시 실행하지 않으며,
|
제공합니다. identity는 event type, actor, payload digest를 포함합니다. 같은 요청의
|
||||||
실패 event는 claim 조건을 만족할 때 attempts를 증가시킵니다.
|
완료 결과는 재사용하고, 다른 내용으로 같은 ID를 사용하면 충돌로 거부합니다.
|
||||||
|
`PENDING`·`FAILED`는 identity가 맞으면 재시도할 수 있고, `PROCESSING`은 임의로
|
||||||
|
다시 선점하지 않습니다.
|
||||||
|
|
||||||
|
업무 오류는 savepoint까지 되돌린 뒤 실패 상태를 저장합니다. DB transaction
|
||||||
|
자체가 실패하면 그 안의 변경은 rollback됩니다. 상세 상태 표와 HTTP 응답 계약은
|
||||||
|
[API 입력 재시도](../architecture/api-input-event-replay.md)를 따릅니다.
|
||||||
|
|
||||||
## Daemon mutation
|
## Daemon mutation
|
||||||
|
|
||||||
```text
|
```text
|
||||||
request
|
request
|
||||||
-> actor·input 검증
|
-> actor·input 검증
|
||||||
-> InputEvent(target=DAEMON)
|
-> InputEvent(target=ENGINE)
|
||||||
-> daemon transport
|
-> daemon transport
|
||||||
-> lease owner claim
|
-> lease owner claim
|
||||||
-> in-memory world mutation
|
-> in-memory world mutation
|
||||||
-> EngineStateManager transaction flush
|
-> EngineStateManager의 메모리 복원 경계 안에서 DB transaction flush
|
||||||
-> PostgreSQL event 결과 commit과 in-memory world checkpoint 확정
|
-> PostgreSQL event 결과 commit과 in-memory world checkpoint 확정
|
||||||
-> SSE/realtime
|
-> SSE/realtime
|
||||||
```
|
```
|
||||||
@@ -60,7 +69,9 @@ request
|
|||||||
7. PostgreSQL transaction에서 dirty state, turn queue, log와 event를 flush하고,
|
7. PostgreSQL transaction에서 dirty state, turn queue, log와 event를 flush하고,
|
||||||
같은 `EngineStateManager` 경계에서 world checkpoint를 확정합니다.
|
같은 `EngineStateManager` 경계에서 world checkpoint를 확정합니다.
|
||||||
|
|
||||||
Transaction 실패 시 `EngineStateManager`가 in-memory snapshot을 복원합니다.
|
`databaseHooks.ts`가 PostgreSQL transaction을 담당하고, 이를 감싼
|
||||||
|
`EngineStateManager`가 실패 시 메모리 snapshot을 복원합니다. 이 관리자는 DB를
|
||||||
|
직접 알지 못합니다. DB rollback과 메모리 복원을 함께 유지해야 합니다.
|
||||||
Lease를 잃은 process는 fencing 검사에서 commit하지 못합니다.
|
Lease를 잃은 process는 fencing 검사에서 commit하지 못합니다.
|
||||||
`InMemoryTurnStateStore`는 checkpoint를 별도로 복제하지 않고 rollback 대상인
|
`InMemoryTurnStateStore`는 checkpoint를 별도로 복제하지 않고 rollback 대상인
|
||||||
`InMemoryTurnWorld`에서 읽습니다. 따라서 flush 실패 뒤 다음 run도 복원된
|
`InMemoryTurnWorld`에서 읽습니다. 따라서 flush 실패 뒤 다음 run도 복원된
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# 경험자를 위한 시스템 읽기
|
||||||
|
|
||||||
|
웹 개발이나 컴퓨터공학 배경이 있고, 이 저장소의 책임과 상태 전이를 빠르게
|
||||||
|
이해하려는 독자를 위한 안내입니다. 웹 용어가 낯설다면 [기초 안내](./first-steps.md)를
|
||||||
|
먼저 읽으세요.
|
||||||
|
|
||||||
|
## 세 가지 흐름으로 나누기
|
||||||
|
|
||||||
|
| 흐름 | 시작과 끝 | 핵심 질문 |
|
||||||
|
| ----------- | -------------------------------------------------------- | --------------------------------------------------------- |
|
||||||
|
| 사용자 요청 | 브라우저 → API → 저장 또는 엔진 입력 → 응답 | actor, 입력 검증, 중복 요청, 공개 범위는 누가 결정하는가? |
|
||||||
|
| 게임 진행 | 스케줄 → 장수 턴·월간 처리 → 저장 → 알림 | 실행 순서, 난수 소비, 실패 후 복원이 보존되는가? |
|
||||||
|
| 운영 | 관리자 요청 → 작업 원장 → 빌드·프로세스 전환 → 준비 확인 | 게임 상태와 배포 상태를 혼동하지 않는가? |
|
||||||
|
|
||||||
|
HTTP 요청 하나가 게임의 한 턴인 것은 아닙니다. 조회, 예약 수정, 즉시 동작,
|
||||||
|
시간에 따라 실행하는 턴은 서로 다른 수명주기를 가집니다. [전체 구조](../architecture/overview.md)에서
|
||||||
|
구성요소를 본 뒤 [요청·턴·저장](./request-turn-persistence.md)에서 경계를 읽으세요.
|
||||||
|
|
||||||
|
## 조회: 인증한 사람과 보여 줄 대상을 분리합니다
|
||||||
|
|
||||||
|
`app/game-api/src/server.ts`, `trpc.ts`, `router.ts`에서 transport와 인증 context,
|
||||||
|
procedure 조립을 따라갑니다. Gateway 계정과 게임 장수는 같은 식별자가 아닙니다.
|
||||||
|
클라이언트가 보낸 장수 번호만으로 소유권을 결정하지 않습니다.
|
||||||
|
|
||||||
|
구체적인 예는 `app/game-api/src/router/world/index.ts`의 `getCurrentCity`입니다.
|
||||||
|
자신의 위치, 같은 국가 장수의 위치, 소유 도시, 첩보와 인접 여부를 조합해
|
||||||
|
정보 공개 수준을 정합니다. 응답에 없는 비밀 값을 프론트엔드에서 가리는 방식이
|
||||||
|
아닙니다. 데이터 전달 객체(DTO)는 저장 행 전체와 다를 수 있습니다.
|
||||||
|
|
||||||
|
## 변경: 입력 원장과 결과의 원자성을 읽습니다
|
||||||
|
|
||||||
|
`InputEvent`는 받아들인 변경 요청과 처리 상태를 기록하는 PostgreSQL 모델입니다.
|
||||||
|
실제 `InputEventTarget` 값은 `API`와 `ENGINE`입니다. 엔진을 설명할 때 쓰는
|
||||||
|
“daemon”은 프로세스 역할이며 DB enum 값이 아닙니다.
|
||||||
|
|
||||||
|
API 쪽 경계는 `app/game-api/src/inputEventBoundary.ts`입니다. request ID만
|
||||||
|
같다고 모든 요청을 동일시하지 않습니다. actor, event type, payload identity와
|
||||||
|
상태를 함께 읽고 재시도·충돌·완료 결과 재사용을 구분합니다. 엔진 쪽 입력은
|
||||||
|
`app/game-api/src/daemon/`에서 따라갑니다.
|
||||||
|
|
||||||
|
예약 변경이 저장된 뒤 실제 턴 실행 조건은 다시 달라질 수 있습니다. request
|
||||||
|
acceptance 검증과 command execution 검증을 하나로 합치지 않습니다. API에서
|
||||||
|
완료된 변경과 엔진이 메모리에 보유한 세계 사이의 동기화도 확인해야 합니다.
|
||||||
|
|
||||||
|
## 엔진: 단일 소유자라도 장애 경계는 필요합니다
|
||||||
|
|
||||||
|
`createTurnDaemonRuntime()`의 조립부터 읽으면 loader, registry, AI, 월간 handler,
|
||||||
|
저장 hook이 연결됩니다. 계산 중 상태는 `InMemoryTurnWorld`, 저장과 실패 복원은
|
||||||
|
`EngineStateManager`, 실행 순서는 `TurnDaemonLifecycle`에서 추적합니다.
|
||||||
|
|
||||||
|
**Lease**는 일정 기간 엔진 소유권을 인정하는 임대입니다. **Fencing token**은
|
||||||
|
소유권 세대를 구분하여, 멈췄다가 돌아온 과거 프로세스가 새 소유자의 상태를
|
||||||
|
덮어쓰지 못하게 합니다. “프로세스가 한 개일 것”이라는 운영 가정만으로 대체할 수
|
||||||
|
없습니다. DB 저장 실패에는 메모리 rollback도 필요합니다.
|
||||||
|
|
||||||
|
난수도 입력 상태의 일부입니다. seed가 같아도 분기·정렬·호출 횟수가 바뀌면
|
||||||
|
이후 결과가 달라집니다. 도메인 계산은 [행동 모듈](../architecture/action-module-protocol.md),
|
||||||
|
비교 범위는 [차등 검증](../architecture/turn-state-differential-testing.md)을 읽으세요.
|
||||||
|
|
||||||
|
## 시간을 하나의 Date로 생각하지 않습니다
|
||||||
|
|
||||||
|
게임 진행 시각과 인증 만료·lease 같은 현실 시각은 목적이 다릅니다. 일시정지나
|
||||||
|
턴 간격 변경이 있다고 로그인 만료와 소유권 heartbeat를 같은 방식으로 옮길 수는
|
||||||
|
없습니다. [시간 도메인](../architecture/time-domains.md), [게임 시계](../architecture/game-clock.md),
|
||||||
|
[복구 절차](./game-clock-recovery.md) 순으로 읽으면 계산과 운영 경계가 이어집니다.
|
||||||
|
|
||||||
|
## 모듈과 프로세스도 다릅니다
|
||||||
|
|
||||||
|
`packages/logic`은 게임 계산을 소유하며 DB·파일·네트워크 I/O를 직접 하지 않습니다.
|
||||||
|
필요한 외부 동작을 interface인 port로 표현하고, app/infra 쪽 adapter를 주입합니다.
|
||||||
|
이 구분은 단위 검증과 런타임 저장 경계를 분리하기 위한 것입니다.
|
||||||
|
|
||||||
|
`game-api`가 `game-engine`의 일부 loader를 import한다고 API 안에서 턴 데몬을
|
||||||
|
실행한다는 뜻은 아닙니다. 공개 subpath와 프로세스 entrypoint를 구분하세요.
|
||||||
|
프론트엔드의 backend router 타입 참조도 서버 실행 코드를 브라우저에 넣는 것과
|
||||||
|
다릅니다. [패키지 경계](../architecture/package-boundaries.md)는 이 규칙의 기준입니다.
|
||||||
|
|
||||||
|
## 다음 조사 위치를 선택하기
|
||||||
|
|
||||||
|
- 화면 상태·재접속 문제: frontend store → 조회 응답 → [실시간 변경 원장](../architecture/realtime-change-journal.md)
|
||||||
|
- 새 게임 규칙: [도메인 조립](./domain-and-classes.md) → 명령·constraint → 엔진 결과 적용·flush
|
||||||
|
- 설정에 따른 차이: [시나리오 합성](../architecture/scenario-composition.md) → 선택한 resource → 실제 loader
|
||||||
|
- 계정·배포 문제: [런타임](../architecture/runtime.md) → [릴리스 운영](../release-operations.md)
|
||||||
|
- 관리자 조사 기록: [플레이 감사 운영](../play-audit-operations.md) → 수집 정책·snapshot·조회
|
||||||
|
|
||||||
|
코드 검증은 [테스트 정책](../testing-policy.md)을 따릅니다. 순수 계산 테스트,
|
||||||
|
실제 DB 저장 검증, 브라우저 검증은 서로 다른 질문에 답합니다. Ref는 계승 규칙의
|
||||||
|
비교 기준이며, 별도로 결정된 Core 기능까지 예전 구현으로 되돌리는 기준은 아닙니다.
|
||||||
+12
-10
@@ -3,8 +3,8 @@ layout: home
|
|||||||
|
|
||||||
hero:
|
hero:
|
||||||
name: core2026 핸드북
|
name: core2026 핸드북
|
||||||
text: 코드와 게임 동작을 연결합니다
|
text: 처음 배우는 게임, 함께 이해하는 코드
|
||||||
tagline: 런타임, 저장 경계, 호환 검증과 플레이 방법을 현재 소스 구조에 맞춰 설명합니다.
|
tagline: 게임이 처음인 플레이어부터 프로그래밍 입문자와 경험자까지, 필요한 순서로 읽는 안내서입니다.
|
||||||
actions:
|
actions:
|
||||||
- theme: brand
|
- theme: brand
|
||||||
text: 개발자 핸드북
|
text: 개발자 핸드북
|
||||||
@@ -30,17 +30,19 @@ features:
|
|||||||
|
|
||||||
## 문서 안내
|
## 문서 안내
|
||||||
|
|
||||||
개발자는 [개발자 핸드북](./developer/index.md)과
|
게임이 처음이라면 [삼국지 모의전투 첫 안내](./user/index.md)부터 읽으세요.
|
||||||
[아키텍처 개요](./architecture/overview.md)에서 시작해 주세요. 플레이어는
|
프로그래밍 기초만 안다면 [기초 구조 안내](./developer/first-steps.md), 개발 경험이
|
||||||
[시간과 턴](./user/time-and-turns.md)과
|
있다면 [경험자를 위한 시스템 읽기](./developer/system-walkthrough.md)에서 시작합니다.
|
||||||
[커맨드 목록](./user/command-catalog.generated.md)을 확인해 주세요. Profile과
|
[용어 사전](./user/glossary.md)은 국가 메시지와 게시판의 줄임말을 풀어 줍니다.
|
||||||
|
|
||||||
|
Profile과
|
||||||
Gateway 배포는 [릴리스 운영 매뉴얼](./release-operations.md)을 따라 주세요.
|
Gateway 배포는 [릴리스 운영 매뉴얼](./release-operations.md)을 따라 주세요.
|
||||||
[Gateway와 게임 공통 메뉴 설정](./runtime-navigation.md)은 코드 재빌드 없이
|
[Gateway와 게임 공통 메뉴 설정](./runtime-navigation.md)은 코드 재빌드 없이
|
||||||
상단 링크와 dropdown을 바꾸는 JSON 형식과 복구 경계를 설명합니다.
|
상단 링크와 dropdown을 바꾸는 JSON 형식과 복구 경계를 설명합니다.
|
||||||
관리자 화면의 메뉴와 권한·운영 경계는
|
관리자 화면의 메뉴와 권한·운영 경계는
|
||||||
[관리자 콘솔](./admin-console.md)에서 확인할 수 있습니다.
|
[관리자 콘솔](./admin-console.md)에서 확인할 수 있습니다.
|
||||||
[프로필별 플레이 감사 설계](./design/play-audit.md)는 현재 미구현인 국가·장수·도시·
|
[플레이 감사 운영](./play-audit-operations.md)은 현재 제공하는 관리자 조사 기능과
|
||||||
외교·NPC 감사와 질문별 조사 도구의 구현 기준 및 DB 비용 검토를 정의합니다.
|
수집 범위를 설명합니다. [설계](./design/play-audit.md)는 요구사항과 비용·배경의 참고 문서입니다.
|
||||||
게임 진행 시각과 운영 벽시계의 경계는
|
게임 진행 시각과 운영 벽시계의 경계는
|
||||||
[게임 시계](./architecture/game-clock.md)에 설명합니다.
|
[게임 시계](./architecture/game-clock.md)에 설명합니다.
|
||||||
[패키지와 파일 경계](./architecture/package-boundaries.md)는 source import와
|
[패키지와 파일 경계](./architecture/package-boundaries.md)는 source import와
|
||||||
@@ -52,8 +54,8 @@ Gateway 배포는 [릴리스 운영 매뉴얼](./release-operations.md)을 따
|
|||||||
|
|
||||||
- `architecture/`: 현재 runtime, action module, scenario와 차등 검증 계약
|
- `architecture/`: 현재 runtime, action module, scenario와 차등 검증 계약
|
||||||
- `developer/`: 파일 위치, 도메인 조립, 요청·저장 흐름
|
- `developer/`: 파일 위치, 도메인 조립, 요청·저장 흐름
|
||||||
- `user/`: 화면, 시간, 국가 기능과 생성된 command catalog
|
- `user/`: 게임 입문, 장수·내정·전쟁·외교, 게시판 용어와 명령 참고서
|
||||||
- `design/`: 확정한 신규 기능의 구현 목표·비용·검증 계약과 미구현 상태
|
- `design/`: 기능의 구현 목표·비용·검증 계약과 설계 배경
|
||||||
- 루트 문서: 통합 테스트, Chromium 비교, Caddy, DB 이관과 운영 절차
|
- 루트 문서: 통합 테스트, Chromium 비교, Caddy, DB 이관과 운영 절차
|
||||||
|
|
||||||
작업 이력은 상위 작업공간의 `report/`에 보존합니다. ref PHP와 core2026의
|
작업 이력은 상위 작업공간의 `report/`에 보존합니다. ref PHP와 core2026의
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
# 커맨드와 실행 시기
|
# 커맨드와 실행 시기
|
||||||
|
|
||||||
|
커맨드는 장수나 국가에 내리는 **행동 명령**입니다. 처음에는 전체 목록을 외우지
|
||||||
|
말고 지금 신분과 목적에 맞는 절을 읽으세요. 전쟁 약어는 [모출·모훈사출출 안내](./war-and-orders.md)에 있습니다.
|
||||||
|
|
||||||
## 화면의 상태 표시
|
## 화면의 상태 표시
|
||||||
|
|
||||||
서버는 내 장수, 도시, 국가와 현재 시점으로 각 커맨드를 사전 평가합니다.
|
서버는 내 장수, 도시, 국가와 현재 시점으로 각 커맨드를 사전 평가합니다.
|
||||||
|
|
||||||
| 표시 | 의미 |
|
| 표시 | 의미 |
|
||||||
| -------------- | ----------------------------------------------------------------------------- |
|
| -------------- | --------------------------------------------------------------------------------- |
|
||||||
| 사용 가능 | 현재 알려진 조건을 만족합니다. 실행 시 다시 검사합니다. |
|
| 사용 가능 | 현재 알려진 조건을 만족합니다. 실행 시 다시 검사합니다. |
|
||||||
| 대상 선택 필요 | 도시·장수·국가·수량 같은 입력을 고르면 판정할 수 있습니다. |
|
| 대상 선택 필요 | 도시·장수·국가·수량 같은 입력을 고르면 판정할 수 있습니다. |
|
||||||
| 사용 불가 | 현재 확정된 조건을 만족하지 않으며 이유가 함께 표시됩니다. |
|
| 사용 불가 | 현재 확정된 조건을 만족하지 않으며 이유가 함께 표시됩니다. |
|
||||||
| 정보 부족 | 사전 화면에 없는 실행 context가 필요합니다. 실제 실행 성공을 뜻하지 않습니다. |
|
| 정보 부족 | 사전 화면에 없는 실행 시점의 정보가 필요합니다. 실제 실행 성공을 뜻하지 않습니다. |
|
||||||
|
|
||||||
전체 key와 화면 이름은 [자동 생성 커맨드 목록](./command-catalog.generated.md)에 있습니다.
|
전체 명령 이름과 내부 식별자는 [자동 생성 커맨드 목록](./command-catalog.generated.md)에 있습니다.
|
||||||
|
|
||||||
## 시기별로 보는 장수 커맨드
|
## 시기별로 보는 장수 커맨드
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@
|
|||||||
|
|
||||||
### 외교 상태에 따른 명령
|
### 외교 상태에 따른 명령
|
||||||
|
|
||||||
선전포고는 기본 구현에서 scenario 시작 연도보다 적어도 1년 지난 뒤에 허용되며, 대상 국가와의 상태도
|
선전포고는 기본 구현에서 시나리오 시작 연도보다 적어도 1년 지난 뒤에 허용되며, 대상 국가와의 상태도
|
||||||
맞아야 합니다. 종전·불가침·불가침 파기 제안은 현재 외교 관계와 최소 기간을 검사합니다. 상대가 제안을
|
맞아야 합니다. 종전·불가침·불가침 파기 제안은 현재 외교 관계와 최소 기간을 검사합니다. 상대가 제안을
|
||||||
수락하는 즉시 행동은 일반 예약 국가 턴과 별도의 승인 흐름으로 처리됩니다.
|
수락하는 즉시 행동은 일반 예약 국가 턴과 별도의 승인 흐름으로 처리됩니다.
|
||||||
|
|
||||||
@@ -72,17 +75,17 @@
|
|||||||
|
|
||||||
### 특수 병종 연구
|
### 특수 병종 연구
|
||||||
|
|
||||||
`event_*연구` 명령은 scenario가 해당 명령을 profile에 넣고 필요한 연구 flag가 아직 없을 때 사용할 수
|
특수 병종 연구 명령은 시나리오가 해당 명령을 서버 설정에 넣고 해당 연구가 아직 완료되지 않았을 때 사용할 수
|
||||||
있습니다. 군주와 국고 조건을 검사하며 11턴 또는 23턴을 연속으로 쌓은 뒤 비용을 지불하고 병종 사용
|
있습니다. 군주와 국고 등의 조건을 만족한 상태에서 정해진 횟수만큼 연속으로 실행해야 합니다.
|
||||||
권한을 얻습니다. 소스의 선행 턴 값 11·23은 완료 턴을 포함하지 않으므로 화면에서 필요한 전체 실행은
|
기본 연구는 완료 턴까지 포함해 12턴 또는 24턴이 필요합니다. 중간에 다른 명령을 넣기 전에
|
||||||
각각 12턴·24턴입니다.
|
진행 조건과 남은 횟수를 확인하세요.
|
||||||
|
|
||||||
## 보호 기간과 연도
|
## 보호 기간과 연도
|
||||||
|
|
||||||
scenario의 `startYear`와 `openingPartYear`가 초반 제한의 기준입니다. 다음은 대표적인 동작이며 서버별
|
시나리오의 시작 연도와 초반 보호 기간이 제한의 기준입니다. 다음은 대표적인 동작이며 서버별
|
||||||
상수에 따라 실제 연도가 달라집니다.
|
설정에 따라 실제 연도가 달라집니다.
|
||||||
|
|
||||||
- 출병은 예약 사전 판단과 실제 실행 모두 보호 기간을 고려합니다.
|
- 출병의 예약 가능 시점과 실제 출병 가능 시점은 다를 수 있습니다. 미리 예약했더라도 실행 차례가 보호 기간 안이면 실패할 수 있습니다.
|
||||||
- 방랑, 의병 모집, 몰수 같은 일부 국가 변화도 초반에는 제한됩니다.
|
- 방랑, 의병 모집, 몰수 같은 일부 국가 변화도 초반에는 제한됩니다.
|
||||||
- 기술 연구의 허용 기술 단계와 임관 인원 제한은 경과 연도에 따라 넓어질 수 있습니다.
|
- 기술 연구의 허용 기술 단계와 임관 인원 제한은 경과 연도에 따라 넓어질 수 있습니다.
|
||||||
- 선전포고는 시작 직후가 아니라 상대 연도 1 이상에서 가능합니다.
|
- 선전포고는 시작 직후가 아니라 상대 연도 1 이상에서 가능합니다.
|
||||||
@@ -94,5 +97,5 @@ scenario의 `startYear`와 `openingPartYear`가 초반 제한의 기준입니다
|
|||||||
1. 메인 로그에서 실패 이유를 확인합니다.
|
1. 메인 로그에서 실패 이유를 확인합니다.
|
||||||
2. 자원·병력, 소속 도시와 보급, 직책, 대상과 외교 상태를 다시 확인합니다.
|
2. 자원·병력, 소속 도시와 보급, 직책, 대상과 외교 상태를 다시 확인합니다.
|
||||||
3. 여러 턴 뒤 실행될 명령이면 앞선 예턴이 상태를 바꾸는지 살펴봅니다.
|
3. 여러 턴 뒤 실행될 명령이면 앞선 예턴이 상태를 바꾸는지 살펴봅니다.
|
||||||
4. command table을 새로 불러와 대상 목록과 가능 상태를 갱신합니다.
|
4. 명령 입력창을 다시 열어 대상 목록과 가능 상태를 갱신합니다.
|
||||||
5. 동일 요청을 반복 전송하지 말고 화면이 최신 revision을 받은 뒤 다시 저장해 주세요.
|
5. 동일 요청을 반복 전송하지 말고 화면이 최신 예약 목록을 불러온 뒤 다시 저장해 주세요.
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# 국가 재정과 외교
|
||||||
|
|
||||||
|
일반 장수도 세금과 외교의 뜻을 알면 국가 지시를 이해하기 쉬워집니다. 설정을
|
||||||
|
직접 바꾸는 일은 군주·담당 직책의 권한이 필요합니다.
|
||||||
|
|
||||||
|
## 세율과 지급률은 다릅니다
|
||||||
|
|
||||||
|
**세율**은 국가가 도시에서 거둘 수입과 주민·내정 변화에 영향을 줍니다.
|
||||||
|
**지급률**은 장수에게 나눠 주는 봉급 쪽 설정입니다. 세율 30%를 “군주가
|
||||||
|
내 돈의 30%를 가져간다”거나, 지급률 200%를 “국가 수입이 두 배”라는 뜻으로
|
||||||
|
읽으면 안 됩니다. 도시의 인구·농업·상업·민심과 국가 설정을 함께 봐야 합니다.
|
||||||
|
|
||||||
|
## 왜 세율 5%와 30%를 이야기하나요?
|
||||||
|
|
||||||
|
세율을 낮추면 당장 걷는 수입은 적지만 도시를 회복·성장시키기 좋고, 높이면
|
||||||
|
더 걷는 대신 도시 상태에 부담을 줍니다. 다음은 현재 기본 반기 처리의 비교입니다.
|
||||||
|
서버별 특수 효과, 도시 최대치, 반올림과 별도 인구 증가가 최종 수치를 바꿀 수 있습니다.
|
||||||
|
|
||||||
|
| 기본 처리에서 비교 | 5% | 30% |
|
||||||
|
| --------------------------------------- | ------- | ------------- |
|
||||||
|
| 같은 도시 상태에서 세율에 비례하는 수입 | 낮음 | 높음 |
|
||||||
|
| 내정치의 세율 보정 | +7.5% | −5% |
|
||||||
|
| 민심 변화 | +15 | −10 |
|
||||||
|
| 인구의 세율 보정 방향 | 증가 쪽 | 비례 증가분 0 |
|
||||||
|
|
||||||
|
내정은 이 보정 전에 기본 1% 감소도 거칩니다. 따라서 5%일 때 최종 내정이
|
||||||
|
무조건 7.5% 증가하는 것은 아닙니다. 30%에서 인구 비례 증가분이 0이어도 별도
|
||||||
|
기본 증가량이 있으므로 “인구가 전혀 늘지 않는다”는 뜻이 아닙니다.
|
||||||
|
|
||||||
|
나라가 가난한지, 전쟁 중인지, 도시가 충분히 성장했는지에 따라 판단이 달라집니다.
|
||||||
|
[10기 회고](https://sam.hided.net/xe/community/15146)에는 5% 세율과 높은 지급률로도
|
||||||
|
재정이 넉넉했던 사례가 있지만, 당시 나라 사정과 규칙에 따른 경험담입니다.
|
||||||
|
이를 모든 기수에 통하는 추천값으로 보지 마세요.
|
||||||
|
|
||||||
|
## 바꾸는 시점도 중요합니다
|
||||||
|
|
||||||
|
현재 구현은 월이 바뀔 때 적용할 세율을 따로 확정해 수입·반기 처리에 사용합니다.
|
||||||
|
화면에서 변경한 값이 이미 처리 중인 정산에 소급 적용되지는 않습니다.
|
||||||
|
월 변경 직전 접속해서 무조건 원하는 결과를 얻을 수 있다고 가정하지 마세요.
|
||||||
|
|
||||||
|
세율 담당자는 현재 설정, 다음 정산, 적용 결과와 국가 재정을 함께 확인합니다.
|
||||||
|
일반 장수라면 임의 변경보다 “모병비가 부족하다”, “이 도시의 인구가 줄었다”처럼
|
||||||
|
현재 문제를 알리는 편이 좋습니다. 계산·적용 시점의 코드 근거는
|
||||||
|
[자료 안내](./sources.md)에 모았습니다.
|
||||||
|
|
||||||
|
## 불가침·선포·개전·종전
|
||||||
|
|
||||||
|
**불가침**은 정한 기간 서로 공격하지 않는 관계입니다. **선포**는 선전포고를
|
||||||
|
줄인 말이고, **개전**은 실제 전쟁이 시작되는 시점입니다. 선포가 처리됐다고
|
||||||
|
그 즉시 아무 도시로나 출병할 수 있다고 가정하지 마세요. 외교 화면의 관계·시각과
|
||||||
|
출병 조건을 확인합니다. **종전**은 전쟁을 끝내는 합의입니다.
|
||||||
|
|
||||||
|
제안을 보낸 것과 상대가 수락한 것은 다릅니다. 합의가 필요하다면 수락 결과까지
|
||||||
|
확인하세요. 국가가 멸망하거나 직책·관계가 바뀌면 이전 제안을 처리할 수 없을 수도
|
||||||
|
있습니다.
|
||||||
|
|
||||||
|
## 최후 2국 외교는 자동 승리 규칙이 아닙니다
|
||||||
|
|
||||||
|
**최후 2국**은 마지막에 두 나라만 남은 상황을 뜻하기도 하고, 두 나라가 마지막까지
|
||||||
|
서로를 남겨 두자는 장기 외교 약속을 뜻하기도 합니다. 게시판이나 대화에서 어느
|
||||||
|
의미로 쓰였는지 먼저 구분해야 합니다.
|
||||||
|
|
||||||
|
“최후 2국 하자” 한 문장만으로는 원조, 다른 나라와의 전쟁 참여, 땅 교환과 길
|
||||||
|
열어 주기, 약속 종료 시점이 정해지지 않습니다.
|
||||||
|
[외교 옵션 건의](https://sam.hided.net/xe/devel/12712)는 이런 포괄적 표현의 모호함을
|
||||||
|
지적했습니다. 이것은 당시의 제안 글이며, 그 글의 모든 문장이 현재 서버의 공식
|
||||||
|
운영 규칙이라는 뜻은 아닙니다.
|
||||||
|
|
||||||
|
실제로 외교를 맡았다면 기간, 공격 관계, 원조, 땅·통로, 상황이 달라졌을 때의
|
||||||
|
처리를 구체적으로 협의하고 게임 안 외교 문서에 남깁니다. 게임의 불가침 설정과
|
||||||
|
사람 사이의 추가 약속은 구별해서 확인해야 합니다. 최후 2국이라는 약속만으로
|
||||||
|
두 나라가 공동 통일하거나 서버가 자동으로 약속을 집행하지는 않습니다.
|
||||||
|
|
||||||
|
처음 참가한 장수라면 국가가 합의한 공격 목표를 따르고, 다른 나라에 나라 전체를
|
||||||
|
대표하는 약속을 하기 전에 담당자에게 알려 주세요.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# 장수와 도시, 내정
|
||||||
|
|
||||||
|
[첫 안내](./index.md)를 읽었다면 이제 내 화면의 숫자를 살펴봅시다.
|
||||||
|
모든 수치를 외우기보다 “어떤 일을 하려는데 무엇이 부족한가”를 찾으면 됩니다.
|
||||||
|
|
||||||
|
## 장수의 능력과 자원
|
||||||
|
|
||||||
|
| 항목 | 처음에는 이렇게 이해하세요 |
|
||||||
|
| --------- | --------------------------------------------------- |
|
||||||
|
| 통솔 | 이끌 수 있는 병력 규모와 관련된 능력 |
|
||||||
|
| 무력 | 무장을 중심으로 한 전투와 관련된 능력 |
|
||||||
|
| 지력 | 지장을 중심으로 한 전투·내정과 관련된 능력 |
|
||||||
|
| 금 | 모병, 장비, 내정 등 행동에 쓰는 돈 |
|
||||||
|
| 쌀·군량 | 병력을 운용하고 전쟁을 이어 가는 식량 |
|
||||||
|
| 병력 | 현재 이끌고 있는 병사의 수 |
|
||||||
|
| 훈련·사기 | 지금 병력이 얼마나 싸울 준비가 되었는지 나타내는 값 |
|
||||||
|
| 숙련도 | 병종별 전투 경험과 관련된 값 |
|
||||||
|
| 특기·장비 | 특정 행동이나 전투에 영향을 주는 추가 효과 |
|
||||||
|
|
||||||
|
능력치만 비슷하다고 전투력이 같지는 않습니다. 병종, 병력 수, 훈련·사기,
|
||||||
|
숙련도, 특기, 장비, 부상과 전투 상황이 함께 작용합니다. 모병으로 병사를 채우는
|
||||||
|
일과 훈련·사기로 전투 준비를 하는 일도 다릅니다.
|
||||||
|
|
||||||
|
**무장**은 무력을, **지장**은 지력을 중심으로 키우는 장수를 가리키는 말입니다.
|
||||||
|
국가가 원하는 역할과 접속 가능한 시간을 먼저 상의하면 능력 배분과 할 일을 정하기
|
||||||
|
쉽습니다. 전쟁에 자주 접속하지 못해도 내정과 예약 행동으로 기여할 수 있습니다.
|
||||||
|
|
||||||
|
## 도시는 나라의 생활 기반입니다
|
||||||
|
|
||||||
|
도시는 병사를 모을 주민, 수입을 만드는 농업·상업, 치안과 방어 시설을 가집니다.
|
||||||
|
이것들을 돌보는 일을 **내정**이라고 합니다. 지도에 우리 색의 도시가 많아도
|
||||||
|
주민과 내정이 무너지면 전쟁을 오래 이어 가기 어렵습니다.
|
||||||
|
|
||||||
|
| 행동 | 목적 | 먼저 볼 것 |
|
||||||
|
| ------------------- | ------------------------------- | ------------------------------------ |
|
||||||
|
| 농지 개간 | 농업을 올려 식량 생산 기반 마련 | 농업이 이미 최대인지 |
|
||||||
|
| 상업 투자 | 상업을 올려 금 수입 기반 마련 | 상업이 이미 최대인지 |
|
||||||
|
| 치안 강화 | 도시의 치안 개선 | 현재 치안과 국가 지시 |
|
||||||
|
| 정착 장려 | 주민 확보 | 현재 인구와 최대 인구 |
|
||||||
|
| 성벽 보수·수비 강화 | 도시 방어 준비 | 전선이 될 도시인지, 부족한 방어 수치 |
|
||||||
|
| 기술 연구 | 국가의 기술 발전 | 허용 기술 단계와 필요한 자원 |
|
||||||
|
|
||||||
|
현재 도시와 소속 국가, 보급, 비용 등의 조건을 만족해야 합니다. 이미 최대인
|
||||||
|
항목만 계속 예약하면 원하는 성과를 얻지 못할 수 있으니 다른 도시나 일을
|
||||||
|
국가에 물어보세요. [명령별 조건](./commands-and-timing.md)에서 확인할 수 있습니다.
|
||||||
|
|
||||||
|
## 내 금·쌀과 국고는 다릅니다
|
||||||
|
|
||||||
|
국가가 부유해도 내 장수의 돈이 부족할 수 있습니다. 국고에서 장수에게 자원을
|
||||||
|
나누는 **포상**, 장수의 자원을 국가로 모으는 **몰수**, 나라 사이의 **원조**는
|
||||||
|
대상과 권한이 다릅니다. 전쟁 중 모병이 막혔다면 “돈이 없어요”보다
|
||||||
|
“어느 도시에서 어떤 병종을 몇 명 모으려는데 금이 부족합니다”라고 알려 주세요.
|
||||||
|
|
||||||
|
봉급과 세금은 [국가 재정](./economy-and-diplomacy.md)에서 이어 설명합니다.
|
||||||
|
장비 구매·경매·유니크 획득은 서로 다른 경로입니다. **유니크**는 특별한 장비를
|
||||||
|
부르는 말이며, 모든 서버에서 원하는 장비를 즉시 살 수 있다는 뜻은 아닙니다.
|
||||||
|
|
||||||
|
## 처음 막혔을 때
|
||||||
|
|
||||||
|
내정이 실패하면 현재 도시가 우리 땅인지, 보급이 되는지, 비용이 있는지, 수치가
|
||||||
|
이미 최대인지부터 봅니다. 전쟁으로 도시 주인이 바뀌거나 발령으로 위치가 달라졌다면
|
||||||
|
이전 예턴이 지금 상태에 맞지 않을 수 있습니다. 행동 기록의 실패 이유를 읽고
|
||||||
|
가까운 예약부터 다시 정하세요.
|
||||||
|
|
||||||
|
다음은 [시간과 턴](./time-and-turns.md)입니다.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# 국가 메시지와 게시판 용어 사전
|
||||||
|
|
||||||
|
모르는 말이 나오면 여기서 찾고, 자세한 행동은 연결한 안내를 읽으세요.
|
||||||
|
**기본 용어**는 화면·명령의 뜻, **커뮤니티 표현**은 이용자들이 줄여 부르는 말입니다.
|
||||||
|
전략 표현 자체가 별도 게임 기능이나 운영 규칙을 뜻하지는 않습니다.
|
||||||
|
조사한 글과 현재 코드의 구분은 [자료 안내](./sources.md)에 있습니다.
|
||||||
|
|
||||||
|
## 참가와 역할
|
||||||
|
|
||||||
|
| 말 | 뜻 |
|
||||||
|
| ------------- | ----------------------------------------------------------------------- |
|
||||||
|
| 삼모 | 삼국지 모의전투의 줄임말 |
|
||||||
|
| 기수·깃수 | 시작부터 종료까지 한 차례 진행하는 게임 |
|
||||||
|
| 천통 | 천하통일. 나라가 통일을 이루는 것 |
|
||||||
|
| 군주 | 국가를 이끄는 장수 |
|
||||||
|
| 수뇌 | 국가 운영을 맡는 주요 직책의 장수들을 묶어 부르는 말 |
|
||||||
|
| 사령턴·국가턴 | 권한을 가진 장수가 넣는 국가 명령의 예약 턴 |
|
||||||
|
| 무장·지장 | 무력 또는 지력을 중심으로 키우는 장수 |
|
||||||
|
| 무지장 | 무력과 지력을 함께 중심으로 둔 장수라는 표현 |
|
||||||
|
| 유저장 | 사람이 맡는 장수 |
|
||||||
|
| NPC·엔장 | 컴퓨터가 행동을 정하는 장수 |
|
||||||
|
| 빙의 | 허용된 기존 NPC를 이용자가 맡는 참가 방식 |
|
||||||
|
| 임관 | 나라에 들어가 소속 장수가 되는 것 |
|
||||||
|
| 랜임 | 무작위 임관. 나라를 직접 고르지 않는 임관 |
|
||||||
|
| 지정임관 | 특정 나라를 골라 들어가는 임관을 가리키는 말 |
|
||||||
|
| 선약 | 함께 참가하기로 미리 한 약속. 서버의 가입 제한보다 우선하는 기능은 아님 |
|
||||||
|
| 재야 | 소속 국가가 없는 장수 상태 |
|
||||||
|
| 하야 | 관직·소속을 내려놓고 나라를 떠나는 명령 |
|
||||||
|
| 방랑 | 도시를 가진 정착 국가와 다른 국가 상태. 단순 장수 이동과 구분 |
|
||||||
|
| 열전 | 기수가 끝난 뒤 활동과 이야기를 회고한 글, 또는 장수의 이력 |
|
||||||
|
|
||||||
|
역할과 참가의 실제 사용례는 [97기 장수열전](https://sam.hided.net/xe/community/78162)과
|
||||||
|
[43기 국가열전](https://sam.hided.net/xe/community/25102)에서 확인했습니다.
|
||||||
|
|
||||||
|
## 소통과 접속
|
||||||
|
|
||||||
|
| 말 | 뜻 |
|
||||||
|
| ---------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 예턴 | 예약 턴. 접속하지 않는 동안에도 순서대로 실행할 행동 목록 |
|
||||||
|
| 실접 | 실제로 접속해 상황을 보며 조작하는 것 |
|
||||||
|
| 국메·국가 메시지 | 같은 나라에 보내는 메시지 |
|
||||||
|
| 갠메·개인 메시지 | 특정 사람에게 보내는 메시지. 글에 따라 갠매로도 씀 |
|
||||||
|
| 전메·전체 메시지 | 전체 이용자에게 보이는 메시지 |
|
||||||
|
| 국톡 | 국가 구성원의 외부 단체 대화방을 가리키는 말 |
|
||||||
|
| 국방 | 문맥상 국가방침의 줄임말일 수 있음. “국가 방어”라는 보통 뜻과 구분 |
|
||||||
|
| 호출 | 상황에 맞춰 접속·행동을 부탁하는 말 |
|
||||||
|
| 삭턴 | 화면에서는 장수 삭제까지의 남은 턴과 관련된 값. 대화의 “삭턴 난다”는 명령 실패 등으로 행동 기회를 낭비한다는 뜻으로도 쓰임 |
|
||||||
|
| 벌점 | 활동 상태와 관련해 표시하는 값. 정확한 조건은 현재 화면 안내를 확인하고, 운영 제재와 같은 것으로 단정하지 않음 |
|
||||||
|
|
||||||
|
[뉴비 질문 모음](https://sam.hided.net/xe/community/51784)에는 삭턴·벌점 질문이,
|
||||||
|
[97기 회고](https://sam.hided.net/xe/community/78162)에는 실패를 피하려고 발령과 경로를
|
||||||
|
조정하는 맥락의 삭턴 표현이 나옵니다. 같은 단어라도 문맥을 확인하세요.
|
||||||
|
|
||||||
|
## 내정과 자원
|
||||||
|
|
||||||
|
| 말 | 뜻 |
|
||||||
|
| ----------- | --------------------------------------------------------------- |
|
||||||
|
| 내정 | 농업·상업·치안·인구·방어·기술 등 나라의 기반을 돌보는 행동 |
|
||||||
|
| 농상 | 농업과 상업을 함께 부르는 말 |
|
||||||
|
| 기연 | 기술 연구 |
|
||||||
|
| 금쌀 | 금과 쌀을 함께 부르는 말 |
|
||||||
|
| 국고·군량 | 국가의 돈·식량. 내 장수의 금·쌀과 구분 |
|
||||||
|
| 포상 | 국가 자원을 장수에게 지급하는 명령 |
|
||||||
|
| 몰수 | 장수 자원을 국가로 회수하는 명령 |
|
||||||
|
| 원조 | 나라 사이에 물자를 보내는 물자 원조 |
|
||||||
|
| 물조 | 물자조달. 장수가 자원을 마련하는 내정 명령으로 물자 원조와 다름 |
|
||||||
|
| 세율 | 수입과 주민·내정 변화에 영향을 주는 국가 설정 |
|
||||||
|
| 지급률 | 장수 봉급 지급과 관련된 국가 설정 |
|
||||||
|
| 유니크·유닉 | 특별한 장비를 부르는 말 |
|
||||||
|
| 유산 | 기수를 넘겨 활용하는 포인트와 그 기능. 현재 소지 금·쌀과 다름 |
|
||||||
|
| 템 | 아이템·장비 |
|
||||||
|
|
||||||
|
금·쌀이 어디에 쓰이는지는 [장수와 도시](./general-and-city.md), 세율 5%·30%의
|
||||||
|
차이는 [재정 안내](./economy-and-diplomacy.md)를 읽으세요. 기연·유니크·유산의
|
||||||
|
사용례는 [97기 회고](https://sam.hided.net/xe/community/78162)에 있습니다.
|
||||||
|
|
||||||
|
## 전쟁과 지도
|
||||||
|
|
||||||
|
| 말 | 뜻 |
|
||||||
|
| -------------- | ---------------------------------------------------------------------- |
|
||||||
|
| 모·징·훈·사·출 | 각각 모병·징병·훈련·사기 진작·출병 |
|
||||||
|
| 모출·모출출 | 모병 후 출병 한 번 또는 두 번을 예약하는 표현 |
|
||||||
|
| 모훈사출출 | 모병 → 훈련 → 사기 진작 → 출병 → 출병 |
|
||||||
|
| 징훈훈사사 | 징병 뒤 훈련 두 번과 사기 진작 두 번 |
|
||||||
|
| 풀훈사 | 훈련·사기를 충분히 채운 병력 상태 |
|
||||||
|
| 양파·모병양파 | 병력을 반복 보충하면서 수비를 이어 가는 방식 |
|
||||||
|
| 점사 | 특정 목표·시점에 공격을 집중하는 전술 |
|
||||||
|
| 턴 정렬 | 동료의 행동·공격 순서를 맞추는 것 |
|
||||||
|
| 병종 저격 | 상대에게 유리한 상성의 병종으로 싸우려는 것. 특기 “저격”과 구분 |
|
||||||
|
| 수비 켬끔 | 수비 참여를 켜고 끄며 전투 참여를 조절하는 플레이 |
|
||||||
|
| 땅따 | 땅따먹기. 특히 초반 공백지를 선점하는 확장 경쟁 |
|
||||||
|
| 공백지 | 어느 나라에도 속하지 않은 도시 |
|
||||||
|
| 접경·전선·후방 | 다른 나라와 닿는 곳·전쟁이 벌어지는 곳·그 뒤의 지역 |
|
||||||
|
| 깃 끊기 | 수도에서 이어지는 보급 연결을 끊는 전술 |
|
||||||
|
| 길막·길 열기 | 진출할 통로를 차지해 막거나, 지나갈 수 있도록 조정하는 것 |
|
||||||
|
| 수복 | 빼앗긴 도시를 되찾는 것 |
|
||||||
|
| 발령 | 국가 명령으로 장수·부대의 배치를 바꾸는 것 |
|
||||||
|
| 집합·집합장 | 부대원을 모으는 행동·이를 맡는 장수. 병사 한 명을 뜻하지 않음 |
|
||||||
|
| 쟁·연쟁 | 전쟁·연속되는 전쟁 |
|
||||||
|
| 다굴 | 여러 나라가 한 나라를 함께 공격하는 상황을 가리키는 표현 |
|
||||||
|
| 선포 | 선전포고 |
|
||||||
|
| 불가침 | 정한 기간 서로 공격하지 않는 관계 |
|
||||||
|
| 최후 2국 | 마지막 남은 두 나라 또는 그때까지 서로 남겨 두자는 외교 약속 |
|
||||||
|
| 의병 | 의병 모집으로 동원하는 전력. 일반 모병과 구분 |
|
||||||
|
| 필즉·백동 | 국가 전략 명령인 필사즉생·백성 동원의 줄임말 |
|
||||||
|
| 전금 | 전쟁 금지 방침을 줄여 부르는 말. 적용 대상·설정 범위를 확인 |
|
||||||
|
| 살상률·교환비 | 적에게 준 병력 손실과 내가 입은 손실을 비교하는 지표·표현. 승률과 다름 |
|
||||||
|
|
||||||
|
조합과 수비는 [전쟁 안내](./war-and-orders.md), 보급과 땅따는
|
||||||
|
[지도 안내](./map-and-supply.md), 최후 2국은 [외교 안내](./economy-and-diplomacy.md)에서
|
||||||
|
사례와 주의점을 설명합니다. [70기 전쟁 회고](https://sam.hided.net/xe/community/47834)는
|
||||||
|
양파·발령·다굴·전쟁 금지·필즉의 사용 맥락을,
|
||||||
|
[99기 국가열전](https://sam.hided.net/xe/community/79329)은 외교·의병·전금·백동의 맥락을
|
||||||
|
보여 줍니다. 사전의 설명은 원문을 그대로 옮긴 것이 아니라 처음 읽는 사람을 위한 풀이입니다.
|
||||||
|
|
||||||
|
물조의 역사적 사용례는 [물조 명성 관련 건의](https://sam.hided.net/xe/devel/2569)에 있습니다. 당시 보상 변경 제안을 현재 효과로 읽지 마세요.
|
||||||
+50
-29
@@ -1,37 +1,58 @@
|
|||||||
# 플레이어 가이드
|
# 삼국지 모의전투, 처음부터 시작하기
|
||||||
|
|
||||||
core2026에서는 지금 누르는 버튼이 즉시 모든 결과를 만드는 것이 아니라, 많은 행동을 **예턴**에 넣고 내
|
삼국지 모의전투는 **여러 사람이 장수 한 명씩을 맡아, 같은 나라를 키우고 다른 나라와
|
||||||
장수의 다음 턴에 실행합니다. 국가 커맨드도 같은 방식이지만 군주·직책과 국가 상태에 따라 이용 범위가
|
경쟁하는 웹 전쟁 게임**입니다. 삼국지 소설이나 프로그래밍을 몰라도 시작할 수 있습니다.
|
||||||
달라집니다.
|
혼자 나라의 모든 병사를 움직이는 게임과 달리, 동료들과 역할과 행동 시간을 맞추는
|
||||||
|
일이 중요합니다. 직접 싸우는 사람도, 돈과 식량을 마련하는 사람도 나라에 필요합니다.
|
||||||
|
|
||||||
## 처음 접속했을 때
|
## 내가 움직이는 것은 장수 한 명입니다
|
||||||
|
|
||||||
1. Gateway에서 로그인하고 열려 있는 서버 profile을 선택합니다.
|
**장수**는 게임 속 내 인물입니다. **국가**는 여러 장수가 함께 속하는 편이고,
|
||||||
2. 게임에 장수가 없다면 장수 생성·참가 화면에서 이름, 능력치 등 필요한 정보를 정합니다.
|
**도시**는 지도 위의 거점입니다. 장수는 한 도시에 머물며 일하거나 병사를 모으고,
|
||||||
3. 메인 화면에서 현재 도시·국가·자원·다음 턴 시각을 확인합니다.
|
다른 도시로 이동하거나 공격합니다. 컴퓨터가 움직이는 장수는 **NPC**, 이용자들은
|
||||||
4. 예턴 목록의 가까운 순서부터 실행할 커맨드를 지정합니다.
|
줄여서 **엔장**이라고 부릅니다.
|
||||||
5. 국가에 소속됐다면 국가 정보·도시·장수·외교 화면에서 상황을 확인합니다.
|
|
||||||
|
|
||||||
서버마다 scenario, 시작 연도, 턴 간격, 가입 방식, map, 병종과 허용 커맨드가 다를 수 있습니다. 이 문서는
|
나라들은 도시를 늘리고 전쟁·외교를 거쳐 통일을 목표로 합니다. 한 차례의 게임을
|
||||||
기본 구현을 설명하며 현재 화면의 가능/불가 표시와 운영 공지를 우선해 주세요.
|
**기수** 또는 **깃수**, 천하통일을 **천통**이라고 부릅니다. 한 기수가 끝나면 다음
|
||||||
|
기수에서는 새 상황에서 다시 시작합니다. 개인적으로는 전투·내정·성장·기록을 즐길
|
||||||
|
수도 있습니다. 처음부터 군주가 되어 나라를 운영할 필요는 없습니다.
|
||||||
|
|
||||||
## 어디서 무엇을 하나요?
|
## 버튼을 누른 순간 모두 실행되지는 않습니다
|
||||||
|
|
||||||
| 메뉴 | 용도 |
|
이 게임의 **턴**은 내 장수가 행동할 차례입니다. **예턴**은 앞으로 할 행동을
|
||||||
| -------------------------- | ---------------------------------------------- |
|
미리 적어 놓은 목록입니다. “농지 개간 → 농지 개간 → 이동”을 넣었다면 내 차례가
|
||||||
| 메인 | 장수·국가 예턴, 현재 상태, 최근 기록 |
|
올 때마다 앞에서부터 하나씩 실행합니다. 저장 직후 도시가 바뀌지 않아도 이상한
|
||||||
| 현재 도시·국가 정보 | 도시 내정치·보급·전선, 국가 자원·기술 |
|
것은 아닙니다. 실제 행동은 다음 턴에 일어납니다.
|
||||||
| 국가 장수·인사 | 소속 장수와 직책, 권한이 있으면 인사·방침 설정 |
|
|
||||||
| 외교 | 국가 관계와 제안·교전 상태 확인 |
|
|
||||||
| 부대 | 부대 생성·가입·관리 |
|
|
||||||
| 경매·토너먼트·베팅 | 해당 기간에 열린 참가·입찰·예측 기능 |
|
|
||||||
| 게시판·메시지 | 공개/국가 소통과 개인 메시지 |
|
|
||||||
| 연감·명장·왕조·과거 플레이 | 현재·과거 기록 열람 |
|
|
||||||
| 내 정보·설정 | 표시·알림 등 개인 설정 |
|
|
||||||
|
|
||||||
## 다음에 읽을 문서
|
로그아웃해도 예약한 행동은 진행됩니다. 다만 전쟁으로 길이 막히거나 자원이
|
||||||
|
부족해지면 실패할 수 있습니다. 접속할 때는 **현재 상태 → 최근 기록 → 다음 예턴**
|
||||||
|
순서로 확인하세요. [시간과 턴](./time-and-turns.md)에 예시가 있습니다.
|
||||||
|
|
||||||
- 예턴이 언제 실행되는지: [시간과 턴](./time-and-turns.md)
|
## 첫 접속에서 할 일
|
||||||
- 어떤 조건에서 커맨드를 쓸 수 있는지: [커맨드와 실행 시기](./commands-and-timing.md)
|
|
||||||
- 현재 소스에 등록된 전체 목록: [커맨드 전체 목록](./command-catalog.generated.md)
|
1. 로그인한 뒤 로비에서 참가할 게임 서버를 고릅니다. 서버마다 지도·속도·규칙이
|
||||||
- 국가 직책과 부가 기능: [국가 운영과 주요 기능](./nation-and-features.md)
|
다를 수 있으니 현재 공지와 시작 시각을 읽습니다.
|
||||||
|
2. 장수 생성·참가 화면의 안내를 따릅니다. 이름과 능력치를 정하는 방식, 기존
|
||||||
|
NPC를 맡는 **빙의** 가능 여부는 서버 설정에 따라 다릅니다.
|
||||||
|
3. 메인에서 내 **국가, 현재 도시, 금, 쌀, 다음 턴 시각**을 찾습니다.
|
||||||
|
4. 나라가 없다면 **임관**으로 나라에 들어갑니다. 무작위 임관만 허용하는 서버도
|
||||||
|
있습니다. 나라가 있다면 국가 공지와 메시지를 먼저 읽습니다.
|
||||||
|
5. “처음입니다. 어느 도시에서 어떤 일을 하면 될까요?”라고 국가 메시지로 물어보고,
|
||||||
|
가까운 예턴부터 입력합니다. 저장된 목록이 원하는 순서인지 확인합니다.
|
||||||
|
6. 다음 접속 때 행동 기록에서 성공·실패 이유를 읽고 남은 예턴을 고칩니다.
|
||||||
|
|
||||||
|
## 어디까지 읽으면 되나요?
|
||||||
|
|
||||||
|
| 지금 필요한 것 | 읽을 문서 |
|
||||||
|
| ------------------------------- | ------------------------------------------------- |
|
||||||
|
| 내 장수와 돈·쌀·도시가 무엇인지 | [장수와 도시, 내정](./general-and-city.md) |
|
||||||
|
| 행동을 예약하고 결과를 확인하기 | [시간과 턴](./time-and-turns.md) |
|
||||||
|
| 처음 전쟁에 참여하기 | [전쟁과 예턴 조합](./war-and-orders.md) |
|
||||||
|
| 지도에서 길과 정보 읽기 | [보급·정찰·땅따](./map-and-supply.md) |
|
||||||
|
| 세율과 외교를 이해하기 | [국가 재정과 외교](./economy-and-diplomacy.md) |
|
||||||
|
| 게시판과 국가 메시지의 줄임말 | [용어 사전](./glossary.md) |
|
||||||
|
| 메뉴와 부가 기능 찾기 | [국가 운영과 주요 기능](./nation-and-features.md) |
|
||||||
|
| 특정 명령의 조건 확인하기 | [커맨드와 실행 시기](./commands-and-timing.md) |
|
||||||
|
|
||||||
|
전략 글은 당시 규칙과 경험에 따른 조언입니다. 이 안내서는
|
||||||
|
[게시판 원문과 확인 근거](./sources.md)를 연결하고, 현재 규칙과 경험담을 구분합니다.
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# 보급·정찰·땅따: 지도 읽기
|
||||||
|
|
||||||
|
지도에서는 도시 색뿐 아니라 **도시 사이의 연결선, 수도, 보급 상태**를 함께 봅니다.
|
||||||
|
가까워 보이는 두 도시라도 길이 연결되지 않았다면 바로 이동하거나 공격할 수
|
||||||
|
없을 수 있습니다.
|
||||||
|
|
||||||
|
## 수도에서 이어지는 길이 보급로입니다
|
||||||
|
|
||||||
|
보급은 나라의 도시들이 수도와 같은 나라의 도시를 거쳐 연결되어 있는지에 관한
|
||||||
|
상태입니다. 단순히 내 장수가 쌀을 많이 들고 있다는 뜻은 아닙니다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
수도 A ─ 우리 도시 B ─ 우리 도시 C
|
||||||
|
```
|
||||||
|
|
||||||
|
다른 길이 없을 때 적이 B를 점령하면 C는 수도와 끊깁니다. C를 직접 공격하지
|
||||||
|
않아도 보급을 위협할 수 있습니다. 반대로 우회하는 우리 도시의 연결이 있다면
|
||||||
|
B 하나를 잃어도 이어질 수 있습니다.
|
||||||
|
|
||||||
|
## 깃 끊기·깃발이 떨어지기
|
||||||
|
|
||||||
|
이 맥락에서 **깃 끊기**는 보급 연결을 끊는 전술을 말합니다. 게임 한 판을 뜻하는
|
||||||
|
“깃수”와 구분하세요. [43기 국가열전](https://sam.hided.net/xe/community/25102)에는
|
||||||
|
연결 도시를 빼앗아 여러 지역의 보급을 끊고, 상대가 연결을 되찾는 공방이 나옵니다.
|
||||||
|
|
||||||
|
현재 기본 보급 처리는 수도에서 같은 나라 도시의 연결을 따라 판정합니다.
|
||||||
|
보급이 끊긴 도시는 내정·인구 등에서 손해를 받고, 그곳의 소속 장수도 병력과
|
||||||
|
훈련·사기가 줄어들 수 있습니다. 모병·내정 등 보급을 요구하는 행동도 확인해야
|
||||||
|
합니다. 도시 하나의 손실이 뒤쪽 여러 도시의 문제로 번지는 이유입니다.
|
||||||
|
|
||||||
|
도시 점령 순간과 보급 갱신 시점은 구분해야 합니다. 화면의 보급 표시와 월 변경
|
||||||
|
뒤의 기록을 확인하고, 고립된 곳에서 모병만 계속 예약하지 마세요. 국가 운영자는
|
||||||
|
연결 도시 수복이나 수도 위치 등 가능한 회복 수단을 살펴야 합니다.
|
||||||
|
|
||||||
|
## 장수 이동으로 도시 정보 밝히기
|
||||||
|
|
||||||
|
내 장수가 있는 도시는 현재 도시 화면에서 자세히 볼 수 있습니다. 따라서
|
||||||
|
허용된 **이동**으로 도시를 직접 살펴보는 것도 정보 수집 방법입니다. 이동은
|
||||||
|
도시를 점령하는 행동이 아니므로, 정보 확인과 공격을 혼동하지 않습니다.
|
||||||
|
|
||||||
|
현재 도시 조회는 자신의 위치, 소속 국가의 도시와 장수 위치, 첩보, 인접 여부
|
||||||
|
등에 따라 공개 범위를 나눕니다. 같은 국가의 관직을 가진 장수는 아군 장수가
|
||||||
|
있는 도시를 조회할 수 있는 조건도 있습니다. 그래서 장수 한 명의 위치가 국가의
|
||||||
|
정보 수집에 도움이 될 수 있습니다. 다만 도시가 보인다고 적 장수의 모든 비밀
|
||||||
|
정보나 예약 행동까지 공개되지는 않습니다.
|
||||||
|
|
||||||
|
이동은 거리·비용 등 실행 조건을 확인해야 하고, 자리를 비우면 원래 도시에서
|
||||||
|
하던 내정·수비·모병 계획이 달라집니다. 떠난 뒤에도 정보가 영구히 남는 탐험
|
||||||
|
게임으로 생각하지 마세요. **첩보**로 얻는 정보와 직접 주둔해서 보이는 정보도
|
||||||
|
구분합니다. 이 설명은 현재 도시 조회 코드로 확인한 기능이며, 같은 표현의
|
||||||
|
게시판 공략을 발견했다고 주장하는 것은 아닙니다. 근거는 [자료 안내](./sources.md)에 있습니다.
|
||||||
|
|
||||||
|
## 땅따와 183년
|
||||||
|
|
||||||
|
**땅따**는 땅따먹기의 줄임말입니다. 특히 게임 초반 출병 제한이 풀린 뒤 공백지
|
||||||
|
등을 선점해 나라의 영역과 접경을 만드는 시기를 가리킵니다. **공백지**는 어느
|
||||||
|
나라에도 속하지 않은 도시이며, 아무 준비 없이 점령할 수 있다는 뜻은 아닙니다.
|
||||||
|
|
||||||
|
게시판에서 자주 보이는 **183년 땅따**는 그 시나리오의 시작 연도와 보호 기간을
|
||||||
|
배경으로 합니다. [84기 회고](https://sam.hided.net/xe/community/72721)에는
|
||||||
|
183년 초 땅따가 끝난 사례가 있습니다. 모든 서버가 반드시 183년에 열리는 것은
|
||||||
|
아닙니다. 현재 서버의 공지와 출병 가능 시점을 확인하세요.
|
||||||
|
|
||||||
|
땅따 전에는 병력·훈련·사기·군량을 준비하고 동료와 목표 도시를 나눕니다.
|
||||||
|
가까운 성 하나만 보지 말고 그 뒤로 갈 길과 수도 연결, 다른 나라와 닿을 국경을
|
||||||
|
살펴보세요. 너무 넓게 뻗으면 지킬 곳이 늘어나고, 다른 나라의 확장과 부딪힐 수
|
||||||
|
있습니다. 이미 외교로 약속한 경로가 있는지도 국가 공지에서 확인합니다.
|
||||||
|
|
||||||
|
다음은 [국가 재정과 외교](./economy-and-diplomacy.md)입니다.
|
||||||
@@ -6,8 +6,9 @@
|
|||||||
전략·재정 설정, 비밀 장수 정보와 정찰 차단 같은 기능에 적용됩니다. 같은 국가 장수에게만 보이는 정보도
|
전략·재정 설정, 비밀 장수 정보와 정찰 차단 같은 기능에 적용됩니다. 같은 국가 장수에게만 보이는 정보도
|
||||||
있고, 공개 목록에는 일부 값이 가려질 수 있습니다.
|
있고, 공개 목록에는 일부 값이 가려질 수 있습니다.
|
||||||
|
|
||||||
내가 직접 URL이나 장수 번호를 입력해도 권한은 늘어나지 않습니다. 서버가 로그인 session에서 내 장수를
|
직책은 단순한 명예 표시가 아니라 실제 이용 권한과 연결됩니다. 처음에는 군주나
|
||||||
찾아 국가·직책·대상 관계를 판단합니다.
|
수뇌(국가 운영 담당 장수)에게 맡을 역할을 물어보세요. 권한이 없는 메뉴가 보이지
|
||||||
|
않거나 일부 정보가 가려지는 것은 정상일 수 있습니다.
|
||||||
|
|
||||||
## 국가 운영 화면
|
## 국가 운영 화면
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
## 외교와 즉시 승인
|
## 외교와 즉시 승인
|
||||||
|
|
||||||
선전포고 같은 일부 외교 행동은 국가 예턴으로 실행합니다. 종전·불가침·파기 제안은 제안 생성과 상대의
|
선전포고 같은 일부 외교 행동은 국가 예턴으로 실행합니다. 종전·불가침·파기 제안은 제안 생성과 상대의
|
||||||
수락이 나뉘며, 수락은 현재 관계와 권한을 다시 확인하는 즉시 action입니다. 제안 뒤 국가가 멸망하거나
|
수락이 나뉘며, 수락은 현재 관계와 권한을 다시 확인하는 즉시 처리입니다. 제안 뒤 국가가 멸망하거나
|
||||||
관계·직책이 바뀌면 수락할 수 없을 수 있습니다.
|
관계·직책이 바뀌면 수락할 수 없을 수 있습니다.
|
||||||
|
|
||||||
## 부대
|
## 부대
|
||||||
@@ -38,14 +39,13 @@
|
|||||||
|
|
||||||
## 경매
|
## 경매
|
||||||
|
|
||||||
경매는 열림·입찰·마감 단계가 있습니다. 입찰은 로그인한 내 장수와 자원을 기준으로 저장되고, 마감은 daemon
|
경매는 열림·입찰·마감 단계가 있습니다. 입찰은 로그인한 내 장수와 자원을 기준으로 저장되고, 마감은 서버가 낙찰과 정산을 확정합니다. 이미 마감됐거나 자원이 바뀌면 이전에 보던 화면의 입찰 가능 상태가
|
||||||
worker가 낙찰과 정산을 확정합니다. 이미 마감됐거나 자원이 바뀌면 이전에 보던 화면의 입찰 가능 상태가
|
|
||||||
유효하지 않을 수 있습니다.
|
유효하지 않을 수 있습니다.
|
||||||
|
|
||||||
## 토너먼트와 베팅
|
## 토너먼트와 베팅
|
||||||
|
|
||||||
토너먼트는 등록·진행·종료와 보상 정산 lifecycle을 가집니다. 국가 베팅도 열림과 마감·정산 event가
|
토너먼트는 등록·진행·종료와 보상 정산 단계을 가집니다. 국가 베팅도 열림과 마감·정산 행사가
|
||||||
scenario 달력에 의해 발생합니다. 각 화면의 현재 상태와 마감 시각을 확인해 주세요. profile에서 event가
|
시나리오 달력에 의해 발생합니다. 각 화면의 현재 상태와 마감 시각을 확인해 주세요. 서버에서 행사가
|
||||||
열리지 않았다면 메뉴가 있어도 참여할 수 없습니다.
|
열리지 않았다면 메뉴가 있어도 참여할 수 없습니다.
|
||||||
|
|
||||||
## 게시판·메시지
|
## 게시판·메시지
|
||||||
@@ -55,10 +55,12 @@ scenario 달력에 의해 발생합니다. 각 화면의 현재 상태와 마감
|
|||||||
|
|
||||||
## 기록
|
## 기록
|
||||||
|
|
||||||
- 연감은 월 경계의 world 상태를 보존합니다.
|
- 연감은 월이 바뀔 때의 게임 상태를 보존합니다.
|
||||||
- 명장·순위·왕조 화면은 현재 또는 종료된 시즌의 집계 데이터를 보여 줍니다.
|
- 명장·순위·왕조 화면은 현재 또는 종료된 시즌의 집계 데이터를 보여 줍니다.
|
||||||
- 과거 플레이는 로그인한 사용자 소유 archive만 볼 수 있습니다.
|
- 과거 플레이는 로그인한 사용자의 보관 기록만 볼 수 있습니다.
|
||||||
- 다른 사용자의 archive나 국가 비밀 정보는 URL을 바꿔도 공개되지 않습니다.
|
- 다른 사용자의 개인 보관 기록과 국가 비밀 정보는 공개 범위에 따라 가려집니다.
|
||||||
|
|
||||||
기록 화면의 값은 실시간 메인 상태와 갱신 시점이 다를 수 있습니다. 월 정산이나 시즌 종료 직후에는 해당
|
기록 화면의 값은 실시간 메인 상태와 갱신 시점이 다를 수 있습니다. 월 정산이나 시즌 종료 직후에는 해당
|
||||||
기능의 최신 상태 표시를 함께 확인해 주세요.
|
기능의 최신 상태 표시를 함께 확인해 주세요.
|
||||||
|
|
||||||
|
국가 운영 판단은 [재정과 외교](./economy-and-diplomacy.md), 낯선 말은 [용어 사전](./glossary.md)에서 이어 읽으세요.
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 자료와 확인 범위
|
||||||
|
|
||||||
|
이 안내서는 공개 게시판의 말과 경험을 처음 참가하는 사람의 언어로 풀고,
|
||||||
|
현재 게임 규칙은 Core 코드와 대조했습니다. 조사일은 **2026-09-29**입니다.
|
||||||
|
게시판 글은 역사적 경험·제안의 근거이며, 현재 설정이나 공식 운영 규정의 증명은
|
||||||
|
아닙니다. 서버 공지와 실제 화면의 조건을 먼저 확인하세요.
|
||||||
|
|
||||||
|
## 게시판에서 읽은 자료
|
||||||
|
|
||||||
|
본문과 필요한 공개 댓글을 확인했습니다. 글 전체·작성자 정보를 복제하지 않고
|
||||||
|
주제와 링크만 남깁니다. 검색 결과의 제목만으로 전략 효과를 확정하지 않았습니다.
|
||||||
|
|
||||||
|
| 원문 | 안내서에 반영한 내용 | 읽을 때의 한계 |
|
||||||
|
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||||
|
| [뉴비의 질문을 모아둡니다](https://sam.hided.net/xe/community/51784) | 직책, 훈사, 세율, 장비, 임관, 삭턴·벌점 등 초보자의 실제 질문 | 질문 모음 자체가 답이나 규칙은 아님 |
|
||||||
|
| [어떻게 해야 숙련도를 많이 높일 수 있을까?](https://sam.hided.net/xe/community/9660) | 모훈사, 양파, 징훈훈사사, 병종 저격, 준비 시간과 성장의 관계 | 2018년 계산 배수·버그 관련 조언을 현행 공략으로 재사용하지 않음 |
|
||||||
|
| [모훈사·징훈훈사사 완료 시 사기 건의](https://sam.hided.net/xe/devel/1143) | 준비 턴 약어와 자원·전쟁 준비 간의 고민 | 당시 제안이며 현재 훈사 결과를 보장하지 않음 |
|
||||||
|
| [2기 물조명성너프필수 개인열전](https://sam.hided.net/xe/community/2846) | 점사, 턴 정렬과 준비 순서가 어긋나는 사례 | 당시 참가자의 회고 |
|
||||||
|
| [10기 충차전 비스마르크 국 열전](https://sam.hided.net/xe/community/15146) | 세율 5%·지급률·포상 사용례 | 특수 기수의 재정 경험을 일반 최적값으로 취급하지 않음 |
|
||||||
|
| [43기 삼린이좋아국 국가열전](https://sam.hided.net/xe/community/25102) | 초반 확장·외교·연결 도시 수복과 보급 차단 | 당시 지도·외교·병종 조건에 의존 |
|
||||||
|
| [70기 일단박죠 이야기 마무리](https://sam.hided.net/xe/community/47834) | 모훈사출출, 양파, 발령, 전쟁 금지, 다굴 | 전술의 성공을 보편적인 보장으로 취급하지 않음 |
|
||||||
|
| [84기 유산채굴국 열전](https://sam.hided.net/xe/community/72721) | 183년 땅따 사용례 | 모든 서버의 출병 해금 연도가 183년인 것은 아님 |
|
||||||
|
| [89기 바나낫 후기](https://sam.hided.net/xe/community/75894) | 최후 2국 약속과 길막, 역할 분담 | 표현의 실제 사용례이며 외교 규정이 아님 |
|
||||||
|
| [97기 탱커단속반 장수열전](https://sam.hided.net/xe/community/78162) | 모출, 예턴, 기연, 발령, 삭턴의 대화상 의미 | 개인별 평가를 이용자 성향 정보로 재배포하지 않음 |
|
||||||
|
| [99기 99](https://sam.hided.net/xe/community/79329) | 국가 운영, NPC 정책, 세율 담당, 외교·의병·수뇌 | 최신에 가까운 회고도 현재 Core 구현과는 별도 |
|
||||||
|
| [외교 옵션을 명확하게 정의해 달라는 건의](https://sam.hided.net/xe/devel/12712) | 최후 2국 같은 포괄적 약속의 모호함 | 제안 글을 공식 규정으로 옮기지 않음 |
|
||||||
|
| [물조 명성 관련 건의](https://sam.hided.net/xe/devel/2569) | 물조와 자원 조달의 맥락 | 당시 제안의 보상 수치를 현행 규칙으로 취급하지 않음 |
|
||||||
|
| [개별 대화에서 설명한 내용을 모은 글](https://sam.hided.net/xe/devel/31537) | 댓글의 도시 장수 정렬·턴 순서 설명 | 오래된 설명이며 현재 화면별 정렬은 따로 확인해야 함 |
|
||||||
|
|
||||||
|
검색은 공개 `community`·`devel` 게시판의 제목+내용과 웹 검색에서 양파, 모출,
|
||||||
|
모훈사출출, 세율, 보급, 깃 끊기, 최후2국, 땅따 등의 표현을 조합했습니다.
|
||||||
|
전수조사나 모든 은어 수집을 뜻하지 않습니다. 다른 서버의 관습이나 사적인 대화방
|
||||||
|
내용을 섞지 않았습니다.
|
||||||
|
|
||||||
|
## 현재 규칙을 확인한 위치
|
||||||
|
|
||||||
|
다음은 문서 편집자·개발자를 위한 근거입니다. 게임을 하기 위해 코드를 읽을
|
||||||
|
필요는 없습니다. 경로는 `core2026` 저장소 기준입니다.
|
||||||
|
|
||||||
|
| 내용 | 확인 위치 |
|
||||||
|
| ---------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
||||||
|
| 모병·훈련·사기·이동·출병의 입력과 실행 조건 | `packages/logic/src/actions/turn/general/`, 생성 커맨드 목록 |
|
||||||
|
| 출병 보호 기간 | `che_출병.ts`의 예약·실행 constraint, `packages/logic/src/constraints/misc.ts` |
|
||||||
|
| 수도와 소유 도시 연결에 따른 보급·고립 손실 | `app/game-engine/src/turn/monthlyCitySupplyAction.ts` |
|
||||||
|
| 주둔·같은 국가 장수 위치·첩보에 따른 도시 정보 | `app/game-api/src/router/world/index.ts`의 `getCurrentCity` |
|
||||||
|
| 세율 5%·30%의 내정·민심·인구 비교 | `app/game-engine/src/turn/monthlySemiAnnualAction.ts` |
|
||||||
|
| 세율에 비례하는 수입 | `packages/logic/src/economy/nationIncome.ts`, `app/game-engine/src/turn/incomeHandler.ts` |
|
||||||
|
| 적용 세율 확정 | `app/game-engine/src/turn/nationTurnMonthlyHandler.ts`, `nationTaxRate.ts` |
|
||||||
|
| 수비 설정의 표시와 선택지 | `app/game-frontend/src/views/MyPageView.vue` |
|
||||||
|
|
||||||
|
“장수 이동으로 도시 정보 밝히기”는 현재 조회·이동 코드로 확인했습니다. 직접
|
||||||
|
설명하는 게시판 원문은 이번 표본에서 확보하지 못했습니다. “모병양파”는 게시판의
|
||||||
|
양파·모병 반복 맥락을 풀어 쓴 표현입니다. 세율 30%의 효과는 계산 코드로 확인했으며,
|
||||||
|
30%를 권장하는 공략을 발견한 것으로 표시하지 않았습니다.
|
||||||
|
|
||||||
|
## 안내서를 고칠 때
|
||||||
|
|
||||||
|
새 용어는 원문에서 어떤 상황에 쓰였는지 보고, 뜻·행동·조건·손해를 함께 적습니다.
|
||||||
|
규칙 숫자는 현재 코드와 서버 설정을 확인하고, 경험담의 효율 배수나 오래된 운영
|
||||||
|
제안을 그대로 옮기지 않습니다. 화면 용어와 커뮤니티 약어가 다르면 양쪽을 연결합니다.
|
||||||
@@ -2,12 +2,20 @@
|
|||||||
|
|
||||||
## 현실 시간과 게임 달력
|
## 현실 시간과 게임 달력
|
||||||
|
|
||||||
서버는 profile의 턴 schedule에 따라 tick을 진행합니다. 한 tick마다 실행 시각이 된 장수의 명령을 처리하고,
|
내 장수는 정해진 간격으로 돌아오는 차례에 행동합니다. 화면의 **다음 턴 시각**이
|
||||||
게임 달력이 다음 달로 넘어갈 경계에서는 월간 정산과 event도 처리합니다. 실제 턴 간격은 서버 설정에 따라
|
그 차례를 알려 줍니다. 서버마다 간격이 다르고 장수마다 차례가 다를 수 있으므로,
|
||||||
달라지므로 메인 화면의 다음 턴 시각을 확인해 주세요.
|
다른 사람의 시각을 내 시각으로 생각하지 마세요.
|
||||||
|
|
||||||
|
예를 들어 다음 턴이 14:03이고 턴 간격이 10분인 서버에서, 정상 진행 중
|
||||||
|
“모병 → 훈련 → 사기 진작”을 예약했다면 14:03, 14:13, 14:23에 하나씩 실행하는
|
||||||
|
식입니다. 예시의 10분은 고정 규칙이 아닙니다. 일시정지·재개나 운영 조정이 있다면
|
||||||
|
갱신된 화면의 시각을 확인합니다.
|
||||||
|
|
||||||
|
현실 시간과 별도로 게임 속 연·월이 흐릅니다. 월이 바뀔 때의 국가 정산과
|
||||||
|
내 장수의 차례는 구분하세요.
|
||||||
|
|
||||||
게임 달력은 1월부터 12월까지 진행하고 12월 다음은 다음 해 1월입니다. 월이 바뀔 때 수입, 외교·전쟁 상태,
|
게임 달력은 1월부터 12월까지 진행하고 12월 다음은 다음 해 1월입니다. 월이 바뀔 때 수입, 외교·전쟁 상태,
|
||||||
도시·국가 상태, 연감, scenario event 같은 여러 처리가 정해진 순서로 일어납니다. 그러므로 같은 커맨드도
|
도시·국가 상태, 연감, 시나리오에 정해진 사건 같은 여러 처리가 정해진 순서로 일어납니다. 그러므로 같은 커맨드도
|
||||||
월 변경 직전과 직후에 자원이나 조건이 달라질 수 있습니다.
|
월 변경 직전과 직후에 자원이나 조건이 달라질 수 있습니다.
|
||||||
|
|
||||||
## 예턴
|
## 예턴
|
||||||
@@ -18,8 +26,8 @@
|
|||||||
- 대상 도시·장수·국가, 병종, 수량 등이 필요한 커맨드는 입력값까지 저장됩니다.
|
- 대상 도시·장수·국가, 병종, 수량 등이 필요한 커맨드는 입력값까지 저장됩니다.
|
||||||
- 비어 있는 칸과 실행 뒤 밀려난 끝 칸은 기본적으로 `휴식`으로 채워집니다.
|
- 비어 있는 칸과 실행 뒤 밀려난 끝 칸은 기본적으로 `휴식`으로 채워집니다.
|
||||||
|
|
||||||
여러 칸에 한꺼번에 같은 명령을 넣거나 예턴을 앞뒤로 이동할 수 있습니다. 다른 탭에서 먼저 수정하면 revision
|
여러 칸에 한꺼번에 같은 명령을 넣거나 예턴을 앞뒤로 이동할 수 있습니다. 다른 탭에서 먼저 수정하면 저장 내용이
|
||||||
충돌이 날 수 있으므로 새 목록을 불러온 뒤 다시 적용해 주세요.
|
충돌할 수 있으므로 최신 목록을 불러온 뒤 다시 적용해 주세요.
|
||||||
|
|
||||||
## 예약할 수 있어도 실행에 실패할 수 있습니다
|
## 예약할 수 있어도 실행에 실패할 수 있습니다
|
||||||
|
|
||||||
@@ -30,7 +38,7 @@
|
|||||||
- 내 소속 국가와 직책
|
- 내 소속 국가와 직책
|
||||||
- 대상 장수·도시·국가의 존재와 외교 관계
|
- 대상 장수·도시·국가의 존재와 외교 관계
|
||||||
- 전쟁·불가침 기간과 전략 커맨드 재사용 상태
|
- 전쟁·불가침 기간과 전략 커맨드 재사용 상태
|
||||||
- scenario 연도, 가입 방식, 기술 수준과 병종 개방
|
- 시나리오 연도, 가입 방식, 기술 수준과 병종 개방
|
||||||
|
|
||||||
입력 화면은 현재 정보로 사전 판단하지만, 실제 턴에서는 전체 조건을 다시 확인합니다. 실패 이유는 명령
|
입력 화면은 현재 정보로 사전 판단하지만, 실제 턴에서는 전체 조건을 다시 확인합니다. 실패 이유는 명령
|
||||||
로그에서 확인하고 다음 예턴을 조정해 주세요.
|
로그에서 확인하고 다음 예턴을 조정해 주세요.
|
||||||
@@ -48,5 +56,7 @@
|
|||||||
- 국가 운영자는 국고·군량, 직책과 외교 제안의 만료·수락 상태를 확인해 주세요.
|
- 국가 운영자는 국고·군량, 직책과 외교 제안의 만료·수락 상태를 확인해 주세요.
|
||||||
- 경매·토너먼트·국가 베팅은 각 화면에 열린 기간과 마감 상태가 표시될 때만 입력해 주세요.
|
- 경매·토너먼트·국가 베팅은 각 화면에 열린 기간과 마감 상태가 표시될 때만 입력해 주세요.
|
||||||
|
|
||||||
월간 event는 scenario resource가 결정하므로 모든 서버가 같은 월에 같은 event를 실행한다고 가정하지
|
월간 사건은 시나리오 설정에 따라 달라지므로 모든 서버가 같은 월에 같은 행사를 연다고 가정하지
|
||||||
말아 주세요.
|
말아 주세요.
|
||||||
|
|
||||||
|
다음은 [전쟁과 예턴 조합](./war-and-orders.md)입니다.
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 전쟁과 예턴 조합
|
||||||
|
|
||||||
|
도시를 공격하려면 먼저 병력을 준비하고, 공격 가능한 관계와 경로인지 확인해야
|
||||||
|
합니다. **출병**은 공격 행동, **이동**은 장수의 위치를 바꾸는 행동입니다.
|
||||||
|
이동만으로 적 도시를 점령하지는 않습니다.
|
||||||
|
|
||||||
|
## 전쟁에 처음 참가할 때
|
||||||
|
|
||||||
|
1. 국가 공지에서 공격할 도시와 방어할 도시를 확인합니다.
|
||||||
|
2. 내 위치, 모병할 병종·수량, 금과 쌀을 확인합니다.
|
||||||
|
3. 병력을 모으고 필요한 훈련·사기를 준비합니다.
|
||||||
|
4. 공격 목표와 수비 설정을 확인하고 예턴을 넣습니다.
|
||||||
|
5. 첫 전투 뒤 병력·훈련·사기·쌀과 로그를 다시 봅니다. 다음 출병도 성공할지는
|
||||||
|
앞 전투의 결과에 달려 있습니다.
|
||||||
|
|
||||||
|
병종마다 사용할 수 있는 도시·기술 등의 조건이 다릅니다. 같은 병종을 계속
|
||||||
|
모으려면 [보급과 지도](./map-and-supply.md)도 함께 봐야 합니다.
|
||||||
|
|
||||||
|
## 모출·모훈사출출은 명령 순서를 줄인 말입니다
|
||||||
|
|
||||||
|
**모**는 모병, **징**은 징병, **훈**은 훈련, **사**는 사기 진작, **출**은 출병입니다.
|
||||||
|
한 글자마다 예턴 한 칸을 뜻합니다. “모훈사출출”이라는 별도 버튼을 찾는 것이
|
||||||
|
아닙니다. 각 칸의 병종·수량·도시도 지정해야 합니다.
|
||||||
|
|
||||||
|
| 조합 | 실제 순서 | 쓰려는 목적과 주의점 |
|
||||||
|
| ---------- | ------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||||
|
| 모출 | 모병 → 출병 | 급히 공격을 이어 갑니다. 훈련·사기 준비를 줄인 만큼 전투 효율을 확인해야 합니다. |
|
||||||
|
| 모출출 | 모병 → 출병 → 출병 | 공격 기회를 늘리려는 조합입니다. 첫 전투 뒤 병력이 남아야 다음 공격도 가능합니다. |
|
||||||
|
| 모훈사출 | 모병 → 훈련 → 사기 진작 → 출병 | 전투 준비 후 공격하는 기본적인 읽기 예시입니다. |
|
||||||
|
| 모훈사출출 | 모병 → 훈련 → 사기 진작 → 출병 → 출병 | 준비된 병력으로 두 차례 공격을 시도합니다. 항상 두 번 싸울 수 있다는 보장은 없습니다. |
|
||||||
|
| 징훈훈사사 | 징병 → 훈련 두 번 → 사기 진작 두 번 | 징병 뒤 준비를 길게 하는 표현입니다. 비용뿐 아니라 인구·민심과 준비 시간도 봅니다. |
|
||||||
|
| 풀훈사 | 훈련·사기를 충분히 채운 상태 | 행동 이름이 아니라 준비 상태입니다. 실제 수치를 확인합니다. |
|
||||||
|
|
||||||
|
이 조합들이 사용된 근거는 [숙련도 토론](https://sam.hided.net/xe/community/9660),
|
||||||
|
[준비 턴 건의](https://sam.hided.net/xe/devel/1143),
|
||||||
|
[70기 전쟁 회고](https://sam.hided.net/xe/community/47834)에 있습니다.
|
||||||
|
오래된 글의 효율 배수나 권장 수비 수치를 현재의 정답으로 옮기지는 않았습니다.
|
||||||
|
|
||||||
|
## 모병양파: 병력을 계속 보충하며 버티기
|
||||||
|
|
||||||
|
**양파**는 병력을 깎아도 다시 보충되어 계속 상대해야 하는 수비를 가리키는 은어입니다.
|
||||||
|
**모병양파**는 모병을 반복하면서 방어 병력을 다시 채우는 방식으로 이해하면 됩니다.
|
||||||
|
훈련·사기 준비에 시간을 쓰기보다 당장 도시가 무너지지 않게 막으려는 선택입니다.
|
||||||
|
|
||||||
|
예턴에 모병만 넣어도 자동으로 모든 공격을 막는 것은 아닙니다. 현재 도시,
|
||||||
|
병력과 자원, 수비 참여 설정·훈련사기 기준이 맞아야 합니다. 계속 모병하면 도시의
|
||||||
|
인구와 내 자원이 줄고, 준비가 덜 된 병력으로 싸우면서 손실이 커질 수 있습니다.
|
||||||
|
[숙련도 토론](https://sam.hided.net/xe/community/9660)도 양파를 개인 성장의 만능 해법으로
|
||||||
|
보지 않습니다. 국가의 급한 수비와 장수 개인의 성장 목적이 다를 수 있습니다.
|
||||||
|
|
||||||
|
처음에는 수비 수치를 임의로 낮추기보다 “지금 양파가 필요한지, 어떤 병종·수량과
|
||||||
|
수비 기준을 쓸지”를 국가에 물어보세요. 수비 설정을 바꿨다면 이후 예턴도 확인합니다.
|
||||||
|
|
||||||
|
## 점사·턴 정렬·병종 저격
|
||||||
|
|
||||||
|
**점사**는 여러 장수의 공격을 특정 도시·시점에 모으는 것, **턴 정렬**은 그 공격
|
||||||
|
순서를 맞추는 일입니다. 먼저 공격한 사람이 수비 병력을 줄이고 뒤의 사람이 성벽을
|
||||||
|
공격할 수 있으므로, 혼자 한 번 더 공격하는 것이 공동 계획과 어긋날 수도 있습니다.
|
||||||
|
[초기 전쟁 회고](https://sam.hided.net/xe/community/2846)에는 준비 턴이 어긋난 사례가 나옵니다.
|
||||||
|
|
||||||
|
**병종 저격**은 상대 병종과의 유리한 상성을 노리는 표현입니다. 장수 특기 이름인
|
||||||
|
“저격”과 문맥을 구분하세요. **수비 켬끔**은 수비 참여를 켜거나 끄며 상대할 전투를
|
||||||
|
조절하는 플레이를 뜻합니다. 처음에는 국가 지시에 맞춰 사용하고, 접속을 마칠 때
|
||||||
|
원하는 수비 설정으로 남아 있는지 확인하세요.
|
||||||
|
|
||||||
|
## 로그에서 다음 행동 결정하기
|
||||||
|
|
||||||
|
승패 한 줄만 보지 말고 누구와 싸웠는지, 병력이 얼마나 줄었는지, 쌀이 남았는지,
|
||||||
|
도시를 실제로 점령했는지 봅니다. 전투 시뮬레이터의 예상은 실제 상대 상태·난수·
|
||||||
|
연속 전투를 모두 보장하지 않습니다. 패배 후에도 내 장수가 남아 있다면 다시 준비해
|
||||||
|
참전할 수 있고, 나라가 멸망한 뒤의 소속 변경은 서버 규칙과 화면 안내를 따릅니다.
|
||||||
|
|
||||||
|
다음은 [보급·정찰·땅따](./map-and-supply.md)입니다.
|
||||||
Reference in New Issue
Block a user