From 21420ba3096bce66e85e9d9379f6e4e1c2226ae1 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 2 Oct 2026 07:56:50 +0800 Subject: [PATCH] =?UTF-8?q?probe=E6=8C=82=E6=AD=BB=E7=81=BE=E5=AE=B3?= =?UTF-8?q?=E6=89=B9=E2=80=94=E2=80=94shot=5Fprobe=20headless=20fail-fast?= =?UTF-8?q?=20=E6=8A=A4=E6=A0=8F=EF=BC=88headless=20=E4=B8=8B=20frame=5Fpo?= =?UTF-8?q?st=5Fdraw=20=E6=B0=B8=E4=B8=8D=E8=A7=A6=E5=8F=91=E5=BF=85?= =?UTF-8?q?=E6=8C=82=E6=A0=B9=E6=B2=BB=EF=BC=8C=E5=AE=9E=E6=B5=8B=200.68s?= =?UTF-8?q?=20=E7=A7=92=E9=80=80=20exit1=EF=BC=89+=E8=BD=BB=E9=97=B8?= =?UTF-8?q?=E6=AD=A3=E4=B8=BB=20tools/probe=5Fgate.sh=EF=BC=88input=20?= =?UTF-8?q?=E6=AE=B5=E9=BB=98=E8=AE=A4=E5=BF=85=E8=B7=91/click=20=E6=AE=B5?= =?UTF-8?q?=20--click=20=E6=98=BE=E5=BC=8F=E5=8A=A0=E8=B7=91=E5=B8=A6=2024?= =?UTF-8?q?0s=20=E8=B6=85=E6=97=B6=E7=BD=91/shot=5Fprobe=20=E7=A6=81?= =?UTF-8?q?=E5=85=A5=E8=87=AA=E5=8A=A8=E9=97=B8=E5=BD=92=E7=9C=9F=E4=BA=BA?= =?UTF-8?q?=E7=9B=AE=E6=A3=80=EF=BC=9B=E4=B8=89=E9=97=B8=3D=E6=AF=8F?= =?UTF-8?q?=E6=AE=B5=20timeout=20=E5=AE=88=E5=8D=AB+=E6=8C=89=E6=9C=AC=20g?= =?UTF-8?q?ame=20=E8=B7=AF=E5=BE=84=E7=B2=BE=E7=A1=AE=E9=A2=84=E6=B8=85?= =?UTF-8?q?=E6=AE=B5=E5=90=8E=E6=B8=85=E5=AD=A4=E5=84=BF=E3=80=94=E4=B8=8D?= =?UTF-8?q?=E8=AF=AF=E6=9D=80=E4=BB=96=E4=BC=9A=E8=AF=9D=E3=80=95+?= =?UTF-8?q?=E6=89=B9=E6=9C=AB=E6=AE=8B=E7=95=99=E6=B8=85=E7=82=B9=E5=BF=85?= =?UTF-8?q?=E9=A1=BB=200=EF=BC=89=EF=BC=9B=E9=AA=8C=E8=AF=81=3D=E6=8A=A4?= =?UTF-8?q?=E6=A0=8F=E7=A7=92=E9=80=80/=E9=BB=98=E8=AE=A4=E9=97=B8=204.99s?= =?UTF-8?q?=20=E5=85=A8=E7=BB=BF/=E4=BA=BA=E9=80=A0=E5=83=B5=E5=B0=B8?= =?UTF-8?q?=C3=972=20=E8=A2=AB=E9=A2=84=E6=B8=85=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E9=97=B8=E5=90=8E=E5=BD=92=E9=9B=B6=EF=BC=9Bmaster=20=E7=89=88?= =?UTF-8?q?=20click=20=E6=8E=A2=E9=92=88=E7=8E=B0=E7=8A=B6=205=20=E5=88=86?= =?UTF-8?q?=E9=92=9F=E4=B8=8D=E6=94=B6=E6=95=9B=EF=BC=88apply=5Fnode=5Fbon?= =?UTF-8?q?us=20=E6=8B=92=E7=BB=9D=E5=88=B7=E5=B1=8F=EF=BC=89=E5=BD=92=20p?= =?UTF-8?q?robe=E9=80=82=E9=85=8D=E6=89=B9=EF=BC=8C=E4=BF=AE=E5=A5=BD?= =?UTF-8?q?=E5=90=8E=20--click=20=E6=AE=B5=E8=87=AA=E5=8A=A8=E8=BD=AC?= =?UTF-8?q?=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/smoke/shot_probe.gd | 5 +++ tools/probe_gate.sh | 72 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tools/probe_gate.sh diff --git a/game/smoke/shot_probe.gd b/game/smoke/shot_probe.gd index edda5c6f..7f4c5215 100644 --- a/game/smoke/shot_probe.gd +++ b/game/smoke/shot_probe.gd @@ -16,6 +16,11 @@ var _meta := {} # 控件实测几何(全局 rect——shot_checker 文字框 func _ready() -> void: + # 运行前提=窗口化真渲染面:headless 下 frame_post_draw 永不触发+视口纹理恒空——无此守卫时 headless 调用方必挂死(2026-10-02 挂死灾害批护栏) + if DisplayServer.get_name() == "headless": + print("[shot_probe] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);headless 自动闸禁跑本探针(正主=tools/probe_gate.sh)") + get_tree().quit(1) + return var args := OS.get_cmdline_user_args() out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://qa-screens" DirAccess.make_dir_recursive_absolute(out_dir) diff --git a/tools/probe_gate.sh b/tools/probe_gate.sh new file mode 100644 index 00000000..d6233932 --- /dev/null +++ b/tools/probe_gate.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# 轻闸探针正主(2026-10-02 挂死灾害批落籍)——headless 安全段固化,禁再手拼同款 Godot 命令。 +# 三闸:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿 Godot +# (按命令行精确匹配本 game 路径,不误杀其他 worktree 会话的合法进程)③末尾残留清点=批末闸(必须 0)。 +# 段:ui_input_probe=默认必跑;--click=加跑 ui_click_probe 行为走查(自退出+退出码闸;超时多半= +# 探针未适配新停点层,归 probe适配批);shot_probe 禁入本闸——必须窗口化运行(headless 无渲染面, +# 探针内已有 fail-fast 护栏),归真人目检。 +# 用法: tools/probe_gate.sh [game目录] [--click] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree) +# 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell;Godot 路径可用环境变量 GODOT_BIN 覆盖。 +set -u +G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}" +REPO="$(cd "$(dirname "$0")/.." && pwd)" +GAME=""; WITH_CLICK=0 +for a in "$@"; do + case "$a" in + --click) WITH_CLICK=1 ;; + *) GAME="$a" ;; + esac +done +GAME="${GAME:-$REPO/game}" +GAME_WIN="$(cygpath -m "$GAME")" # Godot --path 需 Windows 形式 +TMP="$(mktemp -d /tmp/probegate_XXXXXX)" +trap 'rm -rf "$TMP"' EXIT +_rc=0 + +orphans() { # 列出命令行含本 game 路径的 Godot 进程 PID(console wrapper+真引擎都算) + printf "Get-CimInstance Win32_Process -Filter \"Name like 'Godot%%'\" | Where-Object { \$_.CommandLine -like '*%s*' } | ForEach-Object { \$_.ProcessId }\n" "$GAME_WIN" > "$TMP/k.ps1" + powershell -NoProfile -ExecutionPolicy Bypass -File "$(cygpath -w "$TMP/k.ps1")" 2>/dev/null | tr -d '\r' +} +kill_orphans() { + local p; for p in $(orphans); do taskkill //F //PID "$p" >/dev/null 2>&1; done +} + +# 冷检出刷导入(.godot 不入 git,fresh worktree 首跑一次性付) +if [ ! -f "$GAME/.godot/uid_cache.bin" ]; then + echo "[probe_gate] 冷检出:editor --quit 刷导入(一次性,可到 1-2 分钟)" + timeout 300 "$G" --headless --path "$GAME_WIN" --editor --quit >/dev/null 2>&1 + kill_orphans +fi + +pre="$(orphans | wc -l | tr -d ' ')" +if [ "$pre" -gt 0 ]; then echo "[probe_gate] 预清本闸残留 Godot×$pre"; kill_orphans; fi + +echo "======== ui_input_probe ========" +timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_input_probe.tscn >"$TMP/uip.log" 2>&1 +st=$?; kill_orphans +grep -E "PASS|FAIL|问题" "$TMP/uip.log" | head -12 +if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — ui_input_probe 超时挂死(exit=$st),孤儿已清"; _rc=1 +elif ! grep -q "PASS" "$TMP/uip.log"; then + echo "[probe_gate] FAIL — ui_input_probe 未见 PASS(exit=$st)"; _rc=1 +fi + +if [ "$WITH_CLICK" -eq 1 ]; then + echo "======== ui_click_probe 行为走查 ========" + timeout 240 "$G" --headless --path "$GAME_WIN" res://smoke/ui_click_probe.tscn >"$TMP/clk.log" 2>&1 + st=$?; kill_orphans + grep -E "click_probe" "$TMP/clk.log" | tail -4 + if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — ui_click_probe 超时(exit=$st):挂死或探针未适配新停点层(归 probe适配批),孤儿已清"; _rc=1 + elif [ "$st" -ne 0 ]; then + echo "[probe_gate] FAIL — ui_click_probe 退出码=$st(done=false 或 问题>0)"; _rc=1 + fi +fi + +echo "======== 残留检查(批末闸) ========" +left="$(orphans | wc -l | tr -d ' ')" +total="$(tasklist 2>/dev/null | grep -ci '^Godot' || true)" +echo "本闸残留 Godot=$left(必须0) 全局 Godot 进程=$total(其他会话的仅报数不算账)" +if [ "$left" -ne 0 ]; then echo "[probe_gate] FAIL — 本闸残留未清零"; kill_orphans; _rc=1; fi +if [ "$_rc" -eq 0 ]; then echo "[probe_gate] 全绿"; else echo "[probe_gate] 存在 FAIL,见上"; fi +exit "$_rc"