OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_asset_thumb.gd

126 lines · gd ref:
extends Node
## GATE — ASSET THUMBNAILS (#120 L4 step 6, docs/node-tree-and-stacks.md §15.6). WINDOWED:
## it needs a real renderer (the headless dummy draws nothing). A collection "Kit" (a box
## with a mirror + a stair) saved; its thumbnail renders over the next frames.
##   T1  a save yields a thumbnail (thumbnail_ready)
##   T2  a 128 px PNG on disk, named by the CONTENT OVERLAY's hash of it
##   T3  it is a RENDER of the thing: many colours, and the middle is not background
##   T4  the room's catalogue entry and the personal index carry it
##   T5  the browser's tile shows it
##   T6  a late thumbnail never clobbers a NEWER save's entry (only its own oid's)
##
## RUN WINDOWED: CI=true DISPLAY=:0 HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --path . res://scenes/test/gate_asset_thumb.tscn

const AssetLibrary := preload("res://scenes/maps/asset_library.gd")

var fails := 0
var checks := 0
var map: Node3D
var persist: Node
var lib: Node
var _thumb := ""


func _ok(what: String, cond: bool, note: Variant = "") -> void:
	checks += 1
	if not cond:
		fails += 1
	print("  %-64s %s  %s" % [what, "OK" if cond else "**FAIL**", str(note)])


func _ready() -> void:
	var gs: Node = get_tree().root.get_node_or_null("/root/GameState")
	gs.route("world:level:*", 1, "peer")
	var keet: Node = get_tree().root.get_node_or_null("/root/KeetService")
	if keet != null:
		keet.current_member_id = "probe"
	get_tree().root.get_node("/root/GameMode").set_mode("build")
	var pd := AssetLibrary.personal_dir()
	if pd.ends_with("_ci"):
		for f: String in DirAccess.get_files_at(pd):
			DirAccess.remove_absolute(ProjectSettings.globalize_path(pd.path_join(f)))
	map = (load("res://scenes/maps/map_dev_test.tscn") as PackedScene).instantiate()
	add_child(map)
	for i in 6:
		await get_tree().process_frame
	persist = map.get("_persist")
	lib = map.asset_library()
	await _run()
	if checks < 8:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE asset_thumb: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _on_thumb(_n: String, t: String) -> void:
	_thumb = t


func _run() -> void:
	var O0 := Vector3(950.137, 0.31, 949.873)
	persist.begin_gesture("room")
	var a: String = persist.record("shape.create", {"type": "box", "gv": 1, "params": {},
		"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, O0)), "size": [2, 2, 2], "material_id": 2, "level_id": 0})
	persist.record("mod.create", {"target": a, "mod": "mirror", "params": {"axis": "x", "offset": 0.0}})
	var st: String = persist.record("shape.create", {"type": "stairs", "gv": 1, "params": {},
		"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, O0 + Vector3(0, 0, 4))), "size": [2, 2, 3],
		"material_id": 2, "level_id": 0})
	var kit: String = persist.record("col.create", {"name": "Kit", "parent": ""})
	persist.record("col.link", {"target": kit, "members": ["g:" + a, "g:" + st]})
	persist.end_gesture()
	persist.refold()
	lib.thumbnail_ready.connect(_on_thumb)
	var r: Dictionary = lib.save_collection(kit, "Kit")
	var oid := String(r.get("oid", ""))
	for i in 120:
		if _thumb != "":
			break
		await get_tree().process_frame

	# ---- T1 / T2 ----
	_ok("T1 a save yields a thumbnail", _thumb.length() == 64, _thumb)
	var path: String = AssetLibrary.thumb_path(_thumb)
	var img := Image.new()
	var loaded := FileAccess.file_exists(path) and img.load_png_from_buffer(FileAccess.get_file_as_bytes(path)) == OK
	_ok("T2 a 128 px PNG on disk", loaded and img.get_width() == 128 and img.get_height() == 128,
		[img.get_width(), img.get_height()] if loaded else path)
	var co: Node = get_tree().root.get_node("ContentOverlay")
	_ok("T2 ... named by the overlay's own hash of it", loaded and String(co.content_hash_of_file(path)) == _thumb)

	# ---- T3 ----
	var colours := {}
	var bg := Color(0.16, 0.17, 0.19)
	var not_bg := 0
	if loaded:
		img.convert(Image.FORMAT_RGBA8)
		for y in range(0, 128, 2):
			for x in range(0, 128, 2):
				var c := img.get_pixel(x, y)
				colours[c.to_html(false)] = true
		for y2 in range(40, 88):
			for x2 in range(40, 88):
				var c2 := img.get_pixel(x2, y2)
				if absf(c2.r - bg.r) + absf(c2.g - bg.g) + absf(c2.b - bg.b) > 0.08:
					not_bg += 1
	_ok("T3 it is a render: many colours", colours.size() > 20, colours.size())
	_ok("T3 ... and the middle is the thing, not background", not_bg > 48 * 48 / 4, not_bg)

	# ---- T4 ----
	var sync: Node = get_tree().root.get_node("AssetSync")
	var e: Variant = sync.state.get_state("world:assets:asset:Kit", null)
	_ok("T4 the room's catalogue entry carries it", e is Dictionary and String((e as Dictionary).get("thumb", "")) == _thumb, e)
	_ok("T4 the personal index carries it", AssetLibrary.personal_index().any(func(x: Dictionary) -> bool:
		return String(x["oid"]) == oid and String(x["thumb"]) == _thumb))

	# ---- T5 ----
	var ab: Node = map.get("_asset_browser")
	ab.refresh()
	_ok("T5 the browser's tile shows it", ab.tile("Kit") != null and ab.tile("Kit").icon != null)

	# ---- T6 ----
	var before: Variant = sync.state.get_state("world:assets:asset:Kit", null)
	await lib._thumb_after_save("Kit", "0".repeat(64), lib.collection_brushes(kit), true, false)
	_ok("T6 a thumbnail for another oid leaves the entry alone",
		str(sync.state.get_state("world:assets:asset:Kit", null)) == str(before))