# Snapshots

### Overview

Snapshots capture the player's current activity state at a specific moment, including animation status, movement status, and timing information. This is primarily used internally by the `awaitIdle()` and `awaitIdleState()` functions, but can also be useful for debugging and advanced script logic.

***

### Core Classes

#### `PlayerActivitySnapshot`

Captures minimal player activity information for idle checks:

```kotlin
data class PlayerActivitySnapshot(
    val captureTick: Int,        // Server tick when captured
    val animationId: Int,        // Player's animation ID (-1 if idle)
    val isMoving: Boolean?       // Whether player is moving (null if unknown)
)
```

**Properties**:

* `captureTick` - The server tick when this snapshot was taken
* `animationId` - Current animation ID (-1 means idle)
* `isMoving` - Movement status (true/false/null)

**Computed Properties**:

* `isAnimationIdle` - Returns `true` if animationId == -1
* `isMovementIdle` - Returns `true` if not moving

#### `IdleAwaitResult`

Result of awaiting player idleness with diagnostic details:

```kotlin
sealed class IdleAwaitResult {
    data class Idle(val snapshot: PlayerActivitySnapshot) : IdleAwaitResult()
    data class Timeout(
        val snapshot: PlayerActivitySnapshot,
        val animationActive: Boolean,
        val movementActive: Boolean
    ) : IdleAwaitResult()
}
```

**Cases**:

* `Idle` - Player became idle successfully (includes snapshot)
* `Timeout` - Timeout occurred while waiting (includes diagnostic info)

***

### Usage Examples

#### Basic Idle Detection

```kotlin
class MyScript : StateScript<MyState>(debug = true) {
    
    override suspend fun onTick() {
        // Simple idle check (returns boolean)
        val isIdle = awaitIdle(timeout = 10, includeMovement = true)
        
        if (isIdle) {
            println("Player is idle")
        } else {
            println("Player did not become idle within timeout")
        }
    }
}
```

#### Detailed Idle Monitoring

```kotlin
class MyScript : StateScript<MyState>(debug = true) {
    
    override suspend fun onTick() {
        // Detailed idle check with diagnostics
        when (val result = awaitIdleState(timeout = 10, includeMovement = true)) {
            is IdleAwaitResult.Idle -> {
                val snapshot = result.snapshot
                println("Player became idle at tick ${snapshot.captureTick}")
                println("Animation ID: ${snapshot.animationId}")
                println("Was moving: ${snapshot.isMoving}")
            }
            is IdleAwaitResult.Timeout -> {
                val snapshot = result.snapshot
                println("Timeout waiting for idle at tick ${snapshot.captureTick}")
                println("Animation still active: ${result.animationActive}")
                println("Movement still active: ${result.movementActive}")
                println("Current animation ID: ${snapshot.animationId}")
            }
        }
    }
}
```

#### Activity Monitoring

```kotlin
class MyScript : StateScript<MyState>(debug = true) {
    
    private fun monitorPlayerActivity() {
        // You can access the internal capture function through reflection
        // or create your own monitoring logic
        
        // Example: Check if player is currently idle
        val player = LocalPlayer.self()
        val isCurrentlyIdle = player?.animationId == -1 && !(player?.isMoving ?: false)
        
        if (isCurrentlyIdle) {
            println("Player is currently idle")
        } else {
            println("Player is active - Animation: ${player?.animationId}, Moving: ${player?.isMoving}")
        }
    }
}
```

#### Advanced Activity Logging

```kotlin
class MyScript : StateScript<MyState>(debug = true) {
    
    override suspend fun onTick() {
        // Log activity before and after actions
        val result = awaitIdleState(timeout = 15, includeMovement = true)
        
        when (result) {
            is IdleAwaitResult.Idle -> {
                logActivity("Action completed", result.snapshot)
                // Continue with next action
            }
            is IdleAwaitResult.Timeout -> {
                logActivity("Action timed out", result.snapshot)
                logTimeoutDetails(result)
                // Handle timeout case
            }
        }
    }
    
    private fun logActivity(context: String, snapshot: PlayerActivitySnapshot) {
        println("[$context] Tick: ${snapshot.captureTick}")
        println("[$context] Animation: ${snapshot.animationId} (idle: ${snapshot.isAnimationIdle})")
        println("[$context] Movement: ${snapshot.isMoving} (idle: ${snapshot.isMovementIdle})")
    }
    
    private fun logTimeoutDetails(result: IdleAwaitResult.Timeout) {
        println("Timeout Details:")
        println("- Animation still active: ${result.animationActive}")
        println("- Movement still active: ${result.movementActive}")
        println("- Final tick: ${result.snapshot.captureTick}")
    }
}
```

***

### Parameters

#### `awaitIdle()` and `awaitIdleState()`

| Parameter         | Type    | Default | Description                        |
| ----------------- | ------- | ------- | ---------------------------------- |
| `timeout`         | Int     | 10      | Maximum ticks to wait for idleness |
| `includeMovement` | Boolean | true    | Whether to check movement status   |

**Timeout Behavior**:

* `timeout = 0` - Returns immediately with current state
* `timeout > 0` - Waits up to that many ticks
* `timeout < 0` - Throws `IllegalArgumentException`

**Movement Inclusion**:

* `includeMovement = true` - Player must be idle in both animation AND movement
* `includeMovement = false` - Only checks animation idleness

***

### Activity Detection Logic

#### Idle Conditions

A player is considered idle when **ALL** of these conditions are met:

1. **Animation Idle**: `animationId == -1`
2. **Movement Idle**: `isMoving != true` (when `includeMovement = true`)
3. **Timing**: Conditions must be met before timeout

#### Snapshot Capture

Snapshots are captured using:

```kotlin
private fun capturePlayerActivity(): PlayerActivitySnapshot {
    val tick = Client.getServerTick()
    val player = LocalPlayer.self()
    return PlayerActivitySnapshot(
        captureTick = tick,
        animationId = player?.animationId ?: -1,
        isMoving = player?.isMoving
    )
}
```

***

### Common Use Cases

#### 1. **Action Completion Detection**

```kotlin
// Wait for chopping animation to complete
val isIdle = awaitIdle(timeout = 30, includeMovement = true)
if (isIdle) {
    // Tree chopping completed, move to next tree
    nextState()
}
```

#### 2. **Timeout Handling**

```kotlin
when (val result = awaitIdleState(timeout = 15)) {
    is IdleAwaitResult.Idle -> {
        // Action completed successfully
        handleSuccess()
    }
    is IdleAwaitResult.Timeout -> {
        // Action took too long, handle accordingly
        if (result.animationActive) {
            println("Animation still running - may be stuck")
        }
        if (result.movementActive) {
            println("Still moving - may be pathing")
        }
        handleTimeout()
    }
}
```

#### 3. **Debug Information**

```kotlin
// Get detailed information about why an action might be slow
val result = awaitIdleState(timeout = 20)
when (result) {
    is IdleAwaitResult.Timeout -> {
        println("Debug Info:")
        println("- Animation ID: ${result.snapshot.animationId}")
        println("- Moving: ${result.snapshot.isMoving}")
        println("- Tick captured: ${result.snapshot.captureTick}")
    }
}
```

***

### Best Practices

1. **Choose Appropriate Timeouts**:
   * Short actions: 5-10 ticks
   * Long actions: 15-30 ticks
   * Complex actions: 30+ ticks
2. **Use Movement Detection Wisely**:
   * `includeMovement = true` for stationary actions (chopping, mining)
   * `includeMovement = false` for actions that involve movement (banking, traveling)
3. **Handle Timeouts Gracefully**:
   * Always check for timeout results
   * Use diagnostic information to understand failures
   * Implement retry logic when appropriate
4. **Debug with Snapshots**:
   * Use `awaitIdleState()` instead of `awaitIdle()` when you need diagnostics
   * Log snapshot information for troubleshooting
   * Monitor animation IDs to understand what actions are running

***

### Integration with Script Types

All script types inherit snapshot functionality through `SuspendableScript`:

```kotlin
// Works in all script types
class MyLoopingScript : LoopingScript() {
    override suspend fun onTick() {
        awaitIdle() // Available here
    }
}

class MyStateScript : StateScript<MyState>(debug = true) {
    override suspend fun onTick() {
        awaitIdleState(timeout = 15) // Available here
    }
}

class MyPermissiveScript : PermissiveScript<MyState>(debug = true) {
    override fun init() {
        // Snapshots available in suspend functions
    }
}
```

The snapshot system provides powerful diagnostics and monitoring capabilities for understanding and debugging player activity states in your scripts.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kxapi.gitbook.io/kxapi/scripting/snapshots.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
