Skip to main content
Glama
53721222-gif

Backpack Battles MCP

by 53721222-gif

Backpack Battles MCP

类似 MCPTheSpire 的《背包乱斗 / Backpack Battles》本地 MCP 服务: 在游戏内挂一个轻量 bridge,把职业、回合、重试次数、金币、商店物品与价格、刷新费、背包摆盘、储物箱暴露给大模型,并开放购买、刷新、编辑背包/物品、摆放、旋转、开战等操作。


1. 架构

┌──────────────────────────────┐        TCP 127.0.0.1:8765        ┌──────────────────┐
│  Backpack Battles (Godot 3.6)│  <───────────────────────────>  │  bpb_mcp.server  │
│  McpBridge.gd  (autoload)    │      newline-delimited JSON      │  (Python, stdio) │
│  bpb_api.gd    (game logic)  │                                  └────────┬─────────┘
└──────────────────────────────┘                                           │ MCP JSON-RPC
                                                                           ▼
                                                                    ┌──────────────┐
                                                                    │ LLM / MCP    │
                                                                    │ 客户端        │
                                                                    └──────────────┘
  • 不修改 BackpackBattles.pck,不注入 DLL,不使用 BepInEx。

  • 利用 Godot 3.6 的 override.cfg 在 exe 同目录覆盖 autoload,脚本放在 user://mcp/。

  • 游戏内 bridge 只监听 127.0.0.1:8765;Python 侧提供 MCP stdio 服务(可选 HTTP API)。

  • 已在本机 Backpack Battles 1.1.9b / Godot 3.6 上验证;pck 未被改动,游戏日志仍显示 [integrity_check:ok]。


Related MCP server: game-mcp

2. 功能

分类

工具名

说明

状态

bpb_get_state

完整状态(shop/title);战斗期摘要;含 wins / total_wins / survival

状态

bpb_get_phase

轻量状态:state/round/tries/gold/wins/total_wins/survival,战斗期安全

状态

bpb_get_backpack

完整背包布局:bags/items 位置、cells、shape、rotation、grid、storage,只读

信息

bpb_get_star_slots

星位/受影响格(方向 0/2/4/7)、当前命中物品、extension_cells、宝石 sockets

信息

bpb_get_stamina

玩家 stamina/max/regen、每件物品 stamina_cost/types/伤害,已部署总耗

信息

bpb_get_recipes

base/result/ingredients/催化剂、作为材料配方、牌堆 gated_items

购买

bpb_buy_item

买商店物品并放入储物箱(slot 1-5 或 item_id);技能商店时选择技能并放入储物箱(返回 skill_id)

商店

bpb_reserve_item

保留/取消保留商店物品(免费,跨刷新保留)

商店

bpb_refresh_shop

刷新商店(扣金币)

背包

bpb_store_item

把背包物品放回储物箱,腾出格子

背包

bpb_sell_item

出售背包物品,立即获得金币(储物箱物品需先放回背包)

编辑

bpb_open_backpack_edit

进入背包层编辑模式(等价 bags)

编辑

bpb_open_item_edit

进入物品层编辑模式(等价 items)

编辑

bpb_edit_items

只编辑物品:背包固定,物品可放到任意格子(包括包外)

编辑

bpb_edit_bags

只编辑背包:物品固定,背包可移动/旋转,不牵连物品

编辑

bpb_edit_all

编辑全部:只规范化落格,不再把跨包/包外物品移走

编辑

bpb_set_edit_mode

兼容别名:all/bags/items/default/bag/item

摆放

bpb_place_item

原生 drop 落格(pickup 当前位置 → drop);物品/全部模式允许包外/跨包

摆放

bpb_place_bag

移动空背包;非空返回 bag_not_empty。背包模式物品绝对位置不变

旋转

bpb_rotate_item

当前返回 rotate_unsupported:face 只改视觉,drop 用基础碰撞;放置会自动 face=0

旋转

bpb_rotate_bag

当前返回 rotate_unsupported(face 只改视觉,放置强制 face=0/rotation=0)

战斗

bpb_start_battle

开始战斗;开战前只规范化落格,不移走跨包物品

战斗

bpb_wait_for_result

等待结算;自动跳过并回商店;10 胜提示会返回 survival_choice_required

战斗

bpb_skip_result

手动跳过结算;10 胜提示下被拦截,需先选择

10胜

bpb_enter_survival

选择“进入生存模式”

10胜

bpb_stop_here

选择“到此为止”(结束本轮,保留胜场)

10胜

bpb_choose_survival

统一选择:choice=survival/stop

开局

bpb_set_class / bpb_set_loadout

标题界面选择职业 / 初始 loadout

开局

bpb_start_run / bpb_continue

新开一局(可带 class_id/loadout)/ 继续挂起的 run

诊断

bpb_health

检查网桥与当前状态

购买后物品/技能进储物箱,不会自动进背包;需要再调用 bpb_place_item。技能也是 1×1 可摆放 Item。

购买后编辑流程:bpb_edit_items(物品可放包外)→ bpb_edit_bags(移动背包覆盖物品,物品固定)→ bpb_edit_all(规范化落格;跨包物品保留)。

标题界面先看 state.title.classes[].loadouts,用 bpb_set_class / bpb_set_loadout 选择,再 bpb_start_run。

10 胜提示:state.survival.prompt_visible=true 时,用 bpb_enter_survival 或 bpb_stop_here 明确选择;bpb_skip_result / bpb_wait_for_result 会被拦截,不会替你选。


3. 环境要求

  • Windows 10/11

  • 已安装 Backpack Battles(Steam AppID 2427700)

  • Python 3.8+(本项目无第三方依赖)

  • 游戏必须正在运行(bridge 在游戏进程内)

本项目在以下环境验证:

  • 游戏版本 1.1.9b

  • Godot 3.6.stable.custom_build

  • Python 3.9.1(64 位)

  • 游戏目录:D:\Steam\steamapps\common\Backpack Battles


4. 安装

在项目目录(例如 D:\mcpbpb)执行:

cd /d D:\mcpbpb
python -m bpb_mcp.cli install
python -m bpb_mcp.cli doctor

安装器会:

  1. 自动定位 Steam 游戏目录(注册表 / libraryfolders.vdf,也可 --game-dir 指定);

  2. 复制 mod/McpBridge.gd、mod/bpb_api.gd 到:

    %APPDATA%\Godot\app_userdata\Backpack Battles\mcp\
  3. 在 BackpackBattles.exe 同目录创建 / 合并 override.cfg:

    [autoload]
    McpBridge="*user://mcp/McpBridge.gd"

    已有 override.cfg 会先备份为 override.cfg.bak.<时间戳>,并保留其他 section / autoload。

为什么不用改 pck?

Godot 3.6 在启动时会读取 exe 同目录的 override.cfg,并据此注册 autoload;脚本从 user:// 加载。因此:

  • 不触碰 314MB 的 BackpackBattles.pck;

  • 不需要 BepInEx / DLL 注入;

  • 游戏更新后通常只需重新 install;

  • 不会影响 pck 的完整性校验。

卸载

python -m bpb_mcp.cli uninstall

移除 override.cfg 中的 McpBridge autoload 和 user://mcp/ 下的两个脚本;备份文件保留。


5. 启动与验证

  1. 启动游戏(Steam 或 python -m bpb_mcp.cli launch)。

  2. 进入标题界面,点击「继续 / 新游戏」,进入商店。

  3. 验证:

python -m bpb_mcp.cli health
python -m bpb_mcp.cli state

health 正常输出示例:

{
  "ok": true,
  "bridge": {"pong": true, "uptime_ms": 12345, "version": "0.1.0"},
  "game_state": "shop",
  "round": 2,
  "gold": 18,
  "version": "1.1.9"
}

6. 接入 MCP 客户端

Claude Desktop

编辑 claude_desktop_config.json:

{
  "mcpServers": {
    "backpack-battles": {
      "command": "python",
      "args": ["-m", "bpb_mcp.server"],
      "cwd": "D:\\mcpbpb"
    }
  }
}

如果客户端找不到 python,把 command 换成 Python 绝对路径,例如:

"command": "C:\\Users\\<你>\\AppData\\Local\\Programs\\Python\\Python39\\python.exe"

Cursor / Cline / 其他 MCP 客户端

同样是 stdio 方式:

  • command: python

  • args: ["-m", "bpb_mcp.server"]

  • cwd: D:\mcpbpb

仓库里也提供了 start_mcp.bat,可直接作为 command 使用。


7. 典型工作流

0. 标题界面(可选)
   bpb_get_state → state.title.classes[].loadouts
   bpb_set_class {"class_id":2,"loadout":0}
   bpb_start_run {"force_new":true}

1. bpb_get_state
   → 看 state=shop、shop.items(slot/price/size/id/kind)、shop.choose_one、
     shop.reserved、backpack.grid、storage

2. bpb_buy_item {"slot": 1}
   → 普通商店:金币扣减,物品进 storage
   → 技能商店(choose_one=true):选择技能,金币扣减,技能进 storage,返回 skill_id

3. bpb_get_state
   → 从 storage[].id 或 buy_item 返回的 skill_id 拿到 id

4. bpb_place_item {"item_id": 123456, "x": 3, "y": 3}
   → 放进背包某个包覆盖的格子;返回 cells/top_left
   → 背包满了先 bpb_store_item {"item_id": 旧物品id} 腾格
   → 想换金币用 bpb_sell_item {"item_id": 旧物品id}

5. bpb_reserve_item {"slot": 1, "reserved": true}
   → 保留商品(免费);reserved:false 取消保留

6. bpb_refresh_shop
   → 刷新(看 shop.reroll_cost);保留项仍会保留

7. bpb_start_battle
   → 等待真正进入 combat 后返回

8. bpb_wait_for_result {"timeout_seconds": 90}
   → 等待结算界面,自动跳过,返回下一回合 shop 状态
   → 战斗期间 bpb_get_state 只返回摘要,不读背包;用 bpb_get_phase 轮询

模型不需要关心鼠标、拖拽、动画;所有操作都是确定性的格子/ID 操作。

购买后编辑顺序

1. bpb_edit_items
   → 背包固定;bpb_place_item 可把物品放到任意格子,包括包外(outside_bag=true)

2. bpb_edit_bags
   → 物品固定;bpb_place_bag 移动**空背包**去覆盖物品(非空包先移出内容);背包放置强制 face=0/rotation=0,避免显示与 cells 差 90°
   → 移动背包不会牵连物品的绝对格子

3. bpb_edit_all
   → 物品/背包都可编辑;物品可以同时覆盖多个背包,不会被自动移动
   → 返回 moved_to_storage(物品 id/name 列表)
  • bpb_edit_bags 下调 bpb_place_item 返回 items_fixed_in_bag_mode。

  • bpb_edit_items 下调 bpb_place_bag 返回 bags_fixed_in_item_mode;bpb_rotate_bag 返回 rotate_unsupported。

  • bpb_edit_all 是最终化步骤;之后检查 backpack.floating_count == 0,并确认关键物品仍在背包中。跨包允许。

10 胜提示

bpb_get_state
  → state.survival.prompt_visible=true
  → state.wins=10, state.total_wins=累计胜场

选择一:
bpb_enter_survival          # 进入生存模式
或
bpb_stop_here               # 到此为止
或
bpb_choose_survival {"choice":"survival"|"stop"}

注意:
bpb_skip_result / bpb_wait_for_result 在提示出现时返回 survival_choice_required,
不会自动替你选择。

8. 状态结构

bpb_get_state 返回的核心字段:

{
  "state": "shop",
  "state_id": 1,
  "round": 2,
  "max_rounds": 18,
  "tries": 4,
  "gold": 18,
  "wins": 10,
  "current_run_wins": 10,
  "total_wins": 112,
  "survival": {
    "prompt_visible": false,
    "can_start": false,
    "can_stop_here": false,
    "was_max_wins": false,
    "survival_mode": false
  },
  "class": {"id": 0, "name": "Ranger", "name_localized": "游侠"},
  "mode": {"id": 1, "name": "Unranked"},
  "hp": {"current": 35, "max": 35},
  "edit_mode": {"id": 0, "name": "all"},
  "can_start_battle": true,
  "run_started": true,
  "last_result": "loss",
  "combat": {"visible": false, "post_combat_visible": false},
  "can_skip_result": false,
  "version": "1.1.9",

  "shop": {
    "reroll_cost": 1,
    "rerolls": 0,
    "items_bought": 1,
    "choose_one": false,
    "reserved_count": 1,
    "reserved_items": [{"id": 2483562, "name": "Customer Card", "name_localized": "会员卡"}],
    "items": [
      {
        "slot": 1,
        "kind": "item",
        "reserved": false,
        "can_reserve": true,
        "id": 124371,
        "name": "Lump of Coal",
        "name_localized": "一坨煤炭",
        "price": 2,
        "sell_price": 1,
        "rarity": "普通",
        "size": [1, 1],
        "cells_count": 1,
        "rotation": 0,
        "on_sale": false,
        "affordable": true,
        "description": "……"
      }
    ]
  },

  "backpack": {
    "width": 9,
    "height": 7,
    "bags": [
      {
        "id": 81202,
        "name": "Vineweave Basket",
        "name_localized": "藤条编织篮",
        "top_left": [2, 2],
        "cells": [[2,2],[3,2],[4,2],[2,3],[3,3],[4,3],[2,4],[3,4],[4,4]],
        "size": [3, 3],
        "rotation": 0
      }
    ],
    "items": [
      {
        "id": 81297,
        "name": "Healing Herbs",
        "name_localized": "治疗草药",
        "top_left": [2, 2],
        "cells": [[2, 2]],
        "rotation": 0,
        "price": 4,
        "sell_price": 2,
        "rarity": "普通",
        "is_bag": false,
        "bag_id": 81202,
        "bag_name": "藤条编织篮",
        "size": [1, 1]
      }
    ],
    "grid": [
      [null, null, null, ...],
      [null, null, {"bag_id":81202,"bag":"藤条编织篮"}, ...]
    ]
  },

  "storage": [
    {"id": 124371, "name": "Lump of Coal", "name_localized": "一坨煤炭", "price": 2, "sell_price": 1, "size": [1,1]}
  ]
}

标题界面

{
  "state": "title",
  "title": {
    "selected_class": 0,
    "configured_loadout": "0",
    "classes": [
      {
        "id": 0,
        "name": "Ranger",
        "name_localized": "游侠",
        "loadouts": [
          {"index": 0, "items": ["Ranger Bag", "Leather Bag", "Leather Bag", "Lucky Clover", "Wooden Sword"]},
          {"index": 1, "items": ["Vineweave Basket", "Leather Bag", "Healing Herbs", "Stone"]},
          {"index": 2, "items": ["Random Loadout Bag"]}
        ]
      }
    ]
  }
}

战斗期

{
  "state": "combat",
  "combat_only": true,
  "inventory_available": false,
  "round": 3,
  "tries": 4,
  "gold": 21,
  "wins": 10,
  "current_run_wins": 10,
  "total_wins": 112,
  "survival": {
    "prompt_visible": true,
    "can_start": true,
    "can_stop_here": true,
    "was_max_wins": true,
    "survival_mode": false
  },
  "can_skip_result": true,
  "last_result": "loss",
  "shop": {"items": [], "reroll_cost": 0, "rerolls": 0, "items_bought": 0,
           "choose_one": false, "reserved_count": 0, "reserved_items": []},
  "backpack": null,
  "storage": []
}

战斗期间 bpb_get_state 不访问背包/商店,避免游戏在战斗过渡时崩溃。需要完整背包请在 state=shop 时读取;等待结算用 bpb_get_phase / bpb_wait_for_result。

字段说明

  • state:title / shop / combat。

  • inventory_available:false 表示当前是战斗摘要,backpack 为 null、shop.items 为空。

  • combat_only:战斗摘要标记。

  • title:仅标题界面出现;含职业、当前选择、loadout 及其初始物品。

  • tries:剩余重试次数(失败会减少)。

  • round:当前回合(1-18)。

  • wins / current_run_wins:当前 run 胜场(10 胜触发提示)。

  • total_wins:累计胜场(来自游戏 getRunWins())。

  • survival:

    • prompt_visible:10 胜“到此为止 / 进入生存模式”提示是否出现;

    • can_start:是否可选择进入生存;

    • can_stop_here:是否可选择到此为止;

    • was_max_wins:是否达到 10 胜上限;

    • survival_mode:当前是否已在生存模式。

  • shop.items[].price:当前价格(含折扣);on_sale 表示是否打折。

  • shop.choose_one:当前是否为五选一技能商店(第 3/10 回合)。

  • shop.items[].kind:item 或 skill(技能商店时)。

  • shop.items[].reserved / can_reserve:是否已保留 / 是否可保留。

  • shop.reserved_count / reserved_items:当前保留的商品数量与列表。

  • script:物品脚本路径(可用于区分技能/普通 Exclusive 物品)。

  • shape:物品当前朝向的归一化占用格(相对左上角)。

  • sell_price:出售价格;用 bpb_sell_item 卖出。

  • edit_mode:all / bags / items。

  • backpack.outside_count:当前不在任何背包内的物品数量。

  • backpack.invalid_count:兼容字段,当前不把跨包/包外算非法;正常为 0。

  • backpack.items[].outside_bag:该物品是否在包外(物品模式可临时这样放)。

  • backpack.items[].in_one_bag:该物品所有格是否属于同一个背包;false 不代表非法,游戏允许跨包。

  • backpack.items[].placed_by_player:是否走玩家落格流程(影响星位/触发)。

  • backpack.items[].position / bbox_center / float_offset:视觉位置、目标包围盒中心、偏移。

  • backpack.items[].floating:该物品是否浮空(偏移 > 0.75px)。

  • backpack.floating_count:浮空物品数量;正常应为 0。

  • backpack.grid[y][x]:

    • null:空格(不在任何包内);

    • {"bag_id","bag"}:只被背包覆盖,可放物品;

    • {"bag_id","bag","item_id","item"}:已被物品占用;

    • {"item_id","item"}:物品在包外(仅物品模式会出现)。

  • top_left:该物品/背包占据格子的最小 (x, y)。

  • cells:精确占用格列表。

  • storage:储物箱物品(购买后先到这里;全部模式放不下的物品也会自动到这里)。

  • can_skip_result:结算界面是否已出现(只有此时才能 bpb_skip_result / bpb_wait_for_result 跳过)。

  • last_result:上一回合结果(win / loss / draw);不是当前战斗的实时结果。

坐标:x 为列 0-8,y 为行 0-6。正常摆放要求物品在背包覆盖的格子里;items 模式允许临时放在包外,all 模式会把不落在同一背包内的物品自动移入储物箱。

技能商店:第 3/10 回合 shop.choose_one=true,shop.items[].kind="skill"。技能在内部也是 RigidBody2D 的 Item(1×1、有价格/描述),可以摆放:bpb_buy_item 会选择技能并放入储物箱(返回 skill_id),再调用 bpb_place_item 摆进背包;技能会随背包一起持久化,重启后仍在。

保留商店物品:bpb_reserve_item 可保留/取消保留商品(免费)。shop.items[].reserved、shop.reserved_count、shop.reserved_items 会反映保留状态;保留项在刷新商店后仍保留。


9. 工具参数速查

工具

参数

bpb_get_state

无(战斗期返回摘要;含 wins/total_wins/survival)

bpb_get_phase

无(含 wins/total_wins/survival)

bpb_get_backpack

无(只读,不 normalize)

bpb_get_star_slots

item_id(可选;只查一件)

bpb_get_stamina

无

bpb_get_recipes

item_id(可选;可传商店物品 id;全量默认只查背包/储物箱)

bpb_buy_item

slot(1-5)或 item_id;技能商店时选择技能并放入储物箱,返回 skill_id

bpb_reserve_item

slot 或 item_id;reserved(默认 true,false=取消保留)

bpb_store_item

item_id(把背包物品放回储物箱)

bpb_sell_item

item_id(出售背包物品)

bpb_refresh_shop

无

bpb_open_backpack_edit

无(等价 bpb_edit_bags)

bpb_open_item_edit

item_id(可选;等价 bpb_edit_items)

bpb_edit_items

无(只编辑物品,背包固定)

bpb_edit_bags

无(只编辑背包,物品固定)

bpb_edit_all

无(全部;只规范化落格,不移走跨包物品)

bpb_set_edit_mode

mode: all/bags/items(兼容 default/bag/item)

bpb_set_class

class_id(0-6), loadout(0-2,可选)

bpb_set_loadout

loadout(0-2)

bpb_place_item

item_id, x, y(原生 drop;允许跨包/包外)

bpb_place_bag

item_id, x, y(仅空包;非空返回 bag_not_empty)

bpb_rotate_item

item_id, direction(当前返回 rotate_unsupported)

bpb_rotate_bag

item_id, direction(当前返回 rotate_unsupported)

bpb_start_battle

无(开战前只规范化落格,不移走跨包物品)

bpb_skip_result

无(10 胜提示下返回 survival_choice_required)

bpb_wait_for_result

timeout_seconds(默认 90), auto_skip(默认 true;提示下不自动选)

bpb_enter_survival

无(选择进入生存模式)

bpb_stop_here

无(选择到此为止)

bpb_choose_survival

choice: survival/stop

bpb_start_run

mode: unranked/ranked;force_new: 强制新开;class_id/loadout(可选)

bpb_continue

无

bpb_health

无


10. 可选 HTTP API

给非 MCP 客户端(脚本、网页、其他 Agent 框架):

python -m bpb_mcp.cli http --port 8766

方法

路径

说明

GET

/health

网桥 + 游戏状态摘要

GET

/state

完整状态(战斗期自动返回摘要)

GET

/phase

轻量状态(战斗期安全)

GET

/tools

工具 schema

POST

/action

{"action":"buy_item","args":{"slot":1}}

示例(PowerShell):

Invoke-RestMethod http://127.0.0.1:8766/state
Invoke-RestMethod http://127.0.0.1:8766/action -Method Post `
  -ContentType 'application/json' `
  -Body '{"action":"buy_item","args":{"slot":1}}'

默认只绑定 127.0.0.1;不要暴露到公网。


11. 命令行

python -m bpb_mcp.cli state
python -m bpb_mcp.cli phase
python -m bpb_mcp.cli health
python -m bpb_mcp.cli buy --slot 1
python -m bpb_mcp.cli reserve --slot 1
python -m bpb_mcp.cli reserve --slot 1 --unreserve
python -m bpb_mcp.cli store --item-id 123456
python -m bpb_mcp.cli sell --item-id 123456
python -m bpb_mcp.cli refresh
python -m bpb_mcp.cli edit --mode bags
python -m bpb_mcp.cli edit-items
python -m bpb_mcp.cli edit-bags
python -m bpb_mcp.cli edit-all
python -m bpb_mcp.cli set-class --class-id 2 --loadout 0
python -m bpb_mcp.cli set-loadout --index 0
python -m bpb_mcp.cli place --item-id 123456 --x 3 --y 3
python -m bpb_mcp.cli place-bag --item-id 81202 --x 2 --y 2
python -m bpb_mcp.cli rotate --item-id 123456
python -m bpb_mcp.cli battle
python -m bpb_mcp.cli wait-result
python -m bpb_mcp.cli enter-survival
python -m bpb_mcp.cli stop-here
python -m bpb_mcp.cli choose-survival --choice survival
python -m bpb_mcp.cli start-run --mode unranked
python -m bpb_mcp.cli start-run --mode unranked --force-new
python -m bpb_mcp.cli start-run --class-id 2 --loadout 0
python -m bpb_mcp.cli continue
python -m bpb_mcp.cli install
python -m bpb_mcp.cli doctor
python -m bpb_mcp.cli launch

12. 故障排查

game_not_running / 连接被拒绝

  • 游戏没启动;先启动游戏。

  • override.cfg 没放到 BackpackBattles.exe 同目录。

  • 游戏是旧进程:必须重启游戏后 autoload 才生效。

  • 端口 8765 被占用:改 mod/McpBridge.gd 的 PORT,并同步 Python Bridge 端口(或设置 BPB_BRIDGE_PORT,需自行扩展)。

api_load_failed

游戏内 user://mcp/bpb_api.gd 编译失败。查看:

%APPDATA%\Godot\app_userdata\Backpack Battles\logs\godot.log

搜索 Parse Error。修改 mod/bpb_api.gd 后重新 install;bridge 会按文件 MD5 热加载,无需重启游戏。

override.cfg 看起来没生效

游戏日志应有:

[McpBridge] bridge starting pid=...
[McpBridge] listen 127.0.0.1:8765 err=0

没有则检查:

  • 文件是否在 exe 同目录(不是 Backpack Battles_Data,游戏是 Godot 不是 Unity);

  • 文件编码是否为 UTF-8;

  • 是否被其他工具覆盖(安装器会备份并合并)。

can_start_battle=false

刚回到商店的 1-2 秒内,转场动画尚未结束。等一会儿再读状态,或使用 bpb_wait_for_result(它会等到 can_start_battle=true)。

刚启动游戏时 start_run 返回 title_not_ready

标题界面 / 职业资源还没加载完。等 3-5 秒后重试;GameAPI.start_run / continue_run 会自动重试约 6 秒。

skip_result 报 result_screen_not_visible

结算界面还没出现。不要提前调用;用 bpb_wait_for_result,它只在 can_skip_result=true 时跳过。

游戏更新后失效

内部类/方法名可能变化。用 tools/pck_inspect.py / tools/pck_extract.py 重新检查 pck,调整 mod/bpb_api.gd。当前验证版本 1.1.9b。

安装器找不到游戏

python -m bpb_mcp.cli install --game-dir "D:\Steam\steamapps\common\Backpack Battles"

13. 安全声明

  • 游戏内 bridge 只绑定 127.0.0.1:8765。

  • bridge 会加载并执行 user://mcp/bpb_api.gd 中的 GDScript;任何能写这个文件的人都能在游戏进程内执行代码。请勿把该目录共享给不可信程序。

  • HTTP API 默认只绑定 localhost;不要改成 0.0.0.0 暴露到公网。

  • 本工具用于单机/私人对局的状态读取与操作自动化;在线排位请自行遵守游戏规则。


14. 开发

目录

mcpbpb/
├─ bpb_mcp/
│  ├─ bridge.py        TCP 客户端(127.0.0.1:8765)
│  ├─ api.py           高层 API + wait/轮询
│  ├─ server.py        MCP stdio 服务(无第三方依赖)
│  ├─ http_api.py      可选 HTTP API
│  ├─ installer.py     override.cfg / user:// 安装、doctor、launch
│  └─ cli.py           命令行
├─ mod/
│  ├─ McpBridge.gd     游戏内 TCP 网桥 + 热加载
│  └─ bpb_api.gd       游戏内状态序列化与操作逻辑
├─ tools/
│  ├─ pck_inspect.py   解析 Godot pck
│  ├─ pck_extract.py   提取 pck 文件
│  ├─ bpb_tcp.py       原始 TCP 客户端
│  └─ smoke_live.py    实时状态 smoke test
├─ tests/test_offline.py
├─ start_mcp.bat
├─ start_http.bat
└─ README.md

热加载

修改 mod/bpb_api.gd 后:

python -m bpb_mcp.cli install

bridge 在下一次 api 调用时按 MD5 重新编译;不需要重启游戏。修改 McpBridge.gd 才需要重启。

添加新操作

  1. 在 mod/bpb_api.gd 增加 func my_action(ctx, args): ...;

  2. 在 bpb_mcp/api.py 增加方法;

  3. 在 bpb_mcp/server.py 的 TOOLS 和 call_tool 中注册;

  4. python -m bpb_mcp.cli install。

测试

python -m unittest discover -s tests -v
python tools\smoke_live.py

15. 已知限制

  • 10 胜选择:state.survival.prompt_visible=true 时必须用 bpb_enter_survival / bpb_stop_here / bpb_choose_survival 明确选择;bpb_skip_result / bpb_wait_for_result 会返回 survival_choice_required,不会自动选择。

  • 胜场:wins / current_run_wins 是当前 run 胜场(10 胜触发提示);total_wins 来自 getRunWins(),是累计胜场。

  • 编辑模式:items 只编辑物品(包固定,物品可临时在包外);bags 只编辑背包(物品绝对格固定);all 全部可编辑。物品可以同时覆盖多个背包,游戏没有单一背包限制。

  • 背包移动/旋转:只允许移动空背包;非空包返回 bag_not_empty。背包放置会强制 faceDirection=0 / rotation=0,按基础 shape 落格,避免 1×2/1×4 背包显示方向与 cells 差 90°;bpb_rotate_bag 返回 rotate_unsupported。

  • 并发限制:同一游戏同时只应有一个 MCP 客户端/服务在操作;两个窗口并发调用会交错修改背包/商店,造成浮空或闪退。

  • 落格/浮空:bpb_place_item 使用游戏原生 pickup(当前位置) → 设目标包围盒中心 → drop(),触发正常落格回调并写回包围盒位置;跨包不拒绝。位置/朝向由游戏 drop 校验。

  • 开战前最终化:bpb_start_battle 只规范化位置/placedByPlayer,不会把跨包物品移入储物箱。

  • 战斗期状态:为避免战斗过渡时访问背包导致游戏崩溃,bpb_get_state 在 state=combat 只返回摘要(combat_only=true、inventory_available=false、backpack=null、shop.items=[])。战斗期用 bpb_get_phase / bpb_wait_for_result,回到 shop 再读完整状态。

  • 出售:bpb_sell_item 只出售背包内物品;储物箱物品需先 bpb_place_item 放回背包。出售会立即移除物品并加金币;非空背包不能直接出售(bag_not_empty)。

  • 物品 id:id 是游戏运行时的 instance id;游戏重启/continue 后会变化,操作前重新 bpb_get_state。

  • 技能:技能是 1×1 可摆放 Item,会占背包格;背包满时先用 bpb_store_item 腾格,再 bpb_place_item。技能随背包持久化。

  • 保留项:bpb_reserve_item 免费;保留项在刷新商店后仍会保留。取消保留传 reserved=false。

  • 存档写入时机:购买/摆放/旋转会在商店关闭或回合推进时由游戏写入 run 存档;不要在商店界面直接退出游戏,否则本次修改可能尚未保存。

  • 物品形状/旋转:普通矩形/常见 L 形物品已验证;带自定义旋转逻辑的物品若原地放不下会回滚并返回 cannot_rotate_in_place,换个位置再试。

  • 描述文本:游戏返回的 description 含 $t[...]、[sc ...] 等富文本标记,模型可自行忽略。

  • last_result 是上一回合结果,不是当前战斗实时结果;用 can_skip_result 判断结算是否出现。

  • 游戏版本:内部方法名基于 1.1.9b;更新后可能需要调整。

  • 仅本地:依赖游戏进程与 127.0.0.1,不支持远程/云游戏。

  • 不修改 pck:如果游戏未来移除 override.cfg 支持,需要改为 pck 注入方案。


16. License

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to play Slay the Spire 2 by exposing game state and actions through an MCP server, supporting combat, rewards, and run management.
    345
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables an MCP agent to observe and command Slay the Spire 2 game state via HTTP, and optionally autopilot combat with a local deterministic policy, without LLM in the critical path.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to interact with running games in real time — introspecting game state, simulating keyboard and mouse input to actually play, capturing screenshots, executing Lua code, hot-reloading files, and receiving push notifications when state changes.
    8
    MIT