Implements the StartupMessage protocol: client generates world_seed in SessionManager.new_game(), sends it after handshake, server uses it to seed SimRng and sample EntanglementConfig. EntanglementConfig samples flat ∈ [25,35]%, intrigue ∈ [15,25]%, mundane as remainder (D-029). Same seed produces identical config (D-010 determinism). Different seeds produce distinct configs in ≥90% of pairs. Protocol flow: HandshakeMessage (server→client) → StartupMessage with world_seed (client→server) → SimRng initialization → tick loop. 10 Rust tests (determinism, variation, bounds, sum invariant). 9 GDScript test stubs + 2 encode tests for client-side pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,12 @@ var gauntlet_mode: bool = false # true when snapshot includes gauntlet_mode fla
|
||||
# the server's "insert_active" snapshot field, disabling all z-layer-6 UI.
|
||||
var insert_active: bool = true
|
||||
|
||||
# #175: World seed for deterministic simulation (D-010, D-029).
|
||||
# Set by SessionManager.new_game(), sent to server via StartupMessage in SimBridge.
|
||||
# Same seed → same EntanglementConfig → same NPC population across playthroughs.
|
||||
# Persists for the session lifetime; not overwritten by apply_snapshot().
|
||||
var world_seed: int = 0
|
||||
|
||||
# #507: RNG seed for replay determinism — populated from snapshot "rng_seed" field.
|
||||
# Null in v0.1 (server does not yet send this field; protocol change required).
|
||||
var rng_seed: Variant = null
|
||||
|
||||
@@ -31,6 +31,12 @@ func new_game() -> String:
|
||||
save_path, error_string(err)])
|
||||
return ""
|
||||
GameState.current_game_id = game_id
|
||||
|
||||
# #175: Generate world_seed for deterministic simulation (D-010, D-029).
|
||||
# Uses randi() (u32) for a seed that maps cleanly to Rust u64 via MessagePack.
|
||||
# 4 billion seeds is sufficient entropy for EntanglementConfig variation (D-029).
|
||||
GameState.world_seed = rng.randi()
|
||||
|
||||
return game_id
|
||||
|
||||
|
||||
|
||||
@@ -231,6 +231,26 @@ func _process(delta: float) -> void:
|
||||
_set_state(ConnectionState.ERROR)
|
||||
return
|
||||
|
||||
# Send startup message with world_seed (#175, D-010/D-029).
|
||||
# Server blocks waiting for this before entering the tick loop.
|
||||
var startup_bytes := Protocol.encode_startup_message(GameState.world_seed)
|
||||
if startup_bytes.size() > 0:
|
||||
var send_err := _bridge.send_message(startup_bytes)
|
||||
if send_err != OK:
|
||||
var reason := "Failed to send startup message: %s" % error_string(send_err)
|
||||
push_error("SimBridge: %s" % reason)
|
||||
handshake_failed.emit(reason)
|
||||
_bridge.disconnect_from_server()
|
||||
_set_state(ConnectionState.ERROR)
|
||||
return
|
||||
else:
|
||||
var reason := "Failed to encode startup message"
|
||||
push_error("SimBridge: %s" % reason)
|
||||
handshake_failed.emit(reason)
|
||||
_bridge.disconnect_from_server()
|
||||
_set_state(ConnectionState.ERROR)
|
||||
return
|
||||
|
||||
handshake_complete.emit(server_version)
|
||||
_set_state(ConnectionState.CONNECTED)
|
||||
return
|
||||
|
||||
@@ -427,6 +427,18 @@ static func _decode_enum_variant(raw) -> Dictionary:
|
||||
|
||||
# -- Encode: GDScript types → bytes to server ----------------------------------
|
||||
|
||||
## Encode a StartupMessage to MessagePack bytes (#175).
|
||||
## Sent by the client immediately after handshake validation.
|
||||
## Server reads this to initialize SimRng with the world seed (D-010, D-029).
|
||||
static func encode_startup_message(world_seed: int) -> PackedByteArray:
|
||||
var msg := {"world_seed": world_seed}
|
||||
var result = Messagepack.encode(msg)
|
||||
if result.status != null:
|
||||
push_error("Protocol: startup message encode failed: %s" % result.status)
|
||||
return PackedByteArray()
|
||||
return result.value
|
||||
|
||||
|
||||
## Encode a PlayerInput to MessagePack bytes.
|
||||
## action_name: one of "MoveNorth", "MoveSouth", "MoveEast", "MoveWest",
|
||||
## "Interact", "UsePerceptionMode", "Pause", "Unpause"
|
||||
|
||||
Reference in New Issue
Block a user