라이프 시스템

퍼즐 게임의 핵심 자원인 라이프의 회복, 소모, 시간제 라이프 관리를 위한 서버 처리 흐름을 살펴봅니다.

남은 이동20
🧩

준비 완료!

목표를 달성하여 스테이지를 클리어하세요.

핵심 정리

  • 라이프는 두 종류: 시간 기반 회복되는 일반 라이프와, 이벤트/선물로 받는 시간제 라이프가 있습니다.
  • 시간제 라이프가 있으면 일반 라이프보다 먼저 소모됩니다.
  • 라이프 회복은 서버에서 lastRecoveryAt 기준으로 검증하여 시간 조작을 방지합니다.
  • max 라이프 값은 세션에서 가져오며, max 상태에서 회복 요청이 오면 서버가 거부합니다.
  • 게임 시작(Start API) 시 라이프 소모가 함께 처리되어야 합니다.

1. 라이프 회복 API

클라이언트가 시간 경과를 확인한 뒤 호출합니다. 서버는 lastRecoveryAt를 기준으로 회복 가능량을 계산하고, max를 초과하지 않도록 검증합니다.

ClientGame ServerDatabasePOST /api/life/recover { userId }SELECT game_session (userId)session { maxLife }SELECT user_life (userId)userLife { current, lastRecoveryAt }시간제 라이프 확인회복 가능량 계산 (now - lastRecoveryAt)max 초과 검증UPDATE user_life (current, lastRecoveryAt)OK200 { current, max }12345678
1func (s *LifeService) RecoverLife(ctx context.Context, userID string) (resp *RecoverResponse, err error) {
2 tx, err := s.db.BeginTx(ctx, nil)
3 if err != nil {
4 return nil, err
5 }
6 defer func() {
7 if err != nil { tx.Rollback() } else { tx.Commit() }
8 }()
9
10 session, err := s.repo.GetSessionByUser(ctx, tx, userID)
11 if err != nil {
12 return nil, err
13 }
14
15 life, err := s.repo.GetUserLife(ctx, tx, userID)
16 if err != nil {
17 return nil, err
18 }
19
20 // 시간제 라이프가 있으면 회복 불필요
21 timedCount, err := s.repo.CountActiveTimed(ctx, tx, userID)
22 if err != nil {
23 return nil, err
24 }
25 if timedCount > 0 {
26 return &RecoverResponse{Current: life.Current, Max: session.MaxLife}, nil
27 }
28
29 // lastRecoveryAt 기준 회복 가능량 계산
30 elapsed := time.Since(life.LastRecoveryAt)
31 recoverable := int(elapsed / RecoveryInterval)
32 if recoverable <= 0 {
33 return &RecoverResponse{Current: life.Current, Max: session.MaxLife}, nil
34 }
35
36 // max 초과 검증
37 if life.Current >= session.MaxLife {
38 return nil, ErrAlreadyMaxLife
39 }
40
41 life.Current = min(life.Current+recoverable, session.MaxLife)
42 life.LastRecoveryAt = life.LastRecoveryAt.Add(
43 time.Duration(recoverable) * RecoveryInterval,
44 )
45 if err := s.repo.SaveUserLife(ctx, tx, life); err != nil {
46 return nil, err
47 }
48
49 return &RecoverResponse{Current: life.Current, Max: session.MaxLife}, nil
50}

게임 비교

🚧

작성중입니다