OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_terrain_echo_no_refold.gd

106 lines · gd ref:
extends SceneTree
## Gate: a re-delivery of an edit this peer already applied does not re-fold.
##
## hyper-games #88 Tier 1 #1 (terrain half). TerrainEditManager._on_state_changed
## ran the out-of-order check BEFORE asking whether the edit was already applied:
##
##     if seq < _max_applied_seq: _request_rebuild()     # clear_edits + full replay
##
## Carve twice quickly -- edit A (seq n), edit B (seq n+1), both applied locally --
## and A's own write confirmation (set_value's callback -> state_updated ->
## state_changed) then arrives with seq n < n+1. That threw away the whole journal
## and replayed it, for an edit already in the field. Carving fast is exactly when
## that happens, once per carve.
##
## A genuinely NEW edit that sorts below the high-water mark must still re-fold --
## min/max composition does not commute (hyper-games #42 P1). Both are asserted.

var ok := true
var _started := false
var _done := false


class FakeTerrain extends Node:
	var clears: int = 0
	var edits: int = 0

	func clear_edits() -> void:
		clears += 1

	func edit_terrain(_p: Vector3, _r: float, _s: float, _op: int, _i: bool) -> void:
		edits += 1


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 not _started:
		_started = true
		_run()
	if _done:
		print("GATE terrain_echo_no_refold: %s" % ("PASS" if ok else "FAIL"))
		quit(0 if ok else 1)
	return false


func _frames(n: int) -> void:
	for i in n:
		await process_frame


func _run() -> void:
	var gs: Node = root.get_node_or_null("GameState")
	_p(gs != null, "GameState autoload present")
	if gs == null:
		_done = true
		return
	gs.install_routes()
	gs.current_map_id = "gate_echo_map"

	var fake := FakeTerrain.new()
	root.add_child(fake)
	# load(), not the class name: naming TerrainEditManager compiles it at parse
	# time, before GameState is an identifier (see level_persistence.max_seq_in).
	var tem_script: GDScript = load("res://scenes/game/terrain_edit_manager.gd")
	var tem: Node = tem_script.new()
	root.add_child(tem)
	tem._terrain_node = fake          # _find_terrain returns a valid cached node
	# The 1 s retry nulls _terrain_node to re-search by class, and a fake is not a
	# DCTerrain -- it would find nothing and every apply would silently no-op.
	tem._retry_timer.stop()
	await _frames(2)                  # let _ready's deferred load run and settle
	fake.clears = 0

	# Two local carves, as the player makes them.
	tem.apply_sphere(Vector3(1, 2, 3), 1.5, -1.0, 1)
	tem.apply_sphere(Vector3(4, 5, 6), 1.5, -1.0, 1)
	var keys: Array[String] = tem_script.sort_edit_keys(
			gs.keys_with_prefix("world:terrain:edit:"))
	_p(keys.size() == 2, "two edits are in the mirror (got %d)" % keys.size())
	if keys.size() != 2:
		_done = true
		return
	_p(fake.edits == 2, "both were applied to the terrain (got %d)" % fake.edits)

	# The FIRST carve's own write confirmation arrives after the second applied.
	var first: String = keys[0]
	var stored: String = "map:%s:%s" % [gs.current_map_id, first]
	gs._on_durable_updated("gate-room", stored, gs.get_state(first))
	await _frames(3)                  # _request_rebuild is deferred
	_p(fake.clears == 0,
		"the echo of an applied edit does NOT re-fold (clear_edits called %d times)" % fake.clears)
	_p(fake.edits == 2, "and does not re-apply it (edits %d)" % fake.edits)

	# A NEW edit from another peer that sorts BELOW what is applied must re-fold.
	var lower := "world:terrain:edit:zed:1"
	gs._on_durable_updated("gate-room", "map:%s:%s" % [gs.current_map_id, lower],
			{"type": "sphere", "pos": [0, 0, 0], "radius": 1.0, "str": -1.0, "op": 1})
	await _frames(3)
	_p(fake.clears == 1,
		"a new out-of-order edit still re-folds (clear_edits called %d times)" % fake.clears)
	_done = true