> ## Documentation Index
> Fetch the complete documentation index at: https://playwave.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 세션 라이프사이클

> PlayWave 게임 세션의 생성부터 종료까지 상세 흐름

## 세션 상태

게임 세션은 다음 상태를 순서대로 거칩니다.

```mermaid theme={null}
stateDiagram-v2
    [*] --> NONE
    NONE --> VERIFYING : OTT 발견
    VERIFYING --> ACTIVE : 검증 성공
    VERIFYING --> NONE : 검증 실패
    ACTIVE --> ENDING : 종료 요청
    ENDING --> ENDED : API 응답
    ENDED --> [*]
```

| 상태          | 설명                        |
| ----------- | ------------------------- |
| `NONE`      | 유저 접속 전. LaunchData 확인 대기 |
| `VERIFYING` | OTT 검증 API 호출 중           |
| `ACTIVE`    | 검증 성공. 하트비트 루프 실행 중       |
| `ENDING`    | 세션 종료 API 호출 중            |
| `ENDED`     | 세션 종료 완료                  |

## 세션 생성

유저가 PlayWave 런처를 통해 게임에 접속하면 `LaunchData`에 OTT가 포함됩니다.

```lua theme={null}
-- 서버에서 OTT 추출
local joinData = player:GetJoinData()
local ott = joinData.LaunchData  -- OTT UUID 문자열
```

OTT가 있으면 `POST /v1/game/session/verify`를 호출하여 검증합니다. 검증 성공 시 `game_session_id`를 받고 세션이 `ACTIVE` 상태가 됩니다.

<Note>
  PlayWave 런처 없이 접속한 유저는 `LaunchData`가 비어있습니다. 이 경우 검증을 건너뛰고 일반 유저로 처리합니다.
</Note>

## 하트비트

세션이 `ACTIVE` 상태가 되면 **2분(120초) 간격**으로 하트비트를 전송합니다.

```lua theme={null}
-- 하트비트 루프
while activeSessions[userId] do
    task.wait(120)  -- 2분 대기
    apiRequest("PATCH", "/game/session/heartbeat", {
        game_session_id = gameSessionId,
        provider_user_id = providerUserId,
    })
end
```

### 하트비트 응답 처리

| result             | 의미                | 처리                         |
| ------------------ | ----------------- | -------------------------- |
| `OK`               | 정상                | 다음 하트비트까지 대기               |
| `CHARGE_EXHAUSTED` | G-coin 소진         | 유저에게 안내. 서버가 2분 유예 후 자동 종료 |
| `SESSION_REPLACED` | 같은 PC에서 다른 세션 시작됨 | 하트비트 중지. 세션 종료 API 호출 불필요  |

### 하트비트 에러 처리

| HTTP 상태 | 의미            | 처리            |
| ------- | ------------- | ------------- |
| 404     | 세션 없음 (만료/삭제) | 하트비트 중지, 유저 킥 |
| 410     | 세션 이미 종료      | 하트비트 중지, 유저 킥 |
| 5xx     | 서버 오류         | 다음 주기에 재시도    |

<Warning>
  하트비트가 **4분 이상** 수신되지 않으면 서버에서 세션이 자동 종료됩니다. 2분 간격을 유지하세요.
</Warning>

## 세션 종료

세션은 다음 6가지 경우에 종료됩니다.

### 1. 정상 종료 (normal)

유저가 게임을 나가면 `PlayerRemoving` 이벤트에서 세션 종료 API를 호출합니다.

```lua theme={null}
Players.PlayerRemoving:Connect(function(player)
    endPlayerSession(player.UserId)
end)
```

### 2. 서버 종료 (BindToClose)

Roblox 서버가 종료될 때 모든 활성 세션을 정리합니다.

```lua theme={null}
game:BindToClose(function()
    for userId in pairs(activeSessions) do
        endPlayerSession(userId)
    end
end)
```

### 3. 하트비트 타임아웃 (heartbeat\_timeout)

4분간 하트비트가 수신되지 않으면 서버에서 자동 종료합니다. PC 강제 종료, 네트워크 장애 등의 경우에 해당합니다.

### 4. 세션 교체 (session\_replace)

같은 PC에서 다른 게임을 실행하면 기존 게임 세션이 자동 종료되고 새 세션이 생성됩니다. 기존 세션의 하트비트에 `SESSION_REPLACED`가 반환됩니다.

### 5. 런처 하트비트 타임아웃

게임 서버 하트비트 처리 시 서버는 런처의 생존도 확인합니다. 런처가 **4분 이상** 하트비트를 보내지 않으면 서버가 게임 세션을 종료합니다 (HTTP `410` 반환). PlayWave 런처가 크래시되거나 강제 종료된 경우 발생합니다.

### 6. G-coin 소진 (charge\_exhausted)

하트비트 응답에서 `CHARGE_EXHAUSTED`를 수신하면 서버가 2분 유예 후 세션을 자동 종료합니다.

## 타임라인 예시

### 정상 종료

```mermaid theme={null}
gantt
    title 정상 세션 타임라인 (45분 플레이)
    dateFormat mm:ss
    axisFormat %M:%S

    section 세션
    OTT 검증 + 세션 생성       :milestone, 00:00, 0m
    게임 플레이                 :active, play, 00:00, 45m

    section 하트비트
    HB #1 OK                   :milestone, 02:00, 0m
    HB #2 OK                   :milestone, 04:00, 0m
    HB #3 OK                   :milestone, 06:00, 0m
    ...                        :milestone, 08:00, 0m

    section 종료
    PlayerRemoving → DELETE     :crit, milestone, 45:00, 0m
```

### 비정상 종료

```mermaid theme={null}
sequenceDiagram
    participant GS as 게임 서버
    participant API as PlayWave 서버
    participant Redis as Redis

    Note over GS, Redis: 00:00 — 유저 접속, 세션 생성
    GS->>API: 02:00 하트비트 #1
    API-->>GS: OK
    GS->>API: 04:00 하트비트 #2
    API-->>GS: OK

    Note over GS: 04:30 — PC 강제 종료 (전원 차단)

    GS--xAPI: 06:00 하트비트 #3 전송 실패
    GS--xAPI: 08:00 하트비트 #4 전송 실패

    Note over Redis: 10:00 — Redis TTL 만료
    API->>API: 세션 자동 종료
    API->>API: 마지막 하트비트(04:00) 기준 플레이 시간 기록
```
