51 lines
1.5 KiB
GDScript
51 lines
1.5 KiB
GDScript
@tool
|
|
class_name McpGameLogBuffer
|
|
extends McpStructuredLogRing
|
|
|
|
## Ring buffer for game-process log lines (print, push_warning, push_error)
|
|
## ferried back from the playing game over the EngineDebugger channel.
|
|
##
|
|
## Larger cap than McpEditorLogBuffer because games can be noisy. `run_id`
|
|
## rotates each time clear_for_new_run() fires (called on the game's
|
|
## mcp:hello boot beacon), giving agents a stable cursor for "lines since
|
|
## this play started".
|
|
##
|
|
## Single-threaded — game_helper.gd drains its logger from `_process` and
|
|
## calls `append` from the main thread, so this subclass can use the base
|
|
## ring's lockless reads/writes directly.
|
|
|
|
const MAX_LINES := 2000
|
|
|
|
var _run_id := ""
|
|
|
|
|
|
func _init() -> void:
|
|
super._init(MAX_LINES)
|
|
|
|
|
|
func append(level: String, text: String, details: Dictionary = {}) -> void:
|
|
var entry := {"source": "game", "level": _coerce_level(level), "text": text}
|
|
if not details.is_empty():
|
|
entry["details"] = details.duplicate(true)
|
|
_append_entry(entry)
|
|
|
|
|
|
## Rotate the run identifier and drop all buffered entries. Called when the
|
|
## game-side autoload sends its mcp:hello beacon, marking a fresh play cycle.
|
|
## Returns the new run_id.
|
|
func clear_for_new_run() -> String:
|
|
_clear_storage()
|
|
_run_id = _generate_run_id()
|
|
return _run_id
|
|
|
|
|
|
func run_id() -> String:
|
|
return _run_id
|
|
|
|
|
|
static func _generate_run_id() -> String:
|
|
## Opaque to agents — they only check equality. Time-based is plenty
|
|
## unique within a single editor session and avoids the RNG-seed
|
|
## reproducibility footgun.
|
|
return "r%d" % Time.get_ticks_msec()
|