feat: integrate regional caravan persistence

This commit is contained in:
Rijad Zuzo
2026-08-23 17:20:41 +02:00
parent 49c5bda651
commit 52307f1306
24 changed files with 1939 additions and 92 deletions
@@ -0,0 +1,287 @@
extends GutTest
const CARGO_AMOUNT := 3.0
func test_jajce_route_bootstrap_has_stable_required_ids_and_independent_stock() -> void:
var facade := RegionalSimulationFacade.create_jajce_route(4242, 7)
assert_not_null(facade)
assert_eq(facade.get_world_seed(), 4242)
assert_eq(facade.get_last_processed_tick(), 7)
assert_eq(facade.get_active_location_id(), SimulationIds.REGIONAL_LOCATION_JAJCE)
assert_eq(
facade.to_dictionary().keys(),
[
"schema_version",
"world_seed",
"last_processed_tick",
"active_location_id",
"caravan_service",
]
)
var service := facade.get_service()
var state := service.get_world_state()
assert_eq(state.get_world_id(), SimulationIds.REGIONAL_WORLD_BOSNIA)
assert_not_null(state.get_location(RegionalSimulationFacade.LOCATION_JAJCE))
assert_not_null(state.get_location(RegionalSimulationFacade.LOCATION_TRAVNIK))
assert_not_null(state.get_settlement(RegionalSimulationFacade.SETTLEMENT_JAJCE))
assert_not_null(state.get_settlement(RegionalSimulationFacade.SETTLEMENT_TRAVNIK))
assert_not_null(state.get_route(RegionalSimulationFacade.ROUTE_JAJCE_TRAVNIK))
assert_not_null(state.get_person(RegionalSimulationFacade.PERSON_ANA))
assert_not_null(state.get_population_cohort(RegionalSimulationFacade.COHORT_CARRIERS))
assert_not_null(state.get_polity(RegionalSimulationFacade.POLITY_JAJCE))
assert_not_null(state.get_polity(RegionalSimulationFacade.POLITY_TRAVNIK))
assert_not_null(state.get_diplomatic_relation(RegionalSimulationFacade.RELATION_JAJCE_TRAVNIK))
var route := state.get_route(RegionalSimulationFacade.ROUTE_JAJCE_TRAVNIK)
assert_true(
route.connects(
RegionalSimulationFacade.LOCATION_JAJCE, RegionalSimulationFacade.LOCATION_TRAVNIK
)
)
assert_eq(route.get_travel_ticks(), RegionalSimulationFacade.ROUTE_TRAVEL_TICKS)
var caravan := state.get_mobile_group(RegionalSimulationFacade.CARAVAN_BLUE)
assert_true(caravan.is_at_location())
assert_eq(caravan.get_current_location_id(), RegionalSimulationFacade.LOCATION_TRAVNIK)
assert_eq(caravan.get_headcount(), 4)
assert_eq(caravan.get_cargo_ledger(), {})
assert_eq(
state.get_settlement(RegionalSimulationFacade.SETTLEMENT_JAJCE).get_stockpile_ledger(), {}
)
assert_eq(
state.get_settlement(RegionalSimulationFacade.SETTLEMENT_TRAVNIK).get_stockpile_amount(
SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS
),
RegionalSimulationFacade.TRAVNIK_INITIAL_TRADE_GOODS
)
assert_eq(
service.get_scheduler().get_cursor()["last_drain_tick"], facade.get_last_processed_tick()
)
_assert_regional_stock_is_independent(state)
func test_departure_and_arrival_use_facade_tick_and_conserve_regional_authority() -> void:
var facade := RegionalSimulationFacade.create_jajce_route(4242, 10)
var state := facade.get_service().get_world_state()
var goods_before := _regional_trade_goods(state)
var headcount_before := _regional_headcount(state)
assert_true(_depart(facade, CARGO_AMOUNT, &"loaded"))
var caravan := state.get_mobile_group(RegionalSimulationFacade.CARAVAN_BLUE)
assert_true(caravan.is_on_route())
assert_eq(caravan.get_departure_tick(), 10)
assert_eq(caravan.get_arrival_tick(), 10 + RegionalSimulationFacade.ROUTE_TRAVEL_TICKS)
assert_eq(caravan.get_cargo_amount(SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS), CARGO_AMOUNT)
assert_eq(_regional_trade_goods(state), goods_before)
assert_eq(_regional_headcount(state), headcount_before)
assert_true(facade.advance_to_tick(caravan.get_arrival_tick() - 1, &"unloaded"))
assert_true(caravan.is_on_route())
assert_true(facade.advance_to_tick(caravan.get_arrival_tick(), &"always"))
state = facade.get_service().get_world_state()
caravan = state.get_mobile_group(RegionalSimulationFacade.CARAVAN_BLUE)
assert_true(caravan.is_at_location())
assert_eq(caravan.get_current_location_id(), RegionalSimulationFacade.LOCATION_JAJCE)
assert_eq(caravan.get_cargo_amount(SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS), 0.0)
assert_eq(
state.get_settlement(RegionalSimulationFacade.SETTLEMENT_JAJCE).get_stockpile_amount(
SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS
),
RegionalSimulationFacade.JAJCE_INITIAL_TRADE_GOODS + CARGO_AMOUNT
)
assert_eq(_regional_trade_goods(state), goods_before)
assert_eq(_regional_headcount(state), headcount_before)
assert_eq(facade.get_service().get_event_store().size(), 4)
_assert_regional_stock_is_independent(state)
func test_ticks_are_monotonic_and_equal_tick_is_idempotent() -> void:
var facade := RegionalSimulationFacade.create_jajce_route()
assert_true(facade.advance_to_tick(4))
var checksum_at_four := facade.checksum()
assert_true(facade.advance_to_tick(4))
assert_eq(facade.checksum(), checksum_at_four)
assert_false(facade.advance_to_tick(3))
assert_true("cannot move backward" in facade.get_last_error())
assert_eq(facade.get_last_processed_tick(), 4)
assert_eq(facade.checksum(), checksum_at_four)
func test_advance_rolls_back_all_ticks_when_a_scheduled_handler_fails() -> void:
var facade := RegionalSimulationFacade.create_jajce_route()
assert_not_null(
facade.get_service().get_scheduler().schedule_new(
&"unsupported_job", &"unsupported_job", 2, 0, RegionalSimulationFacade.CARAVAN_BLUE
)
)
var before := facade.to_dictionary()
assert_false(facade.advance_to_tick(2))
assert_true("no registered handler" in facade.get_last_error())
assert_eq(facade.to_dictionary(), before)
assert_eq(facade.get_last_processed_tick(), 0)
func test_save_restore_mid_route_continues_exactly() -> void:
var uninterrupted := RegionalSimulationFacade.create_jajce_route(4242, 3)
assert_true(_depart(uninterrupted, 4.0))
assert_true(uninterrupted.advance_to_tick(8))
var saved := uninterrupted.to_dictionary()
var restored := RegionalSimulationFacade.from_dictionary(saved)
assert_not_null(restored)
assert_eq(restored.to_dictionary(), saved)
assert_eq(restored.checksum(), uninterrupted.checksum())
assert_eq(restored.get_last_processed_tick(), 8)
var arrival_tick := 3 + RegionalSimulationFacade.ROUTE_TRAVEL_TICKS
assert_true(uninterrupted.advance_to_tick(arrival_tick, &"always"))
assert_true(restored.advance_to_tick(arrival_tick, &"never"))
assert_eq(restored.to_dictionary(), uninterrupted.to_dictionary())
assert_eq(restored.checksum(), uninterrupted.checksum())
func test_presentation_modes_are_inert_observers() -> void:
var always := _complete_route(&"always", &"always")
var never := _complete_route(&"never", &"never")
var loaded_unloaded := _complete_route(&"loaded", &"unloaded")
assert_eq(always, never)
assert_eq(always, loaded_unloaded)
func test_departure_rejects_local_or_mixed_resource_ledgers_without_mutation() -> void:
var facade := RegionalSimulationFacade.create_jajce_route()
var before := facade.to_dictionary()
assert_false(
facade.depart_caravan(
RegionalSimulationFacade.CARAVAN_BLUE,
RegionalSimulationFacade.SETTLEMENT_JAJCE,
RegionalSimulationFacade.SETTLEMENT_TRAVNIK,
RegionalSimulationFacade.ROUTE_JAJCE_TRAVNIK,
{SimulationIds.RESOURCE_FOOD: 1.0}
)
)
assert_true("only the trade-goods" in facade.get_last_error())
assert_eq(facade.to_dictionary(), before)
assert_false(
(
facade
. depart_caravan(
RegionalSimulationFacade.CARAVAN_BLUE,
RegionalSimulationFacade.SETTLEMENT_JAJCE,
RegionalSimulationFacade.SETTLEMENT_TRAVNIK,
RegionalSimulationFacade.ROUTE_JAJCE_TRAVNIK,
{
SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS: 1.0,
SimulationIds.RESOURCE_WOOD: 1.0,
}
)
)
)
assert_eq(facade.to_dictionary(), before)
func test_restore_rejects_malformed_or_wrong_scope_envelopes() -> void:
var facade := RegionalSimulationFacade.create_jajce_route(4242, 5)
var saved := facade.to_dictionary()
assert_null(RegionalSimulationFacade.from_dictionary({}))
var missing_field := saved.duplicate(true)
missing_field.erase("caravan_service")
assert_null(RegionalSimulationFacade.from_dictionary(missing_field))
var extra_field := saved.duplicate(true)
extra_field["unexpected"] = true
assert_null(RegionalSimulationFacade.from_dictionary(extra_field))
var wrong_schema := saved.duplicate(true)
wrong_schema["schema_version"] = RegionalSimulationFacade.SCHEMA_VERSION + 1
assert_null(RegionalSimulationFacade.from_dictionary(wrong_schema))
var coerced_schema := saved.duplicate(true)
coerced_schema["schema_version"] = str(RegionalSimulationFacade.SCHEMA_VERSION)
assert_null(RegionalSimulationFacade.from_dictionary(coerced_schema))
var coerced_seed := saved.duplicate(true)
coerced_seed["world_seed"] = "4242"
assert_null(RegionalSimulationFacade.from_dictionary(coerced_seed))
var negative_tick := saved.duplicate(true)
negative_tick["last_processed_tick"] = -1
assert_null(RegionalSimulationFacade.from_dictionary(negative_tick))
var coerced_tick := saved.duplicate(true)
coerced_tick["last_processed_tick"] = "5"
assert_null(RegionalSimulationFacade.from_dictionary(coerced_tick))
var cursor_mismatch := saved.duplicate(true)
cursor_mismatch["last_processed_tick"] = 6
assert_null(RegionalSimulationFacade.from_dictionary(cursor_mismatch))
var wrong_active_location := saved.duplicate(true)
wrong_active_location["active_location_id"] = String(RegionalSimulationFacade.LOCATION_TRAVNIK)
assert_null(RegionalSimulationFacade.from_dictionary(wrong_active_location))
var malformed_service := saved.duplicate(true)
malformed_service["caravan_service"] = {}
assert_null(RegionalSimulationFacade.from_dictionary(malformed_service))
var wrong_world := saved.duplicate(true)
wrong_world["caravan_service"]["world_state"]["world_id"] = "another_world"
for location: Dictionary in wrong_world["caravan_service"]["world_state"]["locations"]:
location["world_id"] = "another_world"
location["address"]["world_id"] = "another_world"
for group: Dictionary in wrong_world["caravan_service"]["world_state"]["mobile_groups"]:
if group["location_address"] != null:
group["location_address"]["world_id"] = "another_world"
assert_null(RegionalSimulationFacade.from_dictionary(wrong_world))
var unsupported_stock := saved.duplicate(true)
for settlement: Dictionary in unsupported_stock["caravan_service"]["world_state"]["settlements"]:
if settlement["settlement_id"] == String(RegionalSimulationFacade.SETTLEMENT_TRAVNIK):
settlement["stockpile_ledger"][String(SimulationIds.RESOURCE_FOOD)] = 1.0
assert_null(RegionalSimulationFacade.from_dictionary(unsupported_stock))
var forged_total := saved.duplicate(true)
for settlement: Dictionary in forged_total["caravan_service"]["world_state"]["settlements"]:
if settlement["settlement_id"] == String(RegionalSimulationFacade.SETTLEMENT_TRAVNIK):
settlement["stockpile_ledger"][String(RegionalSimulationFacade.TRADE_GOODS)] = 5.0
assert_null(RegionalSimulationFacade.from_dictionary(forged_total))
func _depart(
facade: RegionalSimulationFacade, amount: float, presentation_mode: StringName = &"never"
) -> bool:
return facade.depart_jajce_delivery(amount, presentation_mode)
func _complete_route(
departure_presentation: StringName, arrival_presentation: StringName
) -> Dictionary:
var facade := RegionalSimulationFacade.create_jajce_route()
assert_true(_depart(facade, CARGO_AMOUNT, departure_presentation))
assert_true(
facade.advance_to_tick(RegionalSimulationFacade.ROUTE_TRAVEL_TICKS, arrival_presentation)
)
return facade.to_dictionary()
func _regional_trade_goods(state: RegionalWorldState) -> float:
var total := 0.0
for settlement: SettlementStateRecord in state.get_all_settlements():
total += settlement.get_stockpile_amount(SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS)
for group: MobileGroupStateRecord in state.get_all_mobile_groups():
total += group.get_cargo_amount(SimulationIds.RESOURCE_REGIONAL_TRADE_GOODS)
return total
func _regional_headcount(state: RegionalWorldState) -> int:
var total := 0
for group: MobileGroupStateRecord in state.get_all_mobile_groups():
total += group.get_headcount()
for cohort: PopulationCohortRecord in state.get_all_population_cohorts():
if cohort.is_at_location():
total += cohort.get_count()
for person: PersonStateRecord in state.get_all_persons():
if person.is_at_location():
total += 1
return total
func _assert_regional_stock_is_independent(state: RegionalWorldState) -> void:
for settlement: SettlementStateRecord in state.get_all_settlements():
assert_false(settlement.get_stockpile_ledger().has(String(SimulationIds.RESOURCE_FOOD)))
assert_false(settlement.get_stockpile_ledger().has(String(SimulationIds.RESOURCE_WOOD)))
for group: MobileGroupStateRecord in state.get_all_mobile_groups():
assert_false(group.get_cargo_ledger().has(String(SimulationIds.RESOURCE_FOOD)))
assert_false(group.get_cargo_ledger().has(String(SimulationIds.RESOURCE_WOOD)))
@@ -0,0 +1 @@
uid://0flo0tck1bwn
+280
View File
@@ -0,0 +1,280 @@
extends GutTest
const MANAGER_SCRIPT := preload("res://simulation/SimulationManager.gd")
var manager: Node
func before_each() -> void:
manager = MANAGER_SCRIPT.new()
add_child_autofree(manager)
func test_canonical_manifest_round_trips_defensively() -> void:
var local_state: SimulationStateRecord = manager.create_state_record()
var regional_facade := RegionalSimulationFacade.create_jajce_route()
var manifest := SimulationSaveManifest.create(local_state, regional_facade)
assert_not_null(manifest)
var saved := manifest.to_dictionary()
var expected_fields := [
"local_checksum",
"local_state",
"regional_checksum",
"regional_state",
"schema",
"schema_version",
]
var saved_fields := saved.keys()
saved_fields.sort()
assert_eq(saved_fields, expected_fields)
assert_eq(saved["schema"], SimulationSaveManifest.SCHEMA_NAME)
assert_eq(saved["schema_version"], SimulationSaveManifest.SCHEMA_VERSION)
assert_eq(
saved["local_checksum"],
SimulationSaveManifest._checksum_dictionary(saved["local_state"]),
)
assert_eq(
saved["regional_checksum"],
SimulationSaveManifest._checksum_dictionary(saved["regional_state"]),
)
var restored := SimulationSaveManifest.from_dictionary(saved)
var restored_json := SimulationSaveManifest.from_json(manifest.to_json())
assert_not_null(restored)
assert_not_null(restored_json)
assert_eq(restored.to_dictionary(), saved)
assert_eq(restored.checksum(), manifest.checksum())
assert_eq(restored_json.checksum(), manifest.checksum())
assert_not_same(manifest.get_local_state(), local_state)
assert_not_same(manifest.get_regional_facade(), regional_facade)
local_state.simulation["tick_count"] = 99
var copied_local := manifest.get_local_state()
copied_local.simulation["tick_count"] = 77
assert_eq(int(manifest.get_local_state().simulation["tick_count"]), 0)
var copied_regional := manifest.get_regional_facade().to_dictionary()
copied_regional["last_processed_tick"] = 88
assert_eq(manifest.get_regional_facade().get_last_processed_tick(), 0)
func test_parser_rejects_missing_extra_and_wrong_manifest_fields() -> void:
var valid := _valid_manifest_dictionary()
for field: String in SimulationSaveManifest.MANIFEST_FIELDS:
var missing := valid.duplicate(true)
missing.erase(field)
assert_null(SimulationSaveManifest.from_dictionary(missing), "Missing %s" % field)
var extra := valid.duplicate(true)
extra["unexpected"] = true
assert_null(SimulationSaveManifest.from_dictionary(extra))
var wrong_schema := valid.duplicate(true)
wrong_schema["schema"] = &"the_steward.save_manifest"
assert_null(SimulationSaveManifest.from_dictionary(wrong_schema))
var wrong_version := valid.duplicate(true)
wrong_version["schema_version"] = "1"
assert_null(SimulationSaveManifest.from_dictionary(wrong_version))
var wrong_float_version := valid.duplicate(true)
wrong_float_version["schema_version"] = 1.0
assert_null(SimulationSaveManifest.from_dictionary(wrong_float_version))
var wrong_local := valid.duplicate(true)
wrong_local["local_state"] = []
assert_null(SimulationSaveManifest.from_dictionary(wrong_local))
var wrong_regional := valid.duplicate(true)
wrong_regional["regional_state"] = "regional"
assert_null(SimulationSaveManifest.from_dictionary(wrong_regional))
var wrong_local_checksum := valid.duplicate(true)
wrong_local_checksum["local_checksum"] = 1
assert_null(SimulationSaveManifest.from_dictionary(wrong_local_checksum))
var wrong_regional_checksum := valid.duplicate(true)
wrong_regional_checksum["regional_checksum"] = []
assert_null(SimulationSaveManifest.from_dictionary(wrong_regional_checksum))
assert_null(SimulationSaveManifest.from_json("[]"))
assert_null(SimulationSaveManifest.from_json("not json"))
func test_nested_tampering_and_checksum_mismatches_fail_closed() -> void:
var valid := _valid_manifest_dictionary()
var local_checksum_mismatch := valid.duplicate(true)
local_checksum_mismatch["local_checksum"] = "0".repeat(64)
assert_null(SimulationSaveManifest.from_dictionary(local_checksum_mismatch))
var regional_checksum_mismatch := valid.duplicate(true)
regional_checksum_mismatch["regional_checksum"] = "f".repeat(64)
assert_null(SimulationSaveManifest.from_dictionary(regional_checksum_mismatch))
var tampered_local := valid.duplicate(true)
tampered_local["local_state"]["simulation"]["seed"] = 4004
assert_null(SimulationSaveManifest.from_dictionary(tampered_local))
var tampered_regional := valid.duplicate(true)
tampered_regional["regional_state"]["last_processed_tick"] = 1
assert_null(SimulationSaveManifest.from_dictionary(tampered_regional))
var nested_extra := valid.duplicate(true)
nested_extra["local_state"]["unexpected"] = true
nested_extra["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
nested_extra["local_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(nested_extra))
assert_null(SimulationSaveManifest.from_json(JSON.stringify(nested_extra)))
func test_local_seed_and_tick_require_exact_integer_types() -> void:
var valid := _valid_manifest_dictionary()
var invalid_boundaries: Array[Dictionary] = [
{"field": "seed", "value": "1337"},
{"field": "seed", "value": 1337.5},
{"field": "tick_count", "value": "0"},
{"field": "tick_count", "value": 0.5},
]
for invalid_boundary: Dictionary in invalid_boundaries:
var field := String(invalid_boundary["field"])
var value: Variant = invalid_boundary["value"]
var malformed := valid.duplicate(true)
malformed["local_state"]["simulation"][field] = value
malformed["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
malformed["local_state"]
)
assert_null(
SimulationSaveManifest.from_dictionary(malformed),
"Dictionary parser should reject %s=%s" % [field, value],
)
assert_null(
SimulationSaveManifest.from_json(JSON.stringify(malformed)),
"JSON parser should reject %s=%s" % [field, value],
)
var local_state: SimulationStateRecord = manager.create_state_record()
local_state.simulation[field] = value
assert_null(
SimulationSaveManifest.create(
local_state, RegionalSimulationFacade.create_jajce_route()
),
"Manifest creation should reject %s=%s" % [field, value],
)
func test_json_parser_rejects_nonnumeric_regional_ledger_values() -> void:
var string_ledger := _valid_manifest_dictionary()
_set_regional_stockpile_value(
string_ledger["regional_state"],
RegionalSimulationFacade.SETTLEMENT_TRAVNIK,
"4",
)
assert_null(SimulationSaveManifest.from_json(JSON.stringify(string_ledger)))
var boolean_ledger := _valid_manifest_dictionary()
_set_regional_stockpile_value(
boolean_ledger["regional_state"],
RegionalSimulationFacade.SETTLEMENT_JAJCE,
false,
)
var canonical_regional_state: Dictionary = boolean_ledger["regional_state"].duplicate(true)
_set_regional_stockpile_value(
canonical_regional_state,
RegionalSimulationFacade.SETTLEMENT_JAJCE,
0.0,
)
boolean_ledger["regional_checksum"] = SimulationSaveManifest._checksum_dictionary(
canonical_regional_state
)
assert_null(SimulationSaveManifest.from_json(JSON.stringify(boolean_ledger)))
func test_world_location_and_tick_must_match_the_regional_boundary() -> void:
var valid := _valid_manifest_dictionary()
var wrong_world := valid.duplicate(true)
wrong_world["local_state"]["simulation"]["world_id"] = "regional_elsewhere"
wrong_world["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
wrong_world["local_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(wrong_world))
var wrong_location := valid.duplicate(true)
wrong_location["local_state"]["simulation"]["location_id"] = String(
RegionalSimulationFacade.LOCATION_TRAVNIK
)
wrong_location["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
wrong_location["local_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(wrong_location))
var wrong_seed := valid.duplicate(true)
wrong_seed["local_state"]["simulation"]["seed"] = (
RegionalSimulationFacade.DEFAULT_WORLD_SEED + 1
)
wrong_seed["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
wrong_seed["local_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(wrong_seed))
var wrong_regional_seed := valid.duplicate(true)
wrong_regional_seed["regional_state"]["world_seed"] = (
RegionalSimulationFacade.DEFAULT_WORLD_SEED + 1
)
wrong_regional_seed["regional_checksum"] = SimulationSaveManifest._checksum_dictionary(
wrong_regional_seed["regional_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(wrong_regional_seed))
var wrong_tick := valid.duplicate(true)
wrong_tick["local_state"]["simulation"]["tick_count"] = 1
wrong_tick["local_checksum"] = SimulationSaveManifest._checksum_dictionary(
wrong_tick["local_state"]
)
assert_null(SimulationSaveManifest.from_dictionary(wrong_tick))
assert_null(
SimulationSaveManifest.create(
manager.create_state_record(),
RegionalSimulationFacade.create_jajce_route(
RegionalSimulationFacade.DEFAULT_WORLD_SEED, 1
)
)
)
assert_null(
SimulationSaveManifest.create(
manager.create_state_record(),
RegionalSimulationFacade.create_jajce_route(
RegionalSimulationFacade.DEFAULT_WORLD_SEED + 1, 0
)
)
)
var negative_seed_local: SimulationStateRecord = manager.create_state_record()
negative_seed_local.simulation["seed"] = -42
assert_not_null(
SimulationSaveManifest.create(
negative_seed_local, RegionalSimulationFacade.create_jajce_route(-42, 0)
)
)
manager.simulate_tick()
var synchronized := SimulationSaveManifest.create(
manager.create_state_record(),
RegionalSimulationFacade.create_jajce_route(RegionalSimulationFacade.DEFAULT_WORLD_SEED, 1)
)
assert_not_null(synchronized)
assert_eq(synchronized.get_regional_facade().get_last_processed_tick(), 1)
assert_eq(int(synchronized.get_local_state().simulation["tick_count"]), 1)
func _valid_manifest_dictionary() -> Dictionary:
var manifest := SimulationSaveManifest.create(
manager.create_state_record(), RegionalSimulationFacade.create_jajce_route()
)
assert_not_null(manifest)
return manifest.to_dictionary()
func _set_regional_stockpile_value(
regional_state: Dictionary, settlement_id: StringName, value: Variant
) -> void:
for settlement: Dictionary in regional_state["caravan_service"]["world_state"]["settlements"]:
if StringName(settlement["settlement_id"]) == settlement_id:
settlement["stockpile_ledger"][String(RegionalSimulationFacade.TRADE_GOODS)] = value
return
assert_true(false, "Expected regional settlement '%s'" % settlement_id)
@@ -0,0 +1 @@
uid://cradryxj45n86