OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_walker_bot.gd

106 lines · gd ref:
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")