> For the complete documentation index, see [llms.txt](https://ak-scripts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ak-scripts.gitbook.io/docs/yi-lai/markdown/gong-neng-mo-kuai-dao-chu/zhuang-tai-suo-ding-state.md).

# 状态锁定（State）

## 🔒 State（状态锁定）模块导出文档

### 📋 概述

State 模块提供了轻量级的玩家状态管理机制，用于管理玩家的各种状态（如 UI 打开、处理中等），并提供锁定/解锁功能，防止状态冲突。

### 🚀 导出方式

#### 服务端

**方式 1：获取模块对象（推荐）⭐**

```lua
local State = exports['ak-lib']:StateModule()
```

**方式 2：单独导出函数**

```lua
exports['ak-lib']:SetPlayerState(source, 'ui', 'shop_menu')
```

#### 客户端

**方式 1：获取模块对象（推荐）⭐**

```lua
local State = exports['ak-lib']:State()
```

**方式 2：单独导出函数**

```lua
exports['ak-lib']:SetState('ui', 'shop_menu')
```

### 📖 可用导出

#### 服务端

**模块对象方法**

使用 `exports['ak-lib']:StateModule()` 获取的对象包含以下方法：

| 方法                                  | 说明                 | 参数                                                              | 返回值     |
| ----------------------------------- | ------------------ | --------------------------------------------------------------- | ------- |
| `SetState(source, state, lockId)`   | 设置玩家状态             | `source`, `state` (string), `lockId` (string, 可选, 默认 'default') | boolean |
| `HasState(source, state, lockId)`   | 检查玩家是否有指定状态        | `source`, `state`, `lockId` (可选)                                | boolean |
| `ClearState(source, state, lockId)` | 清除玩家状态             | `source`, `state`, `lockId` (可选)                                | boolean |
| `ClearAllStates(source, lockId)`    | 清除玩家的所有状态          | `source`, `lockId` (可选)                                         | boolean |
| `GetStates(source)`                 | 获取玩家的所有状态          | `source`                                                        | table   |
| `Lock(source, lockId)`              | 锁定玩家               | `source`, `lockId` (可选)                                         | boolean |
| `Unlock(source, lockId)`            | 解锁玩家               | `source`, `lockId` (可选)                                         | boolean |
| `IsLocked(source, lockId)`          | 检查玩家是否被锁定          | `source`, `lockId` (可选)                                         | boolean |
| `IsBusy(source)` ⭐                  | 检查玩家是否处于任何状态（是否忙碌） | `source`                                                        | boolean |

**单独导出函数**

| 导出                                        | 说明     | 参数                          | 返回值          |
| ----------------------------------------- | ------ | --------------------------- | ------------ |
| `StateModule()`                           | 返回模块对象 | -                           | `AKState` 对象 |
| `SetPlayerState(source, state, lockId)`   | 设置玩家状态 | `source`, `state`, `lockId` | boolean      |
| `HasPlayerState(source, state, lockId)`   | 检查玩家状态 | `source`, `state`, `lockId` | boolean      |
| `ClearPlayerState(source, state, lockId)` | 清除玩家状态 | `source`, `state`, `lockId` | boolean      |
| `ClearAllPlayerStates(source, lockId)`    | 清除所有状态 | `source`, `lockId`          | boolean      |
| `GetPlayerStates(source)`                 | 获取所有状态 | `source`                    | table        |
| `LockPlayer(source, lockId)`              | 锁定玩家   | `source`, `lockId`          | boolean      |
| `UnlockPlayer(source, lockId)`            | 解锁玩家   | `source`, `lockId`          | boolean      |
| `IsPlayerLocked(source, lockId)`          | 检查是否锁定 | `source`, `lockId`          | boolean      |
| `IsPlayerBusy(source)` ⭐                  | 检查是否忙碌 | `source`                    | boolean      |

#### 客户端

（同上，但方法名不同，且不需要 `source` 参数）

| 导出                          | 说明     | 参数                | 返回值          |
| --------------------------- | ------ | ----------------- | ------------ |
| `State()`                   | 返回模块对象 | -                 | `AKState` 对象 |
| `SetState(state, lockId)`   | 设置状态   | `state`, `lockId` | boolean      |
| `HasState(state, lockId)`   | 检查状态   | `state`, `lockId` | boolean      |
| `ClearState(state, lockId)` | 清除状态   | `state`, `lockId` | boolean      |
| `ClearAllStates(lockId)`    | 清除所有状态 | `lockId`          | boolean      |
| `GetStates()`               | 获取所有状态 | -                 | table        |
| `Lock(lockId)`              | 锁定玩家   | `lockId`          | boolean      |
| `Unlock(lockId)`            | 解锁玩家   | `lockId`          | boolean      |
| `IsLocked(lockId)`          | 检查是否锁定 | `lockId`          | boolean      |
| `IsBusy()` ⭐                | 检查是否忙碌 | -                 | boolean      |

### 💡 使用示例

#### 服务端：商店系统

```lua
local State = exports['ak-lib']:StateModule()

RegisterNetEvent('shop:open', function()
    local source = source
    
    -- 检查玩家是否被锁定
    if State.IsLocked(source) then
        TriggerClientEvent('ak-lib:Notify', source, {
            type = 'error',
            message = '你当前无法打开商店'
        })
        return
    end
    
    -- 检查是否有其他 UI 打开
    if State.HasState(source, 'ui') then
        TriggerClientEvent('ak-lib:Notify', source, {
            type = 'error',
            message = '请先关闭其他界面'
        })
        return
    end
    
    -- 推荐：使用 IsBusy 检查（最常用）⭐
    if State.IsBusy(source) then
        TriggerClientEvent('ak-lib:Notify', source, {
            type = 'error',
            message = '你当前正在处理其他事务'
        })
        return
    end
    
    -- 设置状态
    State.SetState(source, 'ui', 'shop')
    State.Lock(source, 'shop')
    
    -- 打开商店
    TriggerClientEvent('shop:client:open', source)
end)

RegisterNetEvent('shop:close', function()
    local source = source
    
    -- 清除状态
    State.ClearState(source, 'ui', 'shop')
    State.Unlock(source, 'shop')
end)
```

#### 客户端：UI 管理

```lua
local State = exports['ak-lib']:State()

RegisterCommand('menu', function()
    -- 推荐：使用 IsBusy 检查
    if State.IsBusy() then
        exports['ak-lib']:Notify({
            type = 'error',
            message = '你当前正在处理其他事务'
        })
        return
    end
    
    -- 或者检查特定状态
    if State.IsLocked() then
        exports['ak-lib']:Notify({
            type = 'error',
            message = '你当前被锁定，无法打开菜单'
        })
        return
    end
    
    -- 设置状态
    State.SetState('ui', 'main_menu')
    
    -- 打开菜单
    OpenMenu()
end, false)

function CloseMenu()
    -- 清除状态
    State.ClearState('ui', 'main_menu')
    -- 关闭菜单 UI
end
```

#### lockId 机制示例

```lua
local State = exports['ak-lib']:StateModule()

-- 插件 A 设置 ui 状态
State.SetState(source, 'ui', 'shop_menu')

-- 插件 B 也设置 ui 状态（不同的 lockId）
State.SetState(source, 'ui', 'inventory_menu')

-- 检查是否有 ui 状态（任意一个）
if State.HasState(source, 'ui') then
    -- 有 UI 打开
end

-- 检查特定锁
if State.HasState(source, 'ui', 'shop_menu') then
    -- shop_menu 处于 ui 状态
end

-- 清除特定锁
State.ClearState(source, 'ui', 'shop_menu')

-- 清除所有 ui 状态
State.ClearState(source, 'ui')
```

#### 与 Progress 模块集成

```lua
local State = exports['ak-lib']:State()

-- 在开始进度条时设置状态
CreateThread(function()
    if State.HasState('processing') then
        return
    end
    
    State.SetState('processing', 'progress')
    
    -- 进度条逻辑
    exports['ak-lib']:Progress({
        duration = 5000,
        label = '处理中...',
        onComplete = function()
            State.ClearState('processing', 'progress')
        end,
        onCancel = function()
            State.ClearState('processing', 'progress')
        end
    })
end)
```

### 📝 API 详细说明

#### 状态类型

**预定义状态**

* `locked`：完全锁定（使用 `Lock()` / `Unlock()` 管理）
* `ui`：UI 界面打开中
* `processing`：处理中（进度条、任务等）

**自定义状态**

插件可以根据需要定义自己的状态：

```lua
-- 商店
State.SetState(source, 'shopping', 'shop_id_123')

-- 交易
State.SetState(source, 'trading', 'trade_id_456')

-- 对话
State.SetState(source, 'dialog', 'npc_dialog_789')
```

#### lockId 机制

每个状态可以支持多个锁（lockId），这样不同插件可以独立管理同一个状态：

```lua
-- 不同插件使用不同的 lockId
State.SetState(source, 'ui', 'shop_menu')      -- 商店插件
State.SetState(source, 'ui', 'inventory_menu') -- 背包插件

-- 检查是否有任意 ui 状态
if State.HasState(source, 'ui') then
    -- 有 UI 打开
end

-- 检查特定锁
if State.HasState(source, 'ui', 'shop_menu') then
    -- shop_menu 处于 ui 状态
end
```

#### IsBusy() ⭐ 推荐

`IsBusy()` 是最常用的检查方式，它会检查玩家是否有任何状态：

```lua
-- 等同于检查多个状态
-- State.IsLocked(source) or State.HasState(source, 'ui') or State.HasState(source, 'processing')

if State.IsBusy(source) then
    -- 玩家正在处理其他事务
end
```

#### GetStates() 返回值格式

```lua
local states = State.GetStates(source)
-- 返回例如: { ui = { 'shop_menu', 'inventory' }, processing = { 'task_123' } }

for state, locks in pairs(states) do
    print(string.format('状态 %s: %d 个锁', state, #locks))
    for _, lockId in ipairs(locks) do
        print(string.format('  - %s', lockId))
    end
end
```

### ⚠️ 注意事项

1. **使用有意义的 lockId**：使用能标识具体操作的 lockId（如 `'shop_123'` 而不是 `'default'`）。
2. **及时清理状态**：在操作完成或取消时及时清除状态，避免状态泄露。
3. **检查状态冲突**：在执行可能冲突的操作前检查相关状态。
4. **使用锁定机制**：对于需要完全禁止交互的场景，使用 `Lock()` 而不是自定义状态。
5. **玩家退出清理**：模块已自动处理玩家退出时的状态清理，无需手动处理。
6. **状态优先级**：`locked` 状态应该优先检查，因为它表示完全禁止。
7. **避免状态泄露**：使用 `lockId` 机制，确保每个插件只管理自己的状态。
8. **服务端权威**：客户端状态仅用于 UI 显示，关键状态应在服务端设置和验证。
9. **状态不是权限**：State 模块只管理状态，不涉及权限验证，权限检查应使用 Framework 模块。
10. **状态不是 Cooldown**：State 管理的是"当前状态"，不是"时间限制"，时间限制应使用 Cooldown 模块。
11. **状态同步**：服务端状态会自动同步到客户端，客户端状态会被服务端状态覆盖。
12. **推荐使用 IsBusy**：对于大多数场景，使用 `IsBusy()` 比检查多个状态更简洁高效。

### 🔗 相关文档

* 状态锁定说明
* Framework 模块导出文档


---

# 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://ak-scripts.gitbook.io/docs/yi-lai/markdown/gong-neng-mo-kuai-dao-chu/zhuang-tai-suo-ding-state.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.
