Skip to content

玩家生命周期运行时语义

本文档包含 Shield 玩家生命周期管理、断线重连、离线消息缓存相关的运行时语义决策。

shield_player 是官方可选模块,不属于 shield_core,也不是当前 Lua API 最小契约。本文用于冻结未来边界,避免把玩家管理反向塞进 core。

当前状态:

  • 本文冻结 shield_player optional module 的边界契约;具体 Lua API 进入 Phase 2+。
  • shield.player.setup 主入口、PlayerRef 跨 service 引用、persistence adapter 为 P0 文档冻结项;实现顺序见文末"实现优先级"。
  • shield.player.*PlayerSessionPlayerRef、重连窗口和离线消息缓存都不属于当前最小单节点 runtime。
  • SessionHandle 仍只留在 gateway / shield_net 内部;shield_player 的公开语义只暴露 session_idPlayerSession(本地)和 PlayerRef(跨 service),不把 SessionHandle 作为跨 service 对象传递。
  • 即使启用 shield_player,普通 Lua service 仍保持 module-table + named method 语义;本模块不恢复 legacy on_message(src, type, data) 统一入口。
  • optional module 的横向 owner、配置归属和 disabled 语义见 官方可选模块契约
  • 如与 Lua API 契约 冲突,以 lua-api.md 为当前最小契约。

设计原则

  • 玩家生命周期与连接生命周期分离。
  • 断线不等于离线,支持短暂断线重连。
  • 离线期间的消息不丢失。
  • 状态管理由框架提供,业务逻辑由 Lua 实现。

Player 与 Service 的关系

Player 是 Service 的增强模式,不是独立的 Actor 类型。

概念说明
ServiceShield 的基本执行单元,拥有 Lua VM、mailbox、name
ActorService 的同义词,强调消息驱动模型
Player绑定网络连接的 Service,有额外的生命周期管理

Player Service = 普通 Service + PlayerSession 管理

┌─────────────────────────────────────────────────────────┐
│                   Player Service                        │
│  ┌────────────────────────────────────────────────────┐ │
│  │  Service 基础能力                                    │ │
│  │  - Lua VM                                          │ │
│  │  - Mailbox                                         │ │
│  │  - Name (可选)                                      │ │
│  │  - 消息处理 (send/call)                             │ │
│  └────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────┐ │
│  │  Player 增强能力                                    │ │
│  │  - PlayerSession (连接绑定)                         │ │
│  │  - 生命周期钩子 (on_auth/on_login/on_logout)        │ │
│  │  - 断线重连                                         │ │
│  │  - 离线消息缓存                                     │ │
│  │  - 数据持久化                                       │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

对比:

lua
-- 普通 Service(如 room、matchmaker)
local M = {}
function M.on_init(args) end
function M.on_exit(reason) end
function M.some_method() end
return M

-- Player Service(如 player、character)
local M = {}
function M.on_init(args) end
function M.on_exit(reason) end

-- 以下为 Player 特有的生命周期钩子
function M.on_auth(session_id, auth_data) end
function M.on_login(player) end
function M.on_client_message(player, payload) end
function M.on_disconnect(player, reason) end
function M.on_reconnect(player) end
function M.on_logout(player, reason) end
function M.on_save(player) end

return M

玩家会话模型

┌─────────────────────────────────────────────────────────┐
│                    连接层 (shield_net)                   │
│  Connection ←→ Session                                  │
├─────────────────────────────────────────────────────────┤
│                    玩家层 (shield_player)                │
│  Session ←→ PlayerSession                               │
│            ├── 状态: connecting → authenticating         │
│            │              → online → disconnecting       │
│            │              → offline                      │
│            ├── 断线重连窗口                              │
│            └── 离线消息队列                              │
├─────────────────────────────────────────────────────────┤
│                    业务层 (Lua service)                  │
│  PlayerSession ←→ Player Service                        │
└─────────────────────────────────────────────────────────┘

玩家会话状态机

                    ┌──────────────┐
                    │  connecting  │
                    └──────┬───────┘
                           │ on_connect

                    ┌──────────────┐
          ┌────────│authenticating│────────┐
          │        └──────┬───────┘        │
          │               │ auth_ok        │ auth_fail
          ▼               ▼                ▼
    ┌──────────┐   ┌──────────┐    ┌──────────┐
    │ rejected │   │  online  │    │ rejected │
    └──────────┘   └────┬─────┘    └──────────┘

           ┌────────────┼────────────┐
           │            │            │
           ▼            ▼            ▼
    ┌────────────┐ ┌──────────┐ ┌──────────┐
    │ reconnect  │ │  kicked  │ │  logout  │
    │  window    │ │          │ │          │
    └──────┬─────┘ └──────────┘ └──────────┘

     ┌─────┴─────┐
     ▼           ▼
┌─────────┐ ┌──────────┐
│ online  │ │  offline │
│(resume) │ │          │
└─────────┘ └──────────┘

shield.player.setup 主入口

业务 Player Service 应当通过 shield.player.setup 注册到框架,作为唯一推荐入口。

基本形态

lua
local M = {}

shield.player.setup(M, {
    -- 必填钩子:业务必须实现
    auth = function(self, session_id, auth_data) ... end,
    login = function(self, player) ... end,
    client_message = function(self, player, payload) ... end,

    -- 可选钩子:未提供时走框架默认实现(默认行为见下方"钩子调用时机"表)
    disconnect = function(self, player, reason) ... end,
    reconnect  = function(self, player) ... end,
    logout     = function(self, player, reason) ... end,
    save       = function(self, player) ... end,

    -- 可选:持久化 adapter(详见"持久化 adapter"章节)
    persistence = {
        backend = "redis",
        key = function(uid) return "player:" .. uid end,
        fields = { "profile", "inventory", "quests" },
        auto_save_interval = 300000,
        on_save_error = "log",
    },

    -- 可选:关联的 PlayerManager 名称
    manager = "player_manager",
})

-- 业务方法独立命名空间,与 setup opts 字段隔离
function M.get_profile(player, uid)
    return player.data.profile
end

return M

设计约束

  • setup 是 Player Service 唯一推荐入口;shield.player.Base 等高级风格留 Phase 2+,不在 P0 冻结。
  • opts 字段一律去掉 on_ 前缀(auth/login/client_message/disconnect/reconnect/logout/save),与 module-level on_init/on_exit 隔离命名空间;service-level hook 仍可保留 M.on_init/on_exit
  • 必填钩子缺失即返回 nil, Error{code="setup_invalid"},service 不进入 running 状态。
  • 未在 setup 中提供的可选钩子由框架按明列的默认实现执行,不允许"未提供即 noop"的隐式行为。
  • 业务方法(与 setup opts 同级的 M.xxx)保留普通 service method dispatch 语义,setup 不改写。

玩家生命周期钩子

钩子定义(setup opts 字段对应)

lua
local M = {}

-- 玩家认证(连接后第一个钩子)
function M.on_auth(session_id, auth_data)
    -- auth_data: 客户端发送的认证信息(token、uid 等)
    -- 返回: true, player_data 或 false, error_reason

    local ok, player = verify_token(auth_data.token)
    if not ok then
        return false, "invalid_token"
    end

    -- 检查是否重复登录
    local existing = shield.player.query(player.uid)
    if existing then
        -- 踢掉旧连接
        existing:kick("duplicate_login")
    end

    return true, player
end

-- 玩家上线(认证成功后)
function M.on_login(player)
    -- player: PlayerSession 对象
    -- 加载玩家数据
    local data = load_player_data(player.uid)
    player:set_data(data)

    shield.log.info("player login: " .. player.uid)
end

-- 玩家消息处理
function M.on_client_message(player, payload)
    -- payload 为协议层解码后的应用消息;示例中用 table 表示
    if payload.kind == "move" then
        handle_move(player, payload)
    elseif payload.kind == "chat" then
        handle_chat(player, payload)
    end
end

-- 玩家断线
function M.on_disconnect(player, reason)
    -- reason 枚举见下方表格
    -- 进入重连窗口,不立即清理

    shield.log.info("player disconnect: " .. player.uid .. " reason: " .. reason)
end

-- 玩家重连
function M.on_reconnect(player)
    -- 恢复会话状态
    -- 推送离线期间的消息

    shield.log.info("player reconnect: " .. player.uid)
end

-- 玩家离线(重连窗口超时或主动登出)
function M.on_logout(player, reason)
    -- reason 枚举见下方表格
    -- 保存玩家数据
    save_player_data(player.uid, player:get_data())

    -- 清理资源
    -- 通知其他服务

    shield.log.info("player logout: " .. player.uid .. " reason: " .. reason)
end

-- 玩家数据保存(定时或手动触发)
function M.on_save(player)
    -- 增量保存或全量保存
    save_player_data(player.uid, player:get_data())
end

return M

钩子调用时机与默认实现

钩子setup 字段触发时机阻塞可选默认实现(业务未提供时由框架执行)
on_authauth连接建立后无;业务必须提供,否则 setup_invalid
on_loginlogin认证成功后PlayerManager.register(player);若配置 persistence 则触发首次 load
on_client_messageclient_message收到客户端业务 payload路由到业务方法 dispatch(与普通 service method 一致)
on_disconnectdisconnect连接断开进入重连窗口 + 启动离线消息缓存
on_reconnectreconnect重连成功推送离线期间缓存消息
on_logoutlogout玩家离线调用 persistence.save(若配置) + PlayerManager.unregister(uid)
on_savesave定时或手动触发若配置 persistence 则调用 persistence.save;否则 no-op

业务覆盖可选钩子时,默认实现不执行;如需保留默认行为,业务实现中应显式调用对应 helper(如 shield.player.defaults.reconnect(player))。

reason 枚举

玩家级钩子的 reason 与服务级 on_exit 的 reason 独立(服务级 reason 见 服务语义)。

on_disconnect reason:

reason说明
"client_close"客户端主动断开
"network_error"网络错误导致断开
"timeout"读写超时
"kicked"被服务端踢下线

on_logout reason:

reason说明
"normal"正常登出(客户端主动退出)
"timeout"重连窗口超时
"kicked"被管理员踢出
"replaced"被新设备登录替换

PlayerSession 对象

lua
-- PlayerSession 是玩家会话的运行时表示
local player = shield.player.get(uid)

-- 基础属性
player.uid              -- 玩家 UID
player.session_id       -- 会话 ID
player.state            -- 状态: online, disconnecting, offline
player.connect_time     -- 连接时间
player.last_active      -- 最后活跃时间
player.remote_addr      -- 客户端地址
player.device_id        -- 设备 ID(可选)

-- 数据管理
player:get_data()       -- 获取玩家数据
player:set_data(data)   -- 设置玩家数据
player:get(key)         -- 获取单个字段
player:set(key, value)  -- 设置单个字段

-- 网络发送(不暴露 SessionHandle)
player:send(payload)            -- 发送单条 payload
player:send_batch(payloads)     -- 批量发送

-- 会话控制
player:kick(reason)     -- 踢下线
player:logout()         -- 主动登出
player:save()           -- 触发保存

-- 重连相关
player:is_online()      -- 是否在线
player:is_reconnecting() -- 是否在重连窗口
player:reconnect_token() -- 获取重连 token

PlayerRef 跨 service 引用

PlayerRefPlayerSession 的轻量引用,用于跨 service 传递。

设计约束

  • PlayerRef 不是 ServiceHandle 的替代品,只是 player 模块内部引用。
  • 跨 service payload 只能PlayerRef不能SessionHandle,也不能传完整 PlayerSession
  • PlayerRef 可被 LuaPack 编码(独立 type tag),跨 service 通过 shield.send/call payload 传递。
  • 接收方通过 shield.player.resolve(ref) 解析为本地 PlayerSession

数据结构

lua
-- PlayerRef {
--   uid: uint64,
--   node_id: uint32,
--   service_id: uint64,
--   epoch: uint64,
-- }

epoch 来自 shield_cluster 的 node_epoch;单节点部署时为 0。

Lua API

lua
-- 从 PlayerSession 取轻量引用
local ref = player:ref()

-- 跨 service 传递
shield.send("combat.1", "kick", ref)

-- 接收方解析
function M.kick(ref)
    local player, err = shield.player.resolve(ref)
    if not player then
        shield.log.warn("resolve failed: " .. err.code)
        return
    end
    player:kick("combat_request")
end

resolve 行为

场景行为
本地且玩家在线返回 PlayerSession
本地但玩家已下线返回 nil, Error{code="player_not_found"}
ref 来自远端节点(P0 不冻结)返回 nil, Error{code="remote_resolve_unimplemented"},业务层走 cluster RPC 自行处理
ref 字段非法或 epoch 不匹配返回 nil, Error{code="invalid_player_ref"}

P0 仅冻结本地 resolve 与数据结构;远端 resolve 由 shield_cluster + shield_player 协作定义,进入 Phase 2+。

持久化 adapter

persistence adapter 是 shield_player 拥有的轻量持久化契约,不属于 shield_data,但底层必须通过 shield_data 调用。

设计约束

  • adapter 复用 shield.db.*shield.redis.*不重新定义 SQL/Redis 语义。
  • adapter 不拥有连接池;连接池归属 shield_data
  • adapter 不引入 ORM、mapper、schema 工具链;只接受可 LuaPack 编码的 table 白名单字段。
  • adapter 失败复用 shield_data 错误码;player 域只新增 persistence_save_failed 等明确属于本模块的错误。
  • 未配置 persistence 时,on_save 默认实现为 no-op。

配置形态

persistence 在 shield.player.setup 的 opts 中声明:

lua
shield.player.setup(M, {
    -- ... 其他钩子 ...

    persistence = {
        backend = "redis",                  -- "redis" | "database",必须已启用
        key = function(uid)                 -- 业务定义 key 模板
            return "player:" .. uid
        end,
        fields = { "profile", "inventory", "quests" },  -- 显式白名单
        auto_save_interval = 300000,        -- ms;0 = 关闭自动保存
        on_save_error = "log",              -- "log" | "panic",失败策略
    },
})

行为契约

时机行为
on_login 默认实现调用 persistence.load(uid),把字段填充到 player.data
on_save 默认实现(自动触发)auto_save_interval 调用 persistence.save(uid, fields)
on_save 默认实现(手动触发)业务调用 player:save() 立即触发
on_logout 默认实现调用 persistence.save(uid, fields) 后再 PlayerManager.unregister
persistence.save 失败on_save_error 配置:log 仅记录 + 计数;panic 触发 on_panic

字段白名单

只接受 LuaPack 支持的类型(见 消息语义)。下列类型禁止作为 persistence 字段:

  • functionthread/coroutine
  • 普通 userdata
  • 循环引用 table
  • SessionHandle 或完整 PlayerSession

业务需要持久化这些类型时,必须自行序列化为 string/binary 并放入白名单字段。

断线重连

重连窗口

玩家断线后进入重连窗口,期间保留会话状态:

yaml
player:
  reconnect:
    enabled: true
    window: 300000         # 重连窗口 5 分钟
    token_ttl: 60000       # 重连 token 有效期 1 分钟
    max_attempts: 3        # 最大重连尝试次数

重连流程

1. 玩家断线
   ├── 记录断线时间和原因
   ├── 保持 PlayerSession 存在
   ├── 进入重连窗口
   └── 开始缓存发往该玩家的消息

2. 玩家重连请求
   ├── 客户端携带: uid + reconnect_token
   ├── 验证 token 有效性
   ├── 检查是否在重连窗口内
   └── 成功: 恢复会话 | 失败: 要求重新登录

3. 重连成功
   ├── 恢复 PlayerSession 绑定到新 Connection
   ├── 推送离线期间缓存的消息
   ├── 调用 on_reconnect 钩子
   └── 恢复消息接收

4. 重连窗口超时
   ├── 标记玩家为 offline
   ├── 调用 on_logout 钩子
   ├── 保存玩家数据
   └── 清理会话

客户端重连实现

lua
-- 客户端伪代码
function client.reconnect()
    local ok, result = send_auth_request({
        uid = self.uid,
        reconnect_token = self.reconnect_token,
        is_reconnect = true,
    })

    if ok then
        -- 重连成功,处理离线消息
        for _, msg in ipairs(result.offline_messages) do
            self:handle_message(msg)
        end
    else
        -- 重连失败,重新登录
        self:login()
    end
end

重连 Token

lua
-- 服务端生成重连 token
function M.on_disconnect(player, reason)
    local token = player:reconnect_token()
    -- 发送给客户端(如果可能)
    -- 或客户端定期保存最新的 token
end

-- token 结构
{
    uid = 12345,
    token = "random_string",
    created_at = 1234567890,
    expires_at = 1234567950,
    session_id = 67890,
}

离线消息缓存

缓存策略

yaml
player:
  offline_messages:
    enabled: true
    max_messages: 100        # 每个玩家最大缓存消息数
    max_age: 3600000         # 消息最大存活时间 1 小时
    storage: memory          # memory | redis

消息缓存

lua
-- 发送消息给离线玩家
shield.player.send(uid, {
    kind = "mail",
    from = "system",
    title = "Welcome back!",
    content = "You have been away for 3 hours.",
})
-- 如果玩家离线,自动缓存

-- 消息结构
{
    id = "msg_12345",
    payload = {
        kind = "mail",
        content = "Welcome back!",
    },
    created_at = 1234567890,
    expires_at = 1234571490,
    delivered = false,
}

消息推送

lua
-- 玩家重连时推送离线消息
function M.on_reconnect(player)
    local messages = player:get_offline_messages()
    if #messages > 0 then
        player:send({
            kind = "offline_messages",
            count = #messages,
            messages = messages,
        })
        player:clear_offline_messages()
    end
end

消息优先级

lua
-- 高优先级消息(如系统通知、紧急邮件)
shield.player.send(uid, {
    kind = "system_alert",
    data = data,
}, {
    priority = "high",
    persist = true,  -- 即使超过最大数量也保留
})

-- 低优先级消息(如世界聊天)
shield.player.send(uid, {
    kind = "world_chat",
    data = data,
}, {
    priority = "low",
    max_age = 300000,  -- 5 分钟过期
})

配置示例

yaml
actors:
  - name: player_manager
    script: scripts/player_manager.lua
    player:
      # 认证配置
      auth:
        timeout: 10000               # 认证超时 10 秒
        max_attempts: 5              # 最大认证尝试次数
        ban_duration: 300000         # 认证失败封禁 5 分钟

      # 重连配置
      reconnect:
        enabled: true
        window: 300000               # 重连窗口 5 分钟
        token_ttl: 60000             # token 有效期 1 分钟
        max_attempts: 3              # 最大重连尝试

      # 离线消息配置
      offline_messages:
        enabled: true
        max_messages: 100            # 最大缓存消息数
        max_age: 3600000             # 消息存活 1 小时
        storage: memory              # memory | redis

      # 会话配置
      session:
        idle_timeout: 300000         # 空闲超时 5 分钟
        heartbeat_interval: 30000    # 心跳间隔 30 秒
        save_interval: 300000        # 定时保存间隔 5 分钟

      # 并发限制
      max_online: 10000              # 最大在线玩家数
      max_per_ip: 5                  # 单 IP 最大连接数

多设备登录策略

yaml
player:
  multi_device:
    policy: single           # single | multi | kick_old
    # single: 同一 UID 只允许一个设备在线
    # multi: 允许多设备同时在线
    # kick_old: 新设备登录时踢掉旧设备
lua
-- 单设备登录实现
function M.on_auth(session_id, auth_data)
    local uid = auth_data.uid
    local existing = shield.player.get(uid)

    if existing then
        if existing:is_reconnecting() then
            -- 在重连窗口内,允许重连
            return true, { reconnect = true }
        else
            -- 在线状态,根据策略处理
            if config.multi_device.policy == "kick_old" then
                existing:kick("replaced_by_new_device")
            else
                return false, "already_online"
            end
        end
    end

    return true, { uid = uid }
end

与 gateway 的关系

┌─────────────────────────────────────────────────────────┐
│                     gateway 服务                         │
│  ┌────────────────────────────────────────────────────┐ │
│  │  连接管理                                            │ │
│  │  on_connect → 认证 → 绑定 session_id                │ │
│  │  on_client_message → 路由到 PlayerSession           │ │
│  │  on_disconnect → 触发断线处理                        │ │
│  └────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│                   player_manager 服务                    │
│  ┌────────────────────────────────────────────────────┐ │
│  │  玩家生命周期                                        │ │
│  │  on_auth → on_login → on_client_message → on_logout│ │
│  │                   ↓                                 │ │
│  │              on_disconnect → on_reconnect           │ │
│  └────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│                   业务服务                               │
│  ┌────────────────────────────────────────────────────┐ │
│  │  player_service                                     │ │
│  │  处理具体业务逻辑                                    │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

约束:

  • SessionHandle 只保留在 gateway / 网络层内部映射中。
  • player_manager、业务 service 和离线消息队列都只通过 session_idPlayerSession 协作。
  • 跨 service payload 不传 SessionHandle

ops 暴露

json
GET /ops/players

{
  "online": 5000,
  "reconnecting": 10,
  "offline_with_messages": 500,
  "by_state": {
    "authenticating": 5,
    "online": 4985,
    "reconnecting": 10
  },
  "offline_message_stats": {
    "total_cached": 1234,
    "pending_delivery": 500,
    "expired_today": 50
  }
}

PlayerManager

PlayerManager 是全局单例 Service,负责管理所有在线玩家。

职责

职责说明
玩家注册/注销Player 上下线时向 PlayerManager 注册/注销
全局查询按 UID、状态、条件查询玩家
在线统计统计在线人数、状态分布
批量操作踢人、广播消息、批量通知
索引维护维护 UID → PlayerSession 的索引

架构

┌─────────────────────────────────────────────────────────┐
│                  PlayerManager (单例)                    │
│  ┌────────────────────────────────────────────────────┐ │
│  │  索引                                               │ │
│  │  - uid_index: Map<UID, PlayerSession>              │ │
│  │  - state_index: Map<State, Set<PlayerSession>>     │ │
│  └────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────┐ │
│  │  能力                                               │ │
│  │  - register(player) / unregister(uid)              │ │
│  │  - get(uid) / query(filter)                        │ │
│  │  - count() / online_list()                         │ │
│  │  - kick(uid, reason) / broadcast(payload)          │ │
│  └────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│  Player 1 (Service) │ Player 2 (Service) │ ...         │
└─────────────────────────────────────────────────────────┘

Lua API

lua
-- 获取 PlayerManager
local pm = shield.player.manager()

-- 查询玩家
local player = pm:get(uid)
local players = pm:query({ state = "online", level_min = 10 })

-- 统计
local count = pm:count()
local stats = pm:stats()
-- stats: { online = 5000, reconnecting = 10, total_today = 12345 }

-- 踢人
local ok, err = pm:kick(uid, "maintenance")

-- 广播
pm:broadcast({ kind = "system_notice", text = "Server maintenance in 5 minutes" })
pm:broadcast_to(filter, { kind = "system_notice", text = "VIP bonus!" })

-- 遍历
pm:for_each(function(player)
    if player.level >= 100 then
        player:send({ kind = "achievement", type = "level_100" })
    end
end)

PlayerSession 注册流程

lua
-- Player Service 的 on_login 中自动注册
function M.on_login(player)
    -- 框架自动调用 PlayerManager.register(player)
    -- 业务层无需手动注册

    -- 业务逻辑
    load_player_data(player.uid)
end

-- Player Service 的 on_logout 中自动注销
function M.on_logout(player, reason)
    -- 框架自动调用 PlayerManager.unregister(player.uid)
    -- 业务层无需手动注销

    -- 业务逻辑
    save_player_data(player.uid)
end

配置

yaml
# PlayerManager 配置
player_manager:
  enabled: true
  name: "player_manager"           # 服务名称
  max_players: 50000               # 最大在线玩家数
  index_cleanup_interval: 60000    # 索引清理间隔(ms)

# Player Service 配置(关联 PlayerManager)
actors:
  - name: player
    script: scripts/player.lua
    player:
      manager: "player_manager"    # 关联的 PlayerManager
      auth:
        timeout: 10000
      reconnect:
        enabled: true
        window: 300000

与其他 Service 的交互

lua
-- 其他 Service 查询玩家信息
local player = shield.call("player_manager", "get", uid)
if player then
    shield.log.info("player online: " .. player.uid)
end

-- 其他 Service 踢人
shield.call("player_manager", "kick", uid, "violation")

-- 其他 Service 广播
shield.send("player_manager", "broadcast", {
    type = "system_notice",
    text = "Welcome to the game!"
})

ops 暴露

json
GET /ops/players

{
  "manager": {
    "name": "player_manager",
    "status": "running"
  },
  "stats": {
    "online": 5000,
    "authenticating": 5,
    "reconnecting": 10,
    "total_today": 12345,
    "peak_today": 8000
  },
  "top_regions": [
    { "region": "us-east", "count": 2000 },
    { "region": "eu-west", "count": 1500 }
  ]
}

实现优先级

功能优先级说明
shield.player.setup 主 API + 默认实现表P0业务唯一推荐入口,默认行为必须明列
基础钩子(auth/login/logout/client_message/disconnect)P0setup 必填钩子,对应 runtime-service 的最小生命周期
PlayerRef + 本地 shield.player.resolveP0跨 service 轻量引用;远端 resolve 留 P2+
persistence adapter 契约P0shield_player 拥有的轻量 adapter,底层走 shield_data
断线重连P0游戏必备
PlayerManagerP0全局玩家管理
离线消息缓存P1提升体验
定时保存(on_save 默认实现触发)P1数据安全,依赖 persistence adapter
anonymous/spectator 状态(opt-in)P1默认状态机不变,配置显式开启
一玩家一 service 容量基准 + 可选 player_pool 模式P1大规模场景验证
多设备策略P2按需实现
shield.player.Base 高级风格P2setup 之上的 OOP 语法糖
远端 PlayerRef resolveP2shield_cluster + shield_player 协作

Apache License 2.0