更新godot ai插件到最新版本

This commit is contained in:
2026-09-24 15:33:39 +08:00
parent 0484839186
commit 2f662c6c6f
6 changed files with 220 additions and 9 deletions
+28 -4
View File
@@ -14,6 +14,7 @@ const SERVER_PID_FILE := "user://godot_ai_server.pid"
## spawn, so a report that exists afterwards belongs to that launch.
const SERVER_STARTUP_REPORT := "user://godot_ai_server_startup.json"
const WindowsPortReservation := preload("res://addons/godot_ai/utils/windows_port_reservation.gd")
const LinuxProc := preload("res://addons/godot_ai/utils/linux_proc.gd")
static var _process_spawn_mutex := Mutex.new()
enum PortOccupancy { UNKNOWN, FREE, OCCUPIED }
@@ -30,10 +31,10 @@ static func unlock_process_spawn() -> void:
_process_spawn_mutex.unlock()
## A managed Linux server needs a listener PID tool to prove ownership.
## Linux can prove ownership through procfs inside minimal desktop sandboxes.
## Query each launch so installing the missing tool makes Retry work.
static func listener_tools_problem() -> String:
if OS.get_name() != "Linux":
if OS.get_name() != "Linux" or LinuxProc.available():
return ""
var output: Array = []
var available := OS.execute("/bin/sh", ["-c",
@@ -41,7 +42,7 @@ static func listener_tools_problem() -> String:
], output, true)
if available == 0:
return ""
return "Cannot verify Linux listener ownership: neither lsof nor ss is available on the editor's PATH. Install lsof or iproute2 (which provides ss), then retry starting the server."
return "Cannot verify Linux listener ownership: /proc is unavailable and neither lsof nor ss is available on the editor's PATH. Make /proc accessible inside the editor environment, or install lsof or iproute2 there, then retry starting the server."
static func can_bind_local_port(port: int) -> bool:
@@ -66,6 +67,13 @@ static func is_port_in_use(port: int) -> bool:
## a wrapping caller's startup trace sees a genuine PowerShell fallback
## as `powershell`, not as a silent extra second under `netstat`.
static func is_port_in_use_via_scrape(port: int, trace: Callable = Callable()) -> bool:
if OS.get_name() == "Linux":
var linux := LinuxProc.listener_snapshot()
if linux.known:
return linux.listeners.has(port)
## A failed observation cannot prove that a port is free.
if not can_bind_local_port(port):
return true
var output: Array = []
if OS.get_name() == "Windows":
return windows_port_occupancy(port, windows_listener_snapshot(trace)) != PortOccupancy.FREE
@@ -189,6 +197,12 @@ static func find_all_pids_on_port(port: int, trace: Callable = Callable(), snaps
if snapshot.known:
pids.assign(snapshot.listeners.get(port, []))
return pids
if OS.get_name() == "Linux":
var linux := LinuxProc.listener_snapshot()
if linux.known:
var linux_pids := LinuxProc.listener_pids(port, linux)
if not linux_pids.is_empty() or not linux.listeners.has(port):
return linux_pids
var output: Array = []
_trace(trace, "lsof")
var exit_code := OS.execute("lsof", ["-ti:%d" % port, "-sTCP:LISTEN"], output, true)
@@ -350,6 +364,8 @@ static func pid_alive(pid: int, snapshot: Variant = null) -> bool:
return false
if snapshot != null:
return not _process_snapshot_row(snapshot, pid).is_empty()
if OS.get_name() == "Linux":
return LinuxProc.is_alive(LinuxProc.process_stat(pid))
if OS.get_name() == "Windows":
var output: Array = []
var exit_code := OS.execute("tasklist", ["/FI", "PID eq %d" % pid, "/NH", "/FO", "CSV"], output, true)
@@ -373,10 +389,12 @@ static func pid_alive(pid: int, snapshot: Variant = null) -> bool:
## One Windows query captures the target and at most fifteen ancestors. The
## dictionary belongs to one proof boundary, never a cache: callers must take
## a fresh snapshot when closing the capture window or authorizing a kill.
## Non-Windows callers receive null and keep the existing live-query path.
## Linux captures the same bounded ancestry using procfs; macOS keeps ps.
## The process enumeration stays inside PowerShell; only the bounded target
## ancestry crosses back into Godot, without logging command-line metadata.
static func capture_process_snapshot(pid: int) -> Variant:
if OS.get_name() == "Linux":
return LinuxProc.process_snapshot(pid)
if OS.get_name() != "Windows":
return null
if pid <= 1:
@@ -511,6 +529,8 @@ static func process_commandline(pid: int, snapshot: Variant = null) -> String:
if execute_windows_powershell(script, output) != 0 or output.is_empty():
return ""
return str(output[0]).strip_edges()
if OS.get_name() == "Linux":
return LinuxProc.commandline(pid)
var proc_path := "/proc/%d/cmdline" % pid
if FileAccess.file_exists(proc_path):
var file := FileAccess.open(proc_path, FileAccess.READ)
@@ -539,6 +559,8 @@ static func process_parent(pid: int, snapshot: Variant = null) -> int:
return 0
if snapshot != null:
return int(_process_snapshot_row(snapshot, pid).get("parent_pid", 0))
if OS.get_name() == "Linux":
return int(LinuxProc.process_stat(pid).get("parent_pid", 0))
var output: Array = []
if OS.get_name() == "Windows":
var script := (
@@ -599,6 +621,8 @@ static func pid_cmdline_is_godot_ai(pid: int, snapshot: Variant = null) -> bool:
static func process_fingerprint(pid: int, snapshot: Variant = null) -> String:
if snapshot == null and OS.get_name() == "Linux":
snapshot = capture_process_snapshot(pid)
if not pid_alive(pid, snapshot):
return ""
var output: Array = []