Skip to content

API: wingman.team ​

Team 模块提供多 Runtime 协同组队、投票协调和群体通信功能。

模块概述 ​

team 模块基于 Go server 的 TeamManager(内存态)实现分布式协同功能:

  • 组队管理:加入/离开队伍,获取队友信息
  • 投票协调:发起投票、参与投票、获取投票结果
  • 状态汇报:上报自身状态,获取队友状态
  • 群体通信:向所有队友广播消息
  • 事件订阅:监听队伍相关事件

架构设计 ​

┌─────────────────────────────────────────────────────────────────────────┐
│                           Runtime A                                       │
│                                                                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │                      Team Module                                 │  │
│  │                                                                   │  │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────────┐  │  │
│  │  │joinTeam  │  │leaveTeam │  │broadcast │  │ on(event, cb)    │  │  │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────────────┘  │  │
│  │                                                                   │  │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────────────────────┐   │  │
│  │  │createVote│  │castVote  │      │ Local State (members, votes)│   │  │
│  │  └──────────┘  └──────────┘      └──────────────────────────────┘   │  │
│  │                                                                   │  │
│  │  脚本调用 → 发 team.outbound 事件(本地乐观更新)                   │  │
│  │  team.inbound 事件 → handleServerMessage 状态机 → on() 事件        │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│         │ team.outbound                        ↑ team.inbound           │
│         ↓                                      │                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │            apps/agent 装配桥(EventHub ↔ RemoteClient)           │  │
│  │   出站:team.outbound → 同名 Notify 帧(team.join 等)             │  │
│  │   下行:inbox.message → team.inbound(处理即自动 inbox.ack)       │  │
│  └──────────────────────────────────────────────────────────────────┘  │
│                                  ↑                                       │
│                          agent 通道(TCP,                        │
│                          runtime 主动 outbound 连接)                    │
│                                  ↓                                       │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                              Go Server                                   │
│                                                                        │
│  ┌──────────────────────────────────────────────────────────────────┐  │
│  │                         TeamManager                                │  │
│  │                                                                   │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │  Teams: teamId → {leaderId, members[], state}             │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  │                                                                   │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │  Votes: voteId → {teamId, subject, responses{}, deadline}  │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  │                                                                   │  │
│  │  ┌────────────────────────────────────────────────────────────┐  │  │
│  │  │  Inboxes: agentId → {msgId → {type, payload, acked}}       │  │  │
│  │  └────────────────────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘
                                   ▲
                                   │ agent 通道
                                   │
┌─────────────────────────────────────────────────────────────────────────┐
│                           Runtime B                                       │
│                           (相同架构)                                       │
└─────────────────────────────────────────────────────────────────────────┘

桥接细节见 docs/architecture-decisions.md「Team inbox push」与 docs/protocols.md inbox.message 行。团队与收件箱均为服务端内存态,服务重启丢失。

加入队伍 ​

joinTeam(teamId, memberId?) ​

说明:加入指定队伍。

函数签名:

python
join_team(team_id: str, member_id: str = None) -> bool
lua
joinTeam(teamId: str, memberId: str = nil) -> boolean

参数:

  • team_id / teamId - 队伍 ID
  • member_id / memberId - 可选,成员 ID(默认自动生成)

返回:是否加入成功

python
from wingman import team

# 加入队伍
if team.join_team("team_001", "player_alice"):
    print("已加入队伍")
    
    # 获取队伍信息
    status = team.get_team_status()
    print(f"队长: {status['leaderId']}")
    print(f"队友: {status['members']}")

离开队伍 ​

leaveTeam() ​

说明:离开当前队伍。

函数签名:

python
leave_team() -> bool
lua
leaveTeam() -> boolean

返回:是否离开成功

python
from wingman import team

# 离开队伍
if team.leave_team():
    print("已离开队伍")

投票功能 ​

createVote(subject, timeout?) ​

说明:发起投票。

函数签名:

python
create_vote(subject: str, timeout: int = 30000) -> bool
lua
createVote(subject: str, timeout: number = 30000) -> boolean

参数:

  • subject - 投票主题
  • timeout - 超时时间(毫秒,默认 30000)

返回:是否发起成功


castVote(voteId, response) ​

说明:对指定投票进行投票。

函数签名:

python
cast_vote(vote_id: str, response: str) -> bool
lua
castVote(voteId: str, response: str) -> boolean

参数:

  • vote_id / voteId - 投票 ID
  • response - 投票响应内容

返回:是否投票成功


getVoteResult(voteId) ​

说明:获取投票结果。

函数签名:

python
get_vote_result(vote_id: str) -> dict
lua
getVoteResult(voteId: str) -> string

参数:

  • vote_id / voteId - 投票 ID

返回:投票结果对象(JSON 字符串)

python
from wingman import team, json

# 发起投票
if team.create_vote("是否开始副本", 30000):
    print("投票已发起")

# 获取投票结果
result_str = team.get_vote_result("vote_123")
result = json.decode(result_str)
print(f"投票主题: {result['subject']}")
print(f"是否活跃: {result['active']}")
print(f"投票响应: {result['responses']}")

状态管理 ​

reportStatus(status) ​

说明:上报自身状态到队伍。

函数签名:

python
report_status(status: dict) -> bool
lua
reportStatus(status: table) -> boolean

参数:

  • status - 状态对象

返回:是否上报成功

python
from wingman import team

# 汇报状态
team.report_status({
    "hp": 100,
    "mp": 50,
    "location": "town",
    "ready": True
})

getTeamStatus() ​

说明:获取当前队伍状态。

函数签名:

python
get_team_status() -> dict
lua
getTeamStatus() -> string

返回:队伍状态对象(JSON 字符串)

  • teamId - 队伍 ID
  • leaderId - 队长 ID
  • members - 成员列表
  • state - 队伍状态(idle/voting/working)
  • lastUpdate - 最后更新时间
python
from wingman import team, json

status_str = team.get_team_status()
status = json.decode(status_str)

print(f"队伍 ID: {status['teamId']}")
print(f"队长: {status['leaderId']}")
print(f"成员: {status['members']}")
print(f"状态: {status['state']}")

群体通信 ​

broadcast(message) ​

说明:向所有队友广播消息。

函数签名:

python
broadcast(message: dict | str) -> bool
lua
broadcast(message: table | string) -> boolean

参数:

  • message - 消息内容(对象或字符串)

返回:是否发送成功

python
from wingman import team

# 广播消息
team.broadcast({
    "type": "coordination",
    "action": "move_to",
    "target": "boss_location",
    "timestamp": 1234567890
})

事件订阅 ​

on(event, callback) ​

说明:订阅队伍事件。

函数签名:

python
on(event: str, callback: Callable) -> bool
lua
on(event: str, callback: function) -> boolean

参数:

  • event - 事件类型(无需 "team." 前缀)
  • callback - 回调函数

可用事件:

  • joined - 已加入队伍(服务器确认后触发)
  • left - 已离开队伍
  • vote_started - 投票开始
  • vote_ended - 投票结束
  • broadcast_received - 收到广播消息
  • member_joined - 队友加入
  • member_left - 队友离开

此外,团队范围推送的自定义类型(如 system.notice)以原类型名触发同名事件: on("system.notice", cb) 即可订阅,回调收到该帧完整载荷(未识别的 messageType 不再被静默丢弃)。

python
from wingman import team, json

# 监听投票开始
team.on("vote_started", lambda data: print(f"新投票: {json.decode(data)['subject']}"))

# 监听投票结束
team.on("vote_ended", lambda data: print(f"投票结束: {json.decode(data)['result']}"))

# 监听广播消息
team.on("broadcast_received", lambda data: print(f"收到广播: {json.decode(data)['message']}"))

查询接口 ​

isJoined() ​

说明:检查是否已加入队伍。

函数签名:

python
is_joined() -> bool
lua
isJoined() -> boolean

返回:是否已加入队伍


getMemberId() ​

说明:获取当前成员 ID。

函数签名:

python
get_member_id() -> str
lua
getMemberId() -> string

返回:成员 ID


完整示例 ​

队伍协同 ​

python
from wingman import team, json, util

# 1. 加入队伍
if not team.join_team("dungeon_party", "warrior_01"):
    print("加入队伍失败")
    exit()

print("已加入队伍")

# 2. 监听队伍事件
team.on("vote_started", lambda data: print(f"投票开始: {json.decode(data)['subject']}"))
team.on("broadcast_received", lambda data: handle_broadcast(json.decode(data)))

def handle_broadcast(msg):
    print(f"收到 {msg['senderId']} 的广播")
    if msg['message']['type'] == 'attack':
        coordinate_attack(msg['message'])

# 3. 汇报状态
team.report_status({
    "role": "warrior",
    "level": 50,
    "ready": True
})

# 4. 等待并处理消息
while team.is_joined():
    util.sleep(1000)
    
    # 检查投票
    status = json.decode(team.get_team_status())
    if status['state'] == 'voting':
        print("队伍正在投票...")
    
    # 定期汇报状态
    team.report_status({"hp": get_current_hp()})

投票流程 ​

python
from wingman import team, json, util

# 监听投票事件
team.on("vote_started", lambda data: handle_vote_start(json.decode(data)))
team.on("vote_ended", lambda data: print(f"投票结果: {json.decode(data)['result']}"))

def handle_vote_start(vote):
    print(f"投票: {vote['subject']}")
    print(f"截止时间: {vote['deadline']}")
    
    # 自动决策(示例)
    if vote['subject'] == "是否休息":
        team.cast_vote(vote['voteId'], "agree")
    else:
        team.cast_vote(vote['voteId'], "disagree")

# 发起投票
team.create_vote("是否前往BOSS区域", 30000)

# 等待投票结果
util.sleep(35000)

result = json.decode(team.get_vote_result("vote_xxx"))
print(f"最终结果: {result['responses']}")

可用接口 ​

Python 函数Lua 函数说明参数
join_team(teamId, memberId?)joinTeam(teamId, memberId?)加入队伍teamId: 队伍ID
memberId: 成员ID
返回: bool
leave_team()leaveTeam()离开队伍返回: bool
create_vote(subject, timeout?)createVote(subject, timeout?)发起投票subject: 主题
timeout: 超时(ms)
返回: bool
cast_vote(voteId, response)castVote(voteId, response)投票voteId: 投票ID
response: 响应
返回: bool
get_vote_result(voteId)getVoteResult(voteId)获取投票结果voteId: 投票ID
返回: JSON字符串
report_status(status)reportStatus(status)汇报状态status: 状态对象
返回: bool
broadcast(message)broadcast(message)广播消息message: 消息对象
返回: bool
get_team_status()getTeamStatus()获取队伍状态返回: JSON字符串
is_joined()isJoined()是否已加入返回: bool
get_member_id()getMemberId()获取成员ID返回: string
on(event, callback)on(event, callback)订阅事件event: 事件名
callback: 回调
返回: bool

协议消息格式 ​

出站帧经 agent 通道 Notify 直发(类型如下表);下行帧由 server 封装在 inbox.message (messageType 字段携带下列类型,载荷即 payload)推送,桌面 runtime 处理后自动回 inbox.ack。完整帧格式见 docs/protocols.md。

Runtime [Server] ​

加入队伍:

json
{
  "type": "team.join",
  "teamId": "team_001",
  "memberId": "player_01",
  "timestamp": 1234567890
}

离开队伍:

json
{
  "type": "team.leave",
  "teamId": "team_001",
  "memberId": "player_01",
  "timestamp": 1234567890
}

发起投票:

json
{
  "type": "team.vote_create",
  "teamId": "team_001",
  "proposerId": "player_01",
  "subject": "是否开始副本",
  "timeout": 30000,
  "timestamp": 1234567890
}

投票:

json
{
  "type": "team.vote_cast",
  "teamId": "team_001",
  "voteId": "vote_123",
  "memberId": "player_01",
  "response": "agree",
  "timestamp": 1234567890
}

广播:

json
{
  "type": "team.broadcast",
  "teamId": "team_001",
  "memberId": "player_01",
  "message": {"type": "coordination", "action": "move"},
  "timestamp": 1234567890
}

状态汇报:

json
{
  "type": "team.status_report",
  "teamId": "team_001",
  "memberId": "player_01",
  "status": {"hp": 100, "ready": true},
  "timestamp": 1234567890
}

Server [Runtime] ​

加入确认:

json
{
  "type": "team.joined",
  "teamId": "team_001",
  "leaderId": "player_leader",
  "memberId": "player_01",
  "members": ["player_leader", "player_01"],
  "timestamp": 1234567890
}

队友加入:

json
{
  "type": "team.member_joined",
  "teamId": "team_001",
  "memberId": "player_02",
  "timestamp": 1234567890
}

队友离开:

json
{
  "type": "team.member_left",
  "teamId": "team_001",
  "memberId": "player_02",
  "timestamp": 1234567890
}

投票开始:

json
{
  "type": "team.vote_started",
  "voteId": "vote_123",
  "teamId": "team_001",
  "proposerId": "player_leader",
  "subject": "是否开始副本",
  "deadline": 1234599999,
  "timestamp": 1234567890
}

投票结束:

json
{
  "type": "team.vote_ended",
  "voteId": "vote_123",
  "result": {
    "voteId": "vote_123",
    "subject": "是否开始副本",
    "responses": {"player_01": "agree", "player_02": "agree"},
    "active": false
  },
  "timestamp": 1234567890
}

收到广播:

json
{
  "type": "team.broadcast_received",
  "senderId": "player_leader",
  "teamId": "team_001",
  "message": {"type": "coordination", "action": "attack"},
  "timestamp": 1234567890
}

注意事项 ​

  1. 依赖 agent 通道连接:

    • team 出站/下行都经 apps/agent 装配桥走 agent 通道 Notify(见 docs/protocols.md)
    • runtime 未连接 Go server 时,team.* 只做本地乐观更新,收不到服务器事件
  2. 本地状态同步:

    • getTeamStatus() 返回本地缓存的状态
    • 实际状态以服务器为准,通过事件更新
  3. 事件订阅时机:

    • 建议在 joinTeam() 之前订阅事件
    • 确保不会错过任何事件通知
  4. 投票超时:

    • 投票有超时时间,超时后自动结束
    • 超时后的投票结果仍可通过 getVoteResult() 获取
  5. 广播消息:

    • 广播消息由服务器转发给所有队友
    • 发送者不会收到自己的广播(避免重复)
  6. 线程安全:

    • 所有函数都是线程安全的
    • 事件回调可能在独立线程中执行