fix(client): use tick-based invalidation for world renderer
Count-based dirty tracking missed content changes when tile/visibility count stayed the same (e.g., door opening, player moving through corridor). Now re-renders all layers when GameState.current_tick advances, which correctly handles every snapshot change. Adds 3 tests for tick advancement and same-count-different-content scenarios. 68 total tests, 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,30 +8,29 @@ extends Node2D
|
||||
@onready var fog_renderer = $FogOverlay
|
||||
@onready var entity_renderer = $Entities
|
||||
|
||||
var _last_tile_count: int = 0
|
||||
var _last_visible_count: int = 0
|
||||
var _last_tick: int = -1
|
||||
|
||||
func _ready() -> void:
|
||||
print("WorldRenderer: Initialized")
|
||||
|
||||
# Called each frame to update visuals from game state
|
||||
# Called each frame to update visuals from game state.
|
||||
# Uses tick-based invalidation — re-renders all layers when a new snapshot arrives.
|
||||
func update_from_state() -> void:
|
||||
var tile_count := GameState.visible_tiles.size()
|
||||
var visible_count := GameState.visible_positions.size()
|
||||
var tick := GameState.current_tick
|
||||
if tick == _last_tick:
|
||||
return
|
||||
_last_tick = tick
|
||||
|
||||
# Update tiles when tile data changes (new chunks loaded, D-012)
|
||||
# Update tiles (may include new chunks or modified tiles)
|
||||
if tile_renderer and tile_renderer.has_method("update_tiles"):
|
||||
if tile_count > 0 and tile_count != _last_tile_count:
|
||||
if GameState.visible_tiles.size() > 0:
|
||||
tile_renderer.update_tiles(GameState.visible_tiles)
|
||||
if fog_renderer and fog_renderer.has_method("register_tile_positions"):
|
||||
fog_renderer.register_tile_positions(GameState.visible_tiles)
|
||||
_last_tile_count = tile_count
|
||||
|
||||
# Update fog overlay when visibility changes
|
||||
# Update fog overlay from visibility data
|
||||
if fog_renderer and fog_renderer.has_method("update_fog"):
|
||||
if visible_count != _last_visible_count:
|
||||
fog_renderer.update_fog(GameState.visible_positions, GameState.player_position)
|
||||
_last_visible_count = visible_count
|
||||
fog_renderer.update_fog(GameState.visible_positions, GameState.player_position)
|
||||
|
||||
# Update entity sprites
|
||||
if entity_renderer and entity_renderer.has_method("update_entities"):
|
||||
|
||||
@@ -31,6 +31,12 @@ func test_tile_size_constant() -> void:
|
||||
|
||||
# -- GameState: tile and visibility data --
|
||||
|
||||
func test_game_state_tick_advances() -> void:
|
||||
GameState.apply_snapshot({"tick": 1, "tiles": _test_tiles})
|
||||
assert_that(GameState.current_tick).is_equal(1)
|
||||
GameState.apply_snapshot({"tick": 2, "tiles": _test_tiles})
|
||||
assert_that(GameState.current_tick).is_equal(2)
|
||||
|
||||
func test_game_state_stores_tiles() -> void:
|
||||
GameState.apply_snapshot({"tick": 1, "tiles": _test_tiles})
|
||||
assert_that(GameState.visible_tiles.size()).is_equal(5)
|
||||
@@ -49,6 +55,23 @@ func test_game_state_clears_old_visible_positions() -> void:
|
||||
assert_that(GameState.visible_positions.size()).is_equal(2)
|
||||
assert_that(GameState.visible_positions.has(Vector2i(1, 1))).is_false()
|
||||
|
||||
func test_game_state_same_count_different_tiles_new_tick() -> void:
|
||||
var tiles_a := [{"x": 0, "y": 0, "z": 0, "type": "floor"}]
|
||||
var tiles_b := [{"x": 0, "y": 0, "z": 0, "type": "door"}]
|
||||
GameState.apply_snapshot({"tick": 1, "tiles": tiles_a})
|
||||
assert_that(GameState.visible_tiles[0].type).is_equal("floor")
|
||||
GameState.apply_snapshot({"tick": 2, "tiles": tiles_b})
|
||||
assert_that(GameState.current_tick).is_equal(2)
|
||||
assert_that(GameState.visible_tiles[0].type).is_equal("door")
|
||||
|
||||
func test_game_state_same_count_different_visibility_new_tick() -> void:
|
||||
GameState.apply_snapshot({"tick": 1, "visible_positions": [{"x": 1, "y": 1}]})
|
||||
assert_that(GameState.visible_positions.has(Vector2i(1, 1))).is_true()
|
||||
GameState.apply_snapshot({"tick": 2, "visible_positions": [{"x": 2, "y": 2}]})
|
||||
assert_that(GameState.current_tick).is_equal(2)
|
||||
assert_that(GameState.visible_positions.has(Vector2i(1, 1))).is_false()
|
||||
assert_that(GameState.visible_positions.has(Vector2i(2, 2))).is_true()
|
||||
|
||||
func test_game_state_warns_on_missing_player() -> void:
|
||||
GameState.player_entity_id = 999
|
||||
GameState.player_position = Vector2(5, 5)
|
||||
|
||||
Reference in New Issue
Block a user