bihong2026/tankbattle--plugins-tank-battle ↗★ 0

dsh-tank-battle

打坦克:在侧栏开一个页面,红白机经典打坦克(Battle City)。单人/组队打 NPC,也可以各带一队 NPC 互相对战,四档 AI 随时换,带在线用户发现与积分榜 适合桌面端或 Web 端用户在侧栏进行单人、组队或对战坦克游戏。

パッケージ
dsh-tank-battle
互換性
未検証
バージョン
0.3.1
ライセンス
MIT
最終更新
2026/10/10

インストール

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:bihong2026/tankbattle#1822e0a65457ee578a1de1939e6bb0ed6e1f3eee&path:plugins/tank-battle

ドキュメント

README 全文を読む ↗

DSH 打坦克插件

红白机经典打坦克(Battle City)娱乐插件。单人打 NPC,也能发现同时在线的其他玩家、 邀请组队一起打,或者各带一队 NPC互相对战。装好之后侧栏会多出一项「打坦克」, 点开就是一整页游戏,随时开打、随时结束,还带积分榜。

游戏本体纯浏览器端跑,不依赖任何第三方库,也不改官方任何一个文件。联机部分靠一台打坦克 后台服务(见仓库里的 server/),自己部署一台就行。

安装

桌面版(DeepSeek Harness)没有可用的命令行安装方式:dsh 启动器对 desktop 这个 profile 会直接报错(「managed exclusively by the desktop application」),它由桌面应用 独占管理。所以走界面装:

  1. 打开应用 → 插件 → 添加插件;
  2. 粘贴下面任意一种地址,点安装;
  3. 完整退出应用再启动(关窗口不算,要从托盘退出)——宿主半要换新版才会带上前端要的那几条路由。
形式填什么
Git(推荐)https://github.com/bihong2026/tankbattle.git#path:plugins/tank-battle
打不开 GitHub 就用 https://gitee.com/bihong2023/tankbattle.git#path:plugins/tank-battle
本地目录把仓库克隆到本地后,填 plugins/tank-battle 这个目录的绝对路径(例如 D:\code\tankbattle\plugins\tank-battle)
包名dsh-tank-battle

装完后侧栏会出现「打坦克」。版本号显示在页面右上角;如果页面提示「请完整退出应用再启动」, 说明前端半已经换了、宿主半还是旧的,退出重开即可。

命令行安装(web / headless / sdk / acp 等 profile)

非桌面 profile 可以直接用 dsh plugin(它会把参数转发给 pnpm,所以 pnpm 要在 PATH 上):

# 直接从 GitHub 装(Gitee 镜像把 github.com/bihong2026 换成 gitee.com/bihong2023 即可)
dsh plugin --profile 
 add "git+https://github.com/bihong2026/tankbattle.git#path:plugins/tank-battle"
# 或者先克隆再装本地目录
dsh plugin --profile 
 add ./plugins/tank-battle
dsh plugin --profile 
 remove dsh-tank-battle

增删组合包后必须重启那个 profile 才会生效。桌面版的 desktop profile 不接受命令行安装, 只能走上面的界面。

怎么玩

操作按键
上下左右↑ ↓ ← →(或 W A S D)
开火空格(或 J / Enter)
暂停 / 继续Esc(或 P)

嫌键盘不方便也可以用页面上的方向键和「开火」按钮(按住即可持续)。

  • 砖墙一发打穿一格;升到三星的子弹能打穿钢墙。
  • 打死特定敌兵会掉道具(星、头盔、时钟、铁锹、手雷、额外命),吃到就生效。
  • 单人/组队:基地被毁或我方生命耗尽即结束。
  • 对战:打掉对面基地就赢,自家基地没了就输;兵打光了还没人掉基地就按比分定胜负。

三种模式

模式说明
单人一辆坦克打 NPC,人机对战。
组队你和队友都在蓝队,一起打中立的 NPC(灰)。子弹只伤非同队,所以不会误伤队友。
对战你(蓝队 A)和对手(红队 B)各带一队 NPC 互相攻防,两边各有一个基地。

联机流程

  1. 进页面就是大厅:能看到在线的人、收到的邀请、别人开的公开房间、以及积分榜。
  2. 点某人后面的「组队」或「对战」发邀请;对方在大厅点「接受」就成房。
  3. 也可以自己「开组队房 / 开对战房」,把房间号发给对方让他「加入」。
  4. 点「开始游戏」。房主权威:房主跑真正的游戏引擎,按节流把画面推给客人; 客人只把按键传过去、照着房主的画面渲染。所以场上永远只有一份真值,两边不会跑偏。
  5. 打完之后积分由服务端算(客户端只上报结果),记进积分榜。

身份

联机要有个名字挂在积分榜上,插件按两条来源取:

  1. 登录的 DeepSeek 账号 —— 走 Harness 自带的账号服务,取稳定账号 UID。 换机器、重装 Harness,只要还是同一个账号,积分和战绩就还是连着的那一份。
  2. 游客 —— 没登录就用一个按「harness home」生成的本机游客号(guest- 开头)。 游客一样能发现人、能邀请、能开房、能上榜,只是名字是「游客 XXXX」而不是你的账号。

两种身份都在宿主半读取,凭证一个字都不出那个进程,前端拿不到也传不出去。 所以单人玩法完全不需要登录,不联网、不装后台服务也能玩。

敌方 AI

开局前可以在页面上选,开局后也能随时换,下一帧就生效:

档位说明
新兵乱走乱打,偶尔摸到基地
老兵直线逼近基地和玩家,对上了就开火
猎手会绕路、会卡位、看到子弹会闪
王牌绕路包抄、预判走位、集火基地

猎手和王牌用 Dijkstra 在「地形代价场」上寻路,所以会主动绕开钢墙和河,不会一根筋撞墙。

页面上的设置

  • 模式:单人 / 组队 / 对战。
  • 敌方 AI:四档,见上。
  • 敌人总数:10 / 20 / 30 辆。
  • 我方生命:1 / 3 / 5 / 9 条。
  • 起始关卡:第 1–4 关(四张手写地图)。
  • 音效:开关。音效是现场用 WebAudio 合成的,不带任何音频文件。

这些选择只作用于当次,不写盘。

插件设置(默认值)

cordis.patch.yml 的 config 里放开局默认值和后台服务地址(改完要重启应用才生效):

config:
  defaultAi: 'veteran'                        # rookie / veteran / hunter / ace
  enemyCount: 20                              # 4 到 60
  lives: 3                                    # 1 到 9
  sound: true
  serverBase: 'http://122.51.206.118/tanks'   # 联机用的后台服务;默认是作者架好的公网服务

装好之后不改也能玩:单人模式跟后台无关;联机也开箱可用,因为默认地址指向仓库作者 公网部署的那一份服务。前端每次开局先向宿主半要一遍这几个值当默认。

想连自己架的服务(或者连别人架好的),别改包里的文件 —— 在 $DSH_HOME/cordis.patch.yml(默认 ~/.dsh/cordis.patch.yml,这一层在每个 bundle 之后生效、 且对所有 profile 生效)里覆盖这个插件的 serverBase 就行:

- id: tank-battle
  config:
    defaultAi: 'veteran'
    enemyCount: 20
    lives: 3
    sound: true
    serverBase: 'http:///tanks'   # 末尾不要斜杠,宿主半会自己接 /api/...

patch 的 config 是整体替换而不是深合并:只写 serverBase 也不会出问题(没写的键会 回落到代码里的默认值,和上面那份一致,自检会核对),但把五个键都写上更不容易看糊。

也可以直接设环境变量 TANK_SERVER_BASE,它的优先级最高。三种来源的优先级是 环境变量 → 插件设置 → 代码默认值。

默认为什么指向作者的服务器:绝大多数装插件的人不会在自己机器上架一个后台,把默认留成 http://127.0.0.1:9010 的结果是每个人一进联机就报「连不上打坦克后台服务」——这是 作者踩过的坑。默认指向已部署好的公网服务,两个人各装一个插件就能直接凑一桌。 自己架服务、不想用作者那台,按上面的办法覆盖一行即可;怎么把服务架到公网、怎么在既有 80 端口上做路径反代,见 server/README.md 的「放到公网」。

宿主半的路由

四条精确路由加一条前缀反代,全部挂在官方的 dsh-host-webserver 上,与页面同源。桌面端由 dsh-app:// 协议整体转发到宿主,所以 Web 形态和桌面形态共用同一份前端代码,也就不用让 Electron 外壳再开 IPC 通道。

路由作用
/tank-battle/ping探活 + 版本自检,顺带把插件设置里的默认值发给前端
/tank-battle/stats读本机战绩($DSH_HOME/tank-battle.json)
/tank-battle/record记一局的结果:分数、打到第几关、哪一档 AI、结果
/tank-battle/identity报本机身份(登录的账号 / 游客),联机时拿它当名字
/tank-battle/api/*(前缀)反代到打坦克后台服务,在线发现 / 邀请 / 房间 / 积分都走这条

反代必须走宿主,不能让前端直接连后台服务:页面自己的来源不是后台服务,浏览器会拦(跨域, 桌面端还会判混合内容);身份也一样,只有宿主半拿得到。

后台服务连不上时反代回 {ok:false, message:"连不上打坦克后台服务(…)"},大厅就是空的, 单人玩法一点不受影响。

战绩是「读—合—写」,脏数据一律收拾成 0 而不是 NaN;文件坏了就退回空战绩,不抛错。

前端半和宿主半版本对不上时(换包后宿主没重启),页面会直接提示「请完整退出应用再启动」。

文件

client.js          浏览器半:引擎 + 绘制 + 大厅 + 那一页(只 require("react"),样式只用 --dsw-alias-* 令牌)
index.js           宿主半:注册四条同源路由 + 一条反代前缀路由
identity.js        本机身份:登录的 DeepSeek 账号,退回游客(只读,不写凭证)
tank-stats.js      战绩落盘($DSH_HOME/tank-battle.json)
cordis.patch.yml   打包补丁:插件的默认设置与后台服务地址
icon.svg           图标(像素坦克 + 砖纹)
preview.html       不装插件也能打开看/玩的单张页面(直接引同一个 client.js,用真实引擎)
locale/zh.json     中文标题与描述
locale/en.json     英文标题与描述
test/run.mjs       纯逻辑 + 路由契约 + 身份分支 + 多人(座位表 / 出生点 / 快照 / 击杀计数)+ 真实对局自检
test/render.mjs    页面行为自检(假 React / 假画布 / 假后台服务,把整页真跑起来)
test/preview.mjs   preview.html 的自检(假 DOM 把整页真跑一遍,顺带核对页面读的每个 id 都存在)

先看一眼再装

preview.html 是给「不想先装插件、只想看看长什么样」用的:双击打开即可(file:// 也能跑, 页面不依赖 fetch),三种模式、四档 AI、屏幕上的方向键都能用;组队/对战时二号位用 I J K L + U 开火。它直接引 client.js 里的同一套引擎,不是另抄一版,所以看到的 就是插件里那个画面。

自检

三套自检都不联网、不依赖第三方库、不开真浏览器,直接用 node 跑:

node test/run.mjs      # 引擎、地图、四档 AI、完整对局、路由契约、身份分支、反代、座位表、出生点、快照、战绩写盘
node test/render.mjs   # 首屏 → 大厅 → 邀请 → 接受 → 开房 → 开始 → 操作 → 暂停 → 结束 → 上报战绩
node test/preview.mjs  # 预览页:首屏 → 单人(操作/暂停/结束)→ 组队两套键 → 对战双队比分

三套都全过才算这版能交。写盘的用例跑在子进程里、并把 DSH_HOME 指到临时目录, 不会动你本机那份 tank-battle.json。

身份那一段不用真去登录:test/run.mjs 拿假账号服务把「登录 / 没登录 / 服务不存在 / 服务自己报错 / 资料里没有 ID」几条分支全撞一遍。

联机那一段还有一份对真实后台服务的契约自检,在 server/test/client_contract.py (只用 Python 标准库,字段名全部抄自 client.js,改契约会先红):

python3 ../server/test/client_contract.py http://127.0.0.1:9010

后台服务

放在仓库的 server/ 下,是一份独立的 Flask + MySQL 小服务(systemd + gunicorn 托管, 默认监听 9010)。装法、接口清单、容量压测和自检见 server/README.md。

不装它也能玩:单人和四档 AI 都在浏览器里跑,联机功能会安静地降级成「大厅是空的」。