GameDesign/design/workslot/README.md 최종 업데이트 2026-08-04

00. 작업슬롯 (WorkStationSlot)

상위 문서: 게임기획코어.md 최종 업데이트: 2026-08-04 · 상태: 골격 확정 · 수치 미정 바뀌면 갱신: 게임UI · 게임기획코어 · 낚시 · 산업레벨 · 자원채취 · 캐릭터 특성

채취가 시작되는 지점이다. 플레이어는 슬롯에 산업·레벨을 지정하고 캐릭터를 배치하며, 배치된 슬롯 하나하나가 독립적으로 자원채취 루프를 돌린다.

이 문서는 "무엇을 얼마나 굴릴 수 있는가" 를 정한다. 그 슬롯이 무엇을 산출하는지는 자원채취가 담당한다.


1. 확정 사항

# 항목 확정 내용
1 슬롯당 캐릭터 1명. 여러 명을 한 슬롯에 넣지 않는다
2 슬롯당 산업 1개. 5종 중 선택
2-1 슬롯당 산업 레벨 1개. 해금된 레벨 중 선택 (2026-08-01) → 산업 레벨
2-2 판정 1회 비용 레벨별 필요 점수. 전역 상수가 아니다 (2026-08-01) → 3장
3 동시 가동 슬롯 수만큼. 산업 택1이 폐지됐다
4 최대 슬롯 수 시작 2개 · 상한 8개. 계정 레벨(상한) × 골드(구매)의 이중 게이트 → 4장
5 채취 판정 주체 서버. 클라이언트는 결과를 받기만 한다
6 접속 중 알림 서버가 실시간으로 밀어 준다 (푸시)
7 요일 로테이션 폐지. 효율배수에서 요일 항이 빠진다
8 오프라인 진행 폐지 (2026-07-29). 접속 중에만 자란다. 접속 보상도 없다
9 채취 속도 캐릭터가 바꾼다. 산출량이 아니라 주기에 작용한다 → 슬롯마다 주기가 다르다
10 속도 보정 합성 적성 기본값 × (1 + Σ가산) × Π승산. 보정은 기본적으로 가산 → 3.4

2. 구조

Player
 ├─ IndustryLevel[]          산업별 최대 해금 레벨 (계정 단위)       ← 저장
 └─ WorkStationSlot[]        ← 최대 개수는 레벨 등으로 증가
      ├─ SlotIndex           슬롯 번호 (0-based)
      ├─ Industry            농사 · 벌목 · 낚시 · 채굴 · 사냥 중 1개  ← 저장
      ├─ IndustryLevel       해금된 레벨 중 1개                      ← 저장
      ├─ Character           배치된 캐릭터 1명 (비어 있을 수 있다)   ← 저장
      ├─ Speed               캐릭터 스탯·버프의 곱 (채취 속도)       ← 접속마다 재계산
      ├─ LastTickAt          마지막 정산 시각                        ← 세션 지역
      └─ Progress            판정에 못 미친 누적 작업량              ← 세션 지역

DB에 남는 것은 "산업 + 레벨 + 캐릭터"라는 배치 설정뿐이다. 오프라인 진행이 없으므로 저장할 진행도가 없다. 비어 있는(캐릭터 미배치) 슬롯은 돌지 않는다.

해금은 계정이 갖고, 선택은 슬롯이 갖는다. 같은 산업 슬롯을 여러 개 돌려도 레벨이 합산되지 않으며, 슬롯마다 다른 레벨을 고를 수 있다. ⚠️ 서버가 해금 여부를 검증한다 — 클라가 보낸 레벨을 그대로 믿으면 안 된다(P4). → 산업 레벨

진행도를 시간이 아니라 "작업량"으로 누적하는 이유

캐릭터마다 채취 속도가 다르고 버프로 세션 중에도 바뀐다. 판정 = floor(경과 / 주기) 형태를 유지하면 두 가지가 깨진다.

문제 내용
구간별 속도를 못 쓴다 "이 3시간 중 10분만 버프"를 하나의 나눗셈으로 표현할 수 없다
자투리 의미가 흔들린다 30초 주기의 15초는 0.5판정, 20초 주기에서는 0.75판정 — 주기를 흔들어 이득을 볼 수 있다

속도는 더할 수 있고 구간별로 쪼갤 수 있지만 주기는 그렇지 않다. 그래서 진행도의 단위를 작업량으로 두고 각 구간의 속도로 누적한다.

누적작업량 += 경과시간 × 속도        ← 구간마다 다른 속도를 그냥 더한다
판정 횟수   = floor(누적 / 1판정비용)
나머지는 "작업량"으로 이월           ← 주기가 바뀌어도 재환산이 필요 없다

규칙: 속도가 바뀌기 직전에 반드시 정산한다. 이 순서를 지키면 한 구간의 속도가 항상 하나로 확정되어 소급이 원천 봉쇄된다. 배치 변경(Assign)이 이미 같은 패턴을 쓴다.


3. 채취 판정 — 서버 권위

클라이언트는 "캤습니다"라고 신고하지 않는다. 재화가 생성되는 지점이므로 서버가 시각을 기준으로 계산하고 결과만 내려 준다 (게임기획코어 P4).

정산(slot, now):
    경과      = now - slot.LastTickAt
    slot.Progress += 경과 × slot.Speed        ← 구간 속도로 작업량 누적
    판정 횟수 = floor(slot.Progress / 1판정비용)
    산출      = 판정 횟수 × 회당산출
    slot.Progress -= 판정 횟수 × 1판정비용    ← 나머지는 작업량 단위로 이월
    slot.LastTickAt = now
항목
1판정비용 (산업, 레벨)IndustryLevelTable.RequiredScore ← 2026-08-01 변경
기준 채취주기 30초 (Lv1 · 적성 1일 때. 전 산업 공통 · 확정)
실효 주기 1판정비용 ÷ 속도 — 슬롯마다·레벨마다 다르다
속도 적성기본값 × (1 + Σ가산) × Π승산 → 3.4
회당 산출 산업별 (1개 고정 · 수치 미정)
오프라인 진행 없음 (2026-07-29 폐지)

⚠️ 1판정비용은 더 이상 전역 상수가 아니다 (2026-08-01). 산업 레벨이 도입되면서 레벨마다 다른 값이 됐다 — Lv1 30,000에서 레벨당 ×3으로 커진다. 누적하는 Progress가 곧 "점수" 이며, 적성 1 캐릭터가 1초 일하면 1000점이다.

레벨 필요 점수 적성 1 적성 10
Lv1 30,000 30초 7.5초
Lv3 270,000 4분 30초 1분 7.5초
Lv5 2,430,000 40분 30초 10분 7.5초

위 의사코드는 그대로 유효하다1판정비용이 상수 참조에서 테이블 조회로 바뀔 뿐이다. 진행도를 작업량으로 누적해 둔 설계가 이 변경을 그대로 받아 줬다. 서버는 밀리초 기준이라 엑셀 값에 ×1000 한다.산업 레벨 2.4

캐릭터는 "얼마나 많이"가 아니라 "얼마나 빨리"에 작용한다. 폐지된 요일배수의 자리를 캐릭터 스탯이 대신하되, 산출량 배수가 아니라 주기를 줄이는 방향이다. → 5장

⚠️ 지금 개발 서버는 30초가 아니라 5초로 돌고 있다. Global.GatherSpeedMultiplier(서버 전역 배수)가 확인 편의로 6.0배에 맞춰져 있다. 위 표의 30초가 기획값이며, 배수는 엑셀·기획 수치를 건드리지 않는 별도 손잡이다. 서버 시작 로그에 배수가 1.0이 아니라는 경고가 찍힌다. 배포 전에 1.0으로 되돌린다.

3.1 정산 시점

속도·배치가 바뀌는 모든 지점에서 먼저 정산한다. 이 규칙이 소급 적용을 막는다.

시점 이유
로그인 정산하지 않는다. 오프라인 구간이 없으므로 진행도 0에서 시작
슬롯 변경 (산업·레벨·캐릭터) 변경 전 구간을 이전 설정으로 정산해야 한다
속도 변경 (버프 적용·만료) 정산하지 않고 바꾸면 이전 구간까지 새 속도로 계산된다
상태 조회 플레이어가 위젯을 열었을 때
접속 종료 완성된 판정은 지급, 진행 조각은 폐기
주기 푸시 접속 중 실시간 알림 (3.2)

3.2 접속 중 실시간 푸시

접속해 있으면 판정이 완성될 때마다 "잡혔다"가 서버에서 온다.

항목 내용
푸시 해상도 1초 — 채취주기가 아니다 (슬롯마다 다르므로 맞출 대상이 없다)
대상 접속 중이고 캐릭터가 배치된 슬롯
내용 이번에 나온 ItemTID와 개수

해상도는 정확성이 아니라 체감에만 영향을 준다. 산출량은 경과 시각이 정하므로 타이머가 늦게 돌아도 개수는 같고, 다만 알림이 그만큼 늦게 도착할 뿐이다. 가장 빠른 슬롯의 주기보다 짧게 두면 몰아서 도착하는 일이 없다.

⚠️ 초당 수십 번 도는 루프(30fps 등)를 두지 않는다. 판정 간격이 수십 초인데 초당 30번 깨우면 대부분의 틱이 헛돈다. 게다가 게임 로직 스레드가 단일이라 채취 계산이 패킷 처리를 밀어낸다. 연출은 클라이언트가 로컬 타이머로 만들고, 값의 진실은 서버가 정한다.

클라이언트는 주기(초)를 받지 않는다. 슬롯마다 다르고 버프로 바뀌기 때문이다. 대신 진행도·속도·1판정비용을 받아 직접 계산한다 — 남은시간 = (비용 - 진행도 - 경과 × 속도) ÷ 속도.

3.3 하트비트 — ✅ 확정 (2026-08-04)

오프라인 진행이 폐지되면서 "접속 중" 판정이 곧 재화 생성 조건이 됐다.

TCP는 클라이언트가 죽어도 서버가 즉시 알지 못한다. 노트북을 덮거나 랜선을 뽑으면 서버는 수십 초~수 분간 그 유저를 "접속 중"으로 보고 계속 채취를 쌓는다. 없애려던 오프라인 진행이 뒷문으로 되살아나는 셈이다.

논점 확정 내용
하트비트 주기 5초 — 클라이언트가 C_PingRequest를 보낸다
무응답 판정 시간 15초 — 이 값이 곧 "공짜로 얻는 최대 오프라인 시간"이다
재접속 유예 (순단 시 진행 보존) 없다. 끊기면 진행 조각은 버려진다 (3.1과 같은 규칙)

판정 시간(15초)은 채취 기준 주기(30초)보다 짧다. 이것이 이 값을 고른 이유다 — 부당 적립 구간이 판정 1회를 채우지 못하므로 아이템이 한 개도 새지 않는다. 주기(5초)의 3배라 두 번까지는 놓쳐도 끊기지 않는다.

살아 있음의 증거는 수신뿐이다. 서버의 송신은 큐 적재라 소켓이 죽어도 성공한다 — 무엇을 받았는지도 보지 않는다. 하트비트든 일반 요청이든 바이트가 왔다는 사실만 본다.

PC 절전은 끊김으로 본다 (확정). 절전 진입 시 소켓이 닫히므로 그 구간은 진행하지 않는다.

같은 계정으로 다시 접속하면 기존 세션을 끊고 새 쪽을 받는다. 좀비가 남아 있을 때 정상 재접속이 막히는 편이 더 나쁘다 — "끊긴 걸 서버가 아직 모르는" 경우가 압도적이다.

3.4 속도 보정 합성 — 가산이 기본, 승산은 예외 (2026-07-30 확정)

보정을 가산(add)/승산(mul)으로 먼저 분류해 모아 두고, 마지막에 한 번 적용한다.

속도 = 적성기본값 × (1 + Σ가산) × Π승산
무엇이 들어가나 의미
적성기본값 캐릭터의 해당 산업 적성 (WorkSpeedTable) 모든 보정이 기준으로 삼는 값
가산 (add) 특성 패시브 · 액티브 부스트 · 장비 "작업속도 +25%" = 기본값의 25%를 더한다
승산 (mul) "총 작업속도 증가" 류 결과 전체에 배수로 얹는다. 드물게 쓴다

예시 — 적성 4(기본 1.8배)에 특성 +25%, 부스트 +100%가 붙은 슬롯:

1.8 × (1 + 0.25 + 1.00) = 1.8 × 2.25 = 4.05배   → 실효 주기 30 ÷ 4.05 ≈ 7.4초

왜 전부 곱하지 않는가

가산끼리 곱하면 보정원의 개수가 밸런스를 흔든다.

가산 (채택) 전부 곱셈
+25% 하나 ×1.25 ×1.25
+25% ×1.50 ×1.5625
+25% ×2.00 ×2.44
+25% 여덟 ×3.00 ×5.96

곱셈에서는 특성 하나를 추가할 때마다 이미 있던 모든 보정의 가치가 함께 오른다. 그러면 후반에 보정이 쌓일수록 개별 수치가 의도보다 크게 작용해, 항을 하나 넣을 때마다 전체 밸런스를 다시 잡아야 한다. 가산으로 두면 적힌 숫자가 곧 그 보정의 몫이다.

승산은 가산 전체를 함께 부풀리므로 영향력이 가산과 비교가 안 된다. 새 보정을 설계할 때는 가산이 기본값이고, 승산은 "총 작업속도"처럼 의도적으로 전체에 얹을 때만 고른다.

부수 성질

  • 붙이는 순서가 결과를 바꾸지 않는다 (가산은 합, 승산은 곱). 어느 시스템이 먼저 계산되는지가 밸런스에 끼어들지 않는다.
  • 적성 0은 배치할 수 없다 (아래 참조). 기본값이 0이라 계산에 들어오지 않는다.
  • 감소 보정은 음수 가산으로 표현한다. 겹쳐서 -100%를 넘어가도 속도 하한에서 막힌다.
  • Global.GatherSpeedMultiplier(개발용 전역 배수)가 현재 유일한 승산이다.

적성 0 = 배치 불가 (2026-08-01 재확정)

적성 0인 산업에는 배치할 수 없다. 서버가 배치 요청 자체를 거절한다.

항목
적성 0 배치 거절 (Character.CanWork = false)
WorkSpeedTable TID 0 0 (= 다루지 못한다)
배치 UI 적성 0 캐릭터를 잠금 표시

이유: "할 수는 있는데 손해다"는 선택지를 늘리는 것처럼 보이지만, 실제로는 적성이라는 축을 무의미하게 만든다. 아무 캐릭터나 아무 데나 놓을 수 있으면 배치 퍼즐이 "속도 순 정렬"로 수렴한다. 못 하는 것이 있어야 누구를 어디에 둘지가 문제가 된다.

⚠️ 2026-07-30에 "배치는 되고 대신 느리다" 로 바꿨다가 2026-08-01에 원복했다. 그 변경은 문서에만 적혔고 구현된 적이 없다 — 코드(Character.CanWork)와 엑셀(WorkSpeedTable TID 0 = 0)은 처음부터 배치 불가였다. 이번 결정으로 문서가 코드·데이터와 다시 일치한다. 엑셀 수정은 필요 없다.

속도 0을 계산에 넣으면 TimeUntilNextJudge에서 0으로 나눈다. "정지"는 배치를 비우는 것으로 표현한다(MinWorkSpeed 참조). → 캐릭터 2.2

용어 — 기본값과 확정값을 이름으로 구분한다

이름 무엇 어디
BaseWorkSpeedPermille 적성별 기본 작업속도 — 계산의 입력 엑셀 WorkSpeedTable
CurrentWorkSpeed 현재 작업속도 — 보정이 전부 적용된 결과 슬롯 · 패킷(WorkStationSlotInfo)
  • 둘 다 천분율이다 (1000 = 1.0배). WorkSpeedScale 한 곳에 정의돼 있다.
  • 클라이언트는 CurrentWorkSpeed만 본다. 보정 내역을 받지 않으므로 계산할 것이 없다.

구현: Server/WSGameServer/User/WorkStation/WorkSpeed.cs (누산기), User.WorkStation.csResolveSlotSpeed(유일한 호출 경로)


4. 슬롯 확장 — 이중 게이트 (2026-08-01 확정)

계정 레벨이 상한을 열고, 골드로 실제 구매한다.

항목
시작 슬롯 수 2개
상한 8개
증가 수단 계정 레벨(자물쇠) × 골드(열쇠)
레벨별 상한 곡선 · 구매 비용 곡선 ❌ 미정 (일감 T-012)

⚠️ 서버는 아직 1개로 준다 (2026-08-02 확인). User.DefaultSlotCount = 1이고 주석도 "시작 슬롯 수가 미확정이라"로 남아 있다 — 확정(2026-08-01) 이전 상태다. 상한 8 검증도 없다. 곡선(T-012)과 무관하게 이 두 숫자는 지금 반영할 수 있다.

한쪽만 쓰면 문제
레벨만 슬롯 확장이 sink가 아니게 된다 — 골드가 갈 곳이 하나 줄어든다
골드만 골드 → 슬롯 → 골드 자기가속이 폭주한다

⚠️ 슬롯 수가 곧 재화 생성량의 배수다. 슬롯이 2배가 되면 경제에 유입되는 총량도 2배다. 곡선은 거래 3.3의 경제 설계와 반드시 함께 정한다 (P4). 슬롯을 먼저 풀어 놓고 경제를 나중에 맞추면 되돌릴 수 없다.

⚠️ 계정 레벨 → 슬롯 → 경험치 되먹임에 주의한다 → 캐릭터 3.2.


5. 캐릭터 — 별도 문서로 분리 (2026-08-01)

슬롯에 배치되는 주체다. 상세는 캐릭터를 본다.

이 문서가 의존하는 것만 옮겨 적는다.

논점 상태
산업 적성 있다. 적성이 곧 기본 작업속도(WorkSpeedTable)
적성 0 배치 불가 (2026-08-01 재확정) → 3.4
적성 → 속도 기여식 ✅ 확정. 주기에 작용한다 (산출량이 아니다)
장비 ✅ 무기1·장신구2·보석1. 효과는 가산
캐릭터 레벨이 무엇을 올리는가 미정
경험치 획득식 · 레벨업 곡선 미정
획득 경로 미정

산업 적성을 두면 배치가 퍼즐이 된다. 슬롯 3개에 캐릭터 5명이면 "누구를 어디에" 가 실제 선택이 된다. 적성이 없으면 스탯 높은 순으로 채우면 끝이라 배치에 의사결정이 없다.


6. 결정 필요 (Open Questions)

  1. 레벨별 슬롯 상한 곡선과 구매 비용 곡선은? (4장 — 경제 총량과 직결, T-012)
  2. 빈 슬롯도 굴러가는가? (캐릭터 없이 산업만 지정 — 현재는 "돌지 않는다"로 가정)
  3. 슬롯 변경에 제약이 있는가? 쿨타임·비용 없이 자유롭게 바꾸는가 (산업·산업 레벨·캐릭터 세 가지 모두에 같은 질문이 걸린다)
  4. 슬롯 확장권을 상점에서 산다면 개당인가, 슬롯 번호별 누진인가?

해소됨:

  • 슬롯당 캐릭터 수 — 1명으로 확정
  • 동시 가동 — 슬롯 수만큼. 산업 택1 폐지
  • 판정 주체 — 서버 권위 확정
  • 실시간 푸시 — 필요함으로 확정 (푸시 해상도 1초. 채취주기와 무관 → 3.2)
  • 요일 로테이션 — 폐지 확정
  • 캐릭터 산업 적성 — ✅ 있다. 적성 0은 배치 불가 (2026-08-01 재확정)
  • 시작 슬롯 수 · 증가 수단 — ✅ 시작 2 · 상한 8 · 레벨 × 골드 (2026-08-01)
  • 설계 원칙 P3 — ✅ 재정의 완료 (액티브 일일 리셋) → 게임기획코어 1장 · 특성 2.4
  • 오프라인 정산 부담 — 오프라인 진행 폐지로 질문이 사라졌다

7. 서버 / 클라이언트 책임

책임 주체 이유
경과 시간 산정 · 판정 횟수 서버 클라 시계 조작 방지
드롭 추첨 서버 재화 생성 지점 (P4)
슬롯 상태 저장 (LastTickAt 포함) 서버 (DB) 진행도의 단일 원본
주기 푸시 (해상도 1초) 서버 접속 중 알림
채취 연출 · 카운트다운 클라이언트 LastTickAt + 주기로 로컬 계산. 값은 서버가 교정

클라이언트의 카운트다운이 서버와 어긋나도 문제되지 않는다. 연출일 뿐이고, 다음 푸시·조회에서 서버 값으로 맞춰진다.