extends SceneTree
## Gate: the walker brain drives a player through its INPUT fields.
##
## The failure this guards is silent and would look like a netcode bug: a brain
## that writes input_direction while player_input.gd is still reading the keyboard
## has its writes erased on the very next physics frame, so the avatar stands
## still and nothing anywhere reports an error.
##
## Uses a stand-in player rather than the real scene: player_procedural.tscn pulls
## a skeleton, an animation tree and a camera in to prove a contract about four
## variables. What matters is the CONTRACT -- a node named InputSync carrying the
## four synced fields plus `driven` -- and that is reproducible exactly.
var ok := true
var _phase := 0
var _sh: Node = null
var _input: Node = null
const BOT := "gate_walker_drive"
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c:
ok = false
func _process(_delta: float) -> bool:
_phase += 1
if _phase == 1:
_setup()
return false
if _phase < 8:
return false
_check()
print("GATE walker_bot: %s" % ("PASS" if ok else "FAIL"))
quit(0 if ok else 1) # returning true alone exits 0 — a FAIL never reached CI
return true
## A node that satisfies exactly the contract walker.gd relies on.
func _setup() -> void:
_sh = root.get_node_or_null(^"/root/ScriptHost")
if _sh == null:
_p(false, "ScriptHost present")
return
var game := Node.new()
game.name = "Game"
root.add_child(game)
var players := Node.new()
players.name = "Players"
game.add_child(players)
var player := Node3D.new()
player.name = "1"
player.set_script(load("res://scenes/test/stub_local_player.gd"))
players.add_child(player)
_input = Node.new()
_input.name = "InputSync"
_input.set_script(load("res://scenes/player/player_input.gd"))
player.add_child(_input)
var head := Node3D.new()
head.name = "CameraHead"
player.add_child(head)
_p(_input.get("driven") == false, "player_input starts undriven")
var inst: Object = _sh.run_file("res://bots/walker.gd", BOT, ["session"], {"bot_id": BOT})
_p(inst != null, "walker brain compiled and ran")
# THE ASSERTION: without this the brain's writes are erased every physics frame
# and the avatar silently never moves.
_p(bool(_input.get("driven")), "brain set driven=true so player_input stands down")
func _check() -> void:
if _input == null:
return
_p(Vector2(_input.get("input_direction")) == Vector2(0.0, -1.0),
"brain holds forward on input_direction (got %s)" % str(_input.get("input_direction")))
_p(float(_input.get("camera_rotation_y")) != 0.0,
"brain is sweeping camera_rotation_y (%.3f)" % float(_input.get("camera_rotation_y")))
# It must steer, never teleport: a bot that moved a position would replicate
# through a different path and prove nothing about the one under test.
# CODE ONLY, NOT COMMENTS. The first version of this searched the whole file and
# failed on walker.gd's own comment explaining why it never writes a position --
# the instrument matching the documentation of the thing it was checking for.
var src: String = FileAccess.get_file_as_string("res://bots/walker.gd")
var writes_position := false
for line: String in src.split("\n"):
var code: String = line.strip_edges()
if code.begins_with("#") or code.begins_with("##"):
continue
if code.contains("global_position") or code.contains("global_transform"):
writes_position = true
_p(not writes_position,
"walker never writes a position — input only, so it exercises the human path")
# Releasing the player is part of the contract: a driven flag left set makes the
# peer unresponsive to its own keyboard with no visible cause.
_sh.unload(BOT)
_p(not bool(_input.get("driven")), "_bot_exit handed the player back (driven=false)")
_p(Vector2(_input.get("input_direction")) == Vector2.ZERO, "and stopped it moving")