feat: synchronize authoritative npc position
- NpcVisual emits position_changed signal during _physics_process - SimulationManager.synchronize_npc_position() writes active position into SimNPC state on movement, arrival, and navigation failure - WorldViewManager forwards position_changed and syncs before despawn/reload - SimNPC stores travel_target_position and has_travel_target as versioned state; NPCStateRecord v2 persists them with v1 migration - WorldViewManager.spawn_npc_visual() resumes travel for traveling NPCs via SimulationManager.request_current_travel() - WorldViewManager.despawn_npc_visual() syncs position before removal - WorldViewManager.reload_npc_visual() spawns a visual from persisted state without rerolling target selection - tests/npc_visual_lifecycle_test.gd proves unload/reload preserves action, target, reservation, position, RNG state, and checksum - simulation_state_serialization_test adds legacy v1 NPC migration test that does not invent an active travel target
This commit is contained in:
@@ -8,6 +8,7 @@ func _initialize() -> void:
|
||||
func _run() -> void:
|
||||
await _test_deterministic_continuation()
|
||||
await _test_resource_state_round_trip()
|
||||
_test_legacy_npc_migration()
|
||||
_test_legacy_resource_migration()
|
||||
_test_schema_rejection()
|
||||
|
||||
@@ -160,6 +161,27 @@ func _test_legacy_resource_migration() -> void:
|
||||
"Resource migration should preserve mutable authority"
|
||||
)
|
||||
|
||||
func _test_legacy_npc_migration() -> void:
|
||||
var npc := SimNPC.new(
|
||||
88,
|
||||
"LegacyNPC",
|
||||
SimulationIds.PROFESSION_FARMER,
|
||||
5.0,
|
||||
5.0
|
||||
)
|
||||
var legacy_data := NPCStateRecord.capture(npc).to_dictionary()
|
||||
legacy_data["schema_version"] = NPCStateRecord.LEGACY_SCHEMA_VERSION
|
||||
legacy_data.erase("travel_target_position")
|
||||
legacy_data.erase("has_travel_target")
|
||||
var migrated := NPCStateRecord.from_dictionary(legacy_data)
|
||||
_check(migrated != null, "NPCStateRecord v1 should migrate explicitly")
|
||||
if migrated == null:
|
||||
return
|
||||
_check(
|
||||
not bool(migrated.data["has_travel_target"]),
|
||||
"Legacy NPC migration should not invent an active travel target"
|
||||
)
|
||||
|
||||
func _create_manager(seed_value: int) -> Node:
|
||||
var manager: Node = load("res://simulation/SimulationManager.gd").new()
|
||||
manager.simulation_seed = seed_value
|
||||
|
||||
Reference in New Issue
Block a user