최종 갱신 (KST)

← 일감 목록
T-128

서버 — 게임 시계 하나로 "지금"을 모으고 시간 조작 치트(AdvanceTime·ResetTime)를 둔다

진우 Resolve 우선순위 보통 목표 261024

배경

처음엔 T-126으로 만들었으나 창고 일감(T-126)이 먼저 그 번호를 써서 T-128로 옮겼다(2026-10-06). 첫 커밋 메시지의 (T-126)은 이 일감이다.

이슈 #48(클라 요청 · T-106). 경매 만료 · 거래소 전일 평균 · 받은 우편 7일 삭제 · 전체 우편 기간을 진짜로 기다리지 않고는 시험할 수 없었다. 서버의 "지금"이 핸들러마다 DateTime.UtcNow, 경매장은 따로 TimeProvider라 한 번에 넘길 자리가 없었다.

결정 (진우, 2026-10-06)

  • 채취는 쌓이지 않는다 — 넘기는 순간 슬롯 기준 시각도 같이 민다. 보상이 크게 튀지 않게
  • 오프셋은 저장하지 않는다 — 재시작하면 0

할 일

  • [x] GameClock(실제 시각 + 오프셋) — 핸들러·채취 스케줄러·경매 릴레이·로그인/종료 시각을 이 시계로. 세션 감시·gRPC 기한·로그·구매 ID는 실제 시간 그대로
  • [x] 경매장 GameTimeProvider + SetClockOffset RPC — 메인 릴레이가 바퀴(1초)마다 보낸다(경매장 재시작에도 다시 맞는다). 청소 타이머는 실제 시간
  • [x] 우편 시각 — 이미 코드가 now를 넘겨 저장하고 있었다(received_at·claimed_at·ends_at). 바꿀 것 없음
  • [x] S_ServerTimeResponse { ServerNowUnixMs }(69) — 로그인 직후(S_LoginResponse 바로 뒤) · 시간 치트 직후. Unity ServerTimeReceived 이벤트 · 더미 클라 핸들러
  • [x] 치트 — AdvanceTime = 12(초, 1~365일, 누적) · ResetTime = 13. 이슈에 적힌 11은 SetTraitLevel(T-115)이 먼저 써서 12·13
  • [x] TokenBucket — 시계가 뒤로 가면(ResetTime) 기준을 당긴다. 두면 넘겼던 만큼 검색이 막힌다
  • [x] 테스트 — GameClockTest · UserCheatTimeTest(채취 안 쌓임 · 다른 접속자 · 릴레이 깨움 · 초기화) · 릴레이 오프셋 동기화 · TokenBucket 역행 · 경매장 GameTimeProvider·gRPC 만료 — 메인 770(1 건너뜀) · 경매장 112 통과
  • [x] 문서 — 치트.md 2장 · 5장(게임 시계) · 경매장.md 5장 · Server/CLAUDE.md 서버 작업 규칙("지금"은 게임 시계에서만)
  • [ ] 실측 — 더미 클라 또는 클라 치트 창(T-106)으로 경매 등록 → AdvanceTime 172800 → 만료 우편 · 우편 받기 → AdvanceTime 604800 → 재접속 시 삭제

완료 조건

AdvanceTime으로 시간을 넘기면 경매 만료·전일 평균·우편 삭제가 그 시각으로 판정되고, 채취는 넘긴 만큼 쌓이지 않는다.

막고 있는 것 / 선행 일감

  • 없음

관련 커밋

  • 487f50d — 게임 시계와 시간 조작 치트를 추가한다