extends SceneTree
## Gate: ScriptHost can load a brain that ships inside the pck.
##
## THE BUG THIS EXISTS FOR. run_file() read every script with
## FileAccess.open(path) + get_as_text() and compiled the source. GDScript
## exports as .gdc BINARY TOKENS, so in an exported build every
## res://bots/*.gd returned error 7 and no shipped brain could run. 0.1141.0
## put bots/*.gd into the pck (export_filter="selected_scenes" had been
## dropping them) and nothing could read them once there. hyper-games #77
## item 3; script_host.gd had flagged it as unverified, and it is verified.
##
## ⛔ WHAT THIS GATE CANNOT DO. The real condition exists only in an EXPORTED
## build -- from the source tree res://bots/walker.gd is an ordinary .gd on
## disk, so the OLD code passes a functional load here too. A gate that only
## called run_file() would be green before and after the fix and would prove
## nothing. That is exactly the shape hyper-games #68 warns about: a gate that
## supplies the condition hiding the bug.
##
## So this checks two different things:
## 1. functionally, that a res:// brain loads and initialises at all
## (a regression guard, honest about being weaker than the real case)
## 2. structurally, that run_file takes a res:// branch which uses load()
## rather than FileAccess -- the part that actually differs in the pck
## with a self-check proving the structural test can fail.
var ok := true
var _ran := false
const FIXTURE := "res://scenes/test/fixture_brain.gd"
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c:
ok = false
func _process(_delta: float) -> bool:
if _ran:
return true
_ran = true
_run()
print("GATE script_host_res: %s" % ("PASS" if ok else "FAIL"))
if not ok:
quit(1)
return true
func _run() -> void:
# ---- 1. functional: a res:// brain loads and runs _bot_init ----
var host: Node = root.get_node_or_null("ScriptHost")
_p(host != null, "ScriptHost autoload present")
if host == null:
return
var inst: Variant = host.run_file(FIXTURE, "gate_fixture")
_p(inst != null, "run_file loaded %s" % FIXTURE.get_file())
if inst != null:
_p(bool(inst.get("inited")), "_bot_init ran on the loaded brain")
host.unload("gate_fixture")
# Every brain we actually ship must load the same way.
for brain: String in ["res://bots/beacon.gd", "res://bots/patrol.gd", "res://bots/walker.gd"]:
# Loaded as a RESOURCE, not run: beacon writes durable state in _bot_init
# and this gate must not put keys in a real room.
var gd: Variant = load(brain)
_p(gd != null and gd is GDScript, "%s loads as a GDScript" % brain.get_file())
# ---- 2. structural: run_file must have a res:// branch that uses load() ----
var src: String = FileAccess.get_file_as_string("res://autoloads/script_host.gd")
_p(src != "", "script_host.gd readable")
if src == "":
return
var idx: int = src.find("func run_file")
_p(idx >= 0, "run_file exists")
if idx < 0:
return
var body: String = src.substr(idx)
var nxt: int = body.find("\nfunc ")
if nxt > 0:
body = body.substr(0, nxt)
_p(body.contains("res://"),
"run_file distinguishes res:// -- without it a packed brain is read as source")
_p(body.contains("load("),
"run_file uses load() for the packed case (.gdc has no readable source text)")
# ---- 3. BotRegistry's own pre-check: the SAME bug in a second place ----
# _start_script_brain refuses a brain before ScriptHost is ever called, and it
# used FileAccess.file_exists(). In the pck res://bots/beacon.gd does not exist
# under that name -- beacon.gdc does -- so every script brain was refused with
# "no such script" while run_file was perfectly able to load it. Found by
# registering the bot matrix against the DEPLOYED build; no gate saw it,
# including the first version of this one, because from the source tree both
# checks answer true.
var br: Node = root.get_node_or_null("BotRegistry")
_p(br != null, "BotRegistry autoload present")
if br != null:
var has_check: bool = br.has_method("_brain_script_exists")
_p(has_check, "BotRegistry routes its existence check through _brain_script_exists()")
if has_check:
for brain: String in ["res://bots/beacon.gd", "res://bots/patrol.gd", "res://bots/walker.gd"]:
_p(bool(br._brain_script_exists(brain)), "%s is found by the pre-check" % brain.get_file())
_p(not bool(br._brain_script_exists("res://bots/definitely_not_here.gd")),
"a missing res:// brain is still reported missing")
_p(not bool(br._brain_script_exists("user://definitely_not_here.gd")),
"a missing user:// brain is still reported missing")
var reg_src: String = FileAccess.get_file_as_string("res://autoloads/bot_registry.gd")
var si: int = reg_src.find("func _start_script_brain")
_p(si >= 0, "_start_script_brain exists")
var sbody: String = reg_src.substr(si, 900) if si >= 0 else ""
_p(not sbody.contains("FileAccess.file_exists"),
"_start_script_brain no longer asks FileAccess whether a packed brain exists")
# SELF-CHECK: the pre-fix shape must be detected as lacking that branch, or
# the two assertions above could pass on any file that merely mentions load().
var old_shape: String = 'func run_file(path: String) -> Object:\n\tvar f := FileAccess.open(path, FileAccess.READ)\n\treturn run(f.get_as_text(), path)\n'
_p(not old_shape.contains("res://"),
"self-check: the pre-fix run_file is detected as having no res:// branch")