> ## 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.

# Session lifecycle

> Game session lifecycle from creation to termination

## Session states

A game session transitions through the following states.

```mermaid theme={null}
stateDiagram-v2
    [*] --> NONE
    NONE --> VERIFYING : OTT found
    VERIFYING --> ACTIVE : Verification success
    VERIFYING --> NONE : Verification failed
    ACTIVE --> ENDING : End requested
    ENDING --> ENDED : API response
    ENDED --> [*]
```

| State       | Description                                        |
| ----------- | -------------------------------------------------- |
| `NONE`      | Before user connects. Waiting for LaunchData check |
| `VERIFYING` | OTT verification API call in progress              |
| `ACTIVE`    | Verification succeeded. Heartbeat loop running     |
| `ENDING`    | Session end API call in progress                   |
| `ENDED`     | Session terminated                                 |

## Session creation

When a user connects through the PlayWave launcher, `LaunchData` contains the OTT.

```lua theme={null}
-- Extract OTT on the server
local joinData = player:GetJoinData()
local ott = joinData.LaunchData  -- OTT UUID string
```

If an OTT is present, call `POST /v1/game/session/verify` to verify it. On success, the `game_session_id` is returned and the session becomes `ACTIVE`.

<Note>
  Users connecting without the PlayWave launcher will have empty `LaunchData`. In this case, verification is skipped and the user is treated as a normal player.
</Note>

## Heartbeat

Once the session is `ACTIVE`, heartbeats are sent every **2 minutes (120 seconds)**.

```lua theme={null}
-- Heartbeat loop
while activeSessions[userId] do
    task.wait(120)  -- 2 min wait
    apiRequest("PATCH", "/game/session/heartbeat", {
        game_session_id = gameSessionId,
        provider_user_id = providerUserId,
    })
end
```

### Heartbeat response handling

| result             | Meaning                            | Action                                                       |
| ------------------ | ---------------------------------- | ------------------------------------------------------------ |
| `OK`               | Normal                             | Wait until next heartbeat                                    |
| `CHARGE_EXHAUSTED` | G-coin depleted                    | Notify user. Server auto-terminates after 2 min grace period |
| `SESSION_REPLACED` | Another session started on same PC | Stop heartbeat. Do not call session end                      |

### Heartbeat error handling

| HTTP Status | Meaning                             | Action                    |
| ----------- | ----------------------------------- | ------------------------- |
| 404         | Session not found (expired/deleted) | Stop heartbeat, kick user |
| 410         | Session already ended               | Stop heartbeat, kick user |
| 5xx         | Server error                        | Retry on next cycle       |

<Warning>
  If no heartbeat is received for **4 minutes or more**, the server automatically terminates the session. Maintain the 2-minute interval.
</Warning>

## Session termination

Sessions can end in 6 ways.

### 1. Normal exit (normal)

When a user leaves the game, the session end API is called in the `PlayerRemoving` event.

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

### 2. Server shutdown (BindToClose)

When the Roblox server shuts down, all active sessions are cleaned up.

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

### 3. Heartbeat timeout (heartbeat\_timeout)

If no heartbeat is received for 4 minutes, the server automatically terminates the session. This covers cases like PC power-off or network failure.

### 4. Session replace (session\_replace)

If a different game is launched from the same PC, the existing game session is auto-terminated and a new session is created. The heartbeat for the old session will return `SESSION_REPLACED`.

### 5. Launcher heartbeat timeout

The server checks launcher liveness during game server heartbeats. If the launcher hasn't sent a heartbeat for over **4 minutes**, the server terminates the game session (returns HTTP `410`). This happens when the launcher crashes or is force-closed while the game is still running.

### 6. G-coin exhausted (charge\_exhausted)

When `CHARGE_EXHAUSTED` is received in a heartbeat response, the server auto-terminates the session after a 2-minute grace period.

## Timeline examples

### Normal exit

```mermaid theme={null}
gantt
    title Normal session timeline (45 min play)
    dateFormat mm:ss
    axisFormat %M:%S

    section Session
    OTT verify + session create    :milestone, 00:00, 0m
    Gameplay                       :active, play, 00:00, 45m

    section Heartbeat
    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 End
    PlayerRemoving → DELETE        :crit, milestone, 45:00, 0m
```

### Abnormal exit

```mermaid theme={null}
sequenceDiagram
    participant GS as Game Server
    participant API as PlayWave Server
    participant Redis as Redis

    Note over GS, Redis: 00:00 — User connects, session created
    GS->>API: 02:00 Heartbeat #1
    API-->>GS: OK
    GS->>API: 04:00 Heartbeat #2
    API-->>GS: OK

    Note over GS: 04:30 — PC forced shutdown (power off)

    GS--xAPI: 06:00 Heartbeat #3 failed
    GS--xAPI: 08:00 Heartbeat #4 failed

    Note over Redis: 10:00 — Redis TTL expired
    API->>API: Session auto-terminated
    API->>API: Play time recorded based on last heartbeat (04:00)
```
