OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_home_button.gd

183 lines · gd ref:
extends SceneTree
## Gate H4 (docs/home-world-design.md §3): the main menu's Home button.
##
##   1. before the daemon / identity: disabled, and says why
##   2. daemon not running: disabled, says so
##   3. identity loaded: asks home.get once; enabled as "Home"
##   4. pressed: selects the home room with the OFFLINE peer — no mesh, no DHT
##      (the mesh path waits for a DHT bootstrap, which offline never finishes,
##      and only then starts the game)
##   5. pressed again at home: just closes the menu
##   6. the room list shows the home room as "Home", with no Purge button
##   7. home.get fails (HOME_ROOM_MISSING): disabled, the error in the tooltip
##
## The menu is taken out of game.tscn on its own (Game's _ready never runs) and
## KeetService.room_mgr is a recording stub. Run with no daemon:
## HYPERTPS_DAEMON_TCP=127.0.0.1:1 makes KeetService's client fail harmlessly.

const HOME := "gatehomeroom00000000000000000000000000000000000000000"
const OTHER := "gateotherroom0000000000000000000000000000000000000000"

var ok := true
var _ran := false
var _finished := false


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()
	# A SCRIPT ERROR aborts _run() without touching `ok`; only a run that reached
	# its last line can pass.
	_p(_finished, "the gate ran to the end (no script error aborted it)")
	print("GATE home_button: %s" % ("PASS" if ok else "FAIL"))
	quit(0 if ok else 1)
	return true


func _menu() -> Node:
	var game_scene := load("res://scenes/game/game.tscn") as PackedScene
	var game: Node = game_scene.instantiate()
	var menu: Node = game.get_node("MenuUI/MenuMain")
	menu.get_parent().remove_child(menu)
	game.free()
	root.add_child(menu)
	# Graphical behaviour under --headless: the gate is what says Home is wanted.
	menu.home_enabled = true
	menu._update_home_button()
	# Never let the gate touch the network: a non-home room's _start_mesh returns
	# early without HyperswarmMultiplayerPeer.
	menu._has_hyperswarm = false
	return menu


func _run() -> void:
	var keet: Node = root.get_node_or_null("KeetService")
	_p(keet != null, "KeetService autoload present")
	if keet == null:
		return
	var stub := preload("res://scenes/test/_stub_keet_room_home.gd").new()
	var real_mgr: Node = keet.room_mgr
	keet.room_mgr = stub
	# A failed assignment is a SCRIPT ERROR that aborts _run() with `ok` still
	# true — the first run of this gate printed PASS that way. Check it took.
	_p(keet.room_mgr == stub, "the stub room manager is installed")
	if keet.room_mgr != stub:
		return
	stub.home_result = {"roomId": HOME, "created": false}

	var menu := _menu()
	var btn: Button = menu.get_node_or_null("MainPanel/HomeButton")
	_p(btn != null, "the main panel has a HomeButton (Resume is gone: %s)"
			% str(menu.get_node_or_null("MainPanel/ResumeButton") == null))
	if btn == null:
		_cleanup(menu, keet, real_mgr, stub)
		return

	# 1. nothing known yet
	_p(btn.disabled and btn.text.contains("waiting for keet-daemon"),
			"disabled + waiting before the daemon answers (text=%s)" % btn.text)

	# 2. daemon not running
	menu._on_daemon_status("disconnected")
	_p(btn.disabled and btn.text.contains("not running"), "daemon down: disabled + says so (text=%s)" % btn.text)
	_p(stub.home_calls == 0, "no home.get while the daemon is down")

	# 3. daemon up, identity loaded -> one home.get, enabled
	menu._on_daemon_status("running")
	menu._on_identity_loaded({"displayName": "gate", "memberId": "gatemember"})
	_p(stub.home_calls == 1, "identity loaded -> home.get asked once (calls=%d)" % stub.home_calls)
	_p(not btn.disabled and btn.text == "Home", "enabled as \"Home\" (text=%s)" % btn.text)
	_p(keet.home_room_id == HOME, "KeetService.home_room_id = the home room")

	# 4. pressed -> home room, offline peer, no mesh, game started
	menu._on_home_pressed()
	_p(keet.get_current_room() == HOME, "pressing Home selects the home room (room=%s)" % keet.get_current_room())
	_p(menu.multiplayer.multiplayer_peer is OfflineMultiplayerPeer,
			"the home room runs on the offline peer (peer=%s)" % str(menu.multiplayer.multiplayer_peer))
	_p(not menu._mesh_active and menu._peer == null, "no mesh / DHT for the home room")
	_p(menu._game_started, "the game is started")
	_p(not menu.visible, "the menu closes")
	# The mesh overlay says it is home, and keeps the id clients compare.
	var gs: Node = keet.game_state
	var was_visible: bool = gs._diag_overlay.visible
	gs._diag_overlay.visible = true
	gs._process(0.0)
	var hud: String = gs._mesh_label.text
	gs._diag_overlay.visible = was_visible
	_p(hud.contains("room=home(%s)" % HOME.substr(0, 12)), "HUD reads room=home(<id>) (hud=%s)" % hud)

	# 5. pressed again at home: only closes the menu
	menu.show_menu()
	menu._on_home_pressed()
	_p(keet.get_current_room() == HOME and not menu.visible, "pressed again at home: menu closes, room unchanged")

	# 6. the room list
	keet._room_cache[HOME] = "Home"
	keet._room_cache[OTHER] = "Other Room"
	menu._refresh_room_list()
	var home_row := _row_for(menu, "Home")
	var other_row := _row_for(menu, "Other Room")
	_p(home_row != null, "the home room is listed as \"Home\"")
	_p(home_row != null and not _has_button(home_row, "Purge"), "the home room has no Purge button")
	_p(other_row != null and _has_button(other_row, "Purge"), "other rooms keep Purge")

	# 6b. leaving home for another room ends the home session
	keet.select_room(OTHER)
	_p(keet.get_current_room() == OTHER, "another room can be selected from home")
	_p(not menu._game_started, "leaving home ends the home session (game_started=%s)" % str(menu._game_started))
	_p(not (menu.multiplayer.multiplayer_peer is OfflineMultiplayerPeer),
			"the offline peer is gone (peer=%s)" % str(menu.multiplayer.multiplayer_peer))
	# ...and Home takes you back
	menu._on_home_pressed()
	_p(keet.get_current_room() == HOME and menu._game_started
			and menu.multiplayer.multiplayer_peer is OfflineMultiplayerPeer, "Home from another room returns home, offline")

	# 7. home.get fails
	menu.queue_free()
	keet.home_room_id = ""
	stub.home_result = {"error": "HOME_ROOM_MISSING: identity-0 homeRoomId abc is not a room on this identity"}
	var menu2 := _menu()
	var btn2: Button = menu2.get_node("MainPanel/HomeButton")
	menu2._on_daemon_status("running")
	menu2._on_identity_loaded({"displayName": "gate", "memberId": "gatemember"})
	_p(btn2.disabled, "home.get error: disabled (text=%s)" % btn2.text)
	_p(btn2.tooltip_text.contains("HOME_ROOM_MISSING"), "the error is in the tooltip (tooltip=%s)" % btn2.tooltip_text)
	menu2._on_home_pressed()
	_p(keet.get_current_room() == HOME, "pressing a disabled Home changes nothing")
	_cleanup(menu2, keet, real_mgr, stub)
	_finished = true


func _row_for(menu: Node, title: String) -> Node:
	for row in menu.room_list.get_children():
		if row.is_queued_for_deletion():
			continue
		for c in row.get_children():
			if c is Button and (c.text == title or c.text == "> " + title):
				return row
	return null


func _has_button(row: Node, text: String) -> bool:
	for c in row.get_children():
		if c is Button and c.text == text:
			return true
	return false


func _cleanup(menu: Node, keet: Node, real_mgr: Node, stub: Node) -> void:
	keet.room_mgr = real_mgr
	keet._room_cache.erase(HOME)
	keet._room_cache.erase(OTHER)
	keet.home_room_id = ""
	menu.queue_free()
	stub.free()