refactor(simulation): drop PROTOCOL_VERSION from bridge handshake (#874, D-192)

Removes the PROTOCOL_VERSION lockstep handshake across the bridge
transports. HandshakeMessage is now an empty marker indicating the
server is ready; the client sends StartupMessage next. Field-presence
and roundtrip behavioral tests are retained — tautological
version-literal assertions were deleted in Sprint 36.

After this lands, genuine schema drift surfaces as MessagePack
missing-field errors downstream, which is the intended signal per
D-192. Client ticket #875 is blocked by this commit and will ride
the same wave; server schema changes in #878 (character_archetype
removal) also depend on PROTOCOL_VERSION already being gone.

types.rs changes are bundled with the broader #877/#878 dead-code
sweep in the following commit, since they share the same file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 08:55:19 +02:00
co-authored by Claude Opus 4.6
parent bae1c5e156
commit c640563fc0
7 changed files with 21 additions and 48 deletions
+5 -5
View File
@@ -96,17 +96,17 @@ impl SimBridge for LocalBridge {
}
fn send_handshake(&self) -> Result<(), BridgeError> {
use super::types::{HandshakeMessage, PROTOCOL_VERSION};
let msg = HandshakeMessage {
protocol_version: PROTOCOL_VERSION,
};
// D-192: HandshakeMessage carries no version. Send an empty marker so the
// client knows to begin the startup sequence (send StartupMessage next).
use super::types::HandshakeMessage;
let msg = HandshakeMessage {};
let payload = rmp_serde::to_vec_named(&msg)?;
let mut writer = self
.writer
.lock()
.map_err(|e| BridgeError::MutexPoisoned(format!("writer: {}", e)))?;
write_framed(writer.get_mut(), &payload)?;
tracing::info!("sent handshake: protocol_version={}", PROTOCOL_VERSION);
tracing::info!("sent handshake");
Ok(())
}
+5 -5
View File
@@ -158,10 +158,10 @@ impl SimBridge for TcpBridge {
}
fn send_handshake(&self) -> Result<(), BridgeError> {
use super::types::{HandshakeMessage, PROTOCOL_VERSION};
let msg = HandshakeMessage {
protocol_version: PROTOCOL_VERSION,
};
// D-192: HandshakeMessage carries no version. Send an empty marker so the
// client knows to begin the startup sequence (send StartupMessage next).
use super::types::HandshakeMessage;
let msg = HandshakeMessage {};
let payload = rmp_serde::to_vec_named(&msg)?;
let mut writer = self
.writer
@@ -173,7 +173,7 @@ impl SimBridge for TcpBridge {
let result = write_framed(stream, &payload);
stream.set_nonblocking(true).map_err(BridgeError::Io)?;
result?;
tracing::info!("sent handshake: protocol_version={}", PROTOCOL_VERSION);
tracing::info!("sent handshake");
Ok(())
}
-2
View File
@@ -220,7 +220,6 @@ mod tests {
fn make_snapshot() -> ObserverSnapshot {
ObserverSnapshot {
version: PROTOCOL_VERSION,
tick: 42,
game_time: GameTime {
day: 0,
@@ -427,7 +426,6 @@ mod tests {
#[test]
fn empty_snapshot_no_panic() {
let snap = ObserverSnapshot {
version: PROTOCOL_VERSION,
tick: 0,
game_time: GameTime {
day: 0,
+9 -28
View File
@@ -153,7 +153,7 @@ fn main() {
app.add_plugins(settled_reach_server::npc::NpcPlugin);
app.add_plugins(settled_reach_server::storyteller::StorytellerPlugin);
app.add_plugins(settled_reach_server::settings::SettingsPlugin);
app.add_plugins(settled_reach_server::bookmark::BookmarkPlugin);
app.add_plugins(settled_reach_server::bookmark::BookmarkPlugin::default());
// Initialize culture resolver (#679, D-128).
// systems.db is shipped read-only alongside the binary.
@@ -216,20 +216,17 @@ fn main() {
),
);
// Character archetype from client's StartupMessage (#587).
let archetype = startup.character_archetype;
// Gauntlet test world for --test-mode, proof room for normal mode.
if test_mode {
#[cfg(feature = "gauntlet")]
settled_reach_server::test_world::setup_gauntlet(&mut app, archetype);
settled_reach_server::test_world::setup_gauntlet(&mut app);
#[cfg(not(feature = "gauntlet"))]
{
eprintln!("--test-mode requires the 'gauntlet' feature");
std::process::exit(1);
}
} else {
setup_proof_room(&mut app, archetype, seed);
setup_proof_room(&mut app, seed);
}
tracing::info!(
@@ -325,7 +322,6 @@ fn send_panic_error(app: &App, panic_msg: &str) {
// Build a minimal snapshot carrying the panic error
let snapshot = ObserverSnapshot {
version: PROTOCOL_VERSION,
tick,
game_time: GameTime {
day: 0,
@@ -394,7 +390,7 @@ fn dump_schedule_graph() {
app.add_plugins(settled_reach_server::npc::NpcPlugin);
app.add_plugins(settled_reach_server::storyteller::StorytellerPlugin);
app.add_plugins(settled_reach_server::settings::SettingsPlugin);
app.add_plugins(settled_reach_server::bookmark::BookmarkPlugin);
app.add_plugins(settled_reach_server::bookmark::BookmarkPlugin::default());
app.insert_resource(settled_reach_server::simulation::rng::SimRng::new(0));
// Access Schedules resource directly — schedules are populated by plugins
@@ -422,11 +418,7 @@ fn dump_schedule_graph() {
/// Proof room: 32x32 map, wall at (16,14), player at (16,16), 3 NPCs.
/// Extracted from the original inline setup for reuse by both test-mode and normal mode.
fn setup_proof_room(
app: &mut App,
archetype: settled_reach_server::bridge::types::CharacterArchetype,
world_seed: u64,
) {
fn setup_proof_room(app: &mut App, world_seed: u64) {
use settled_reach_server::knowledge::registry::EntityRegistry;
use settled_reach_server::knowledge::KnowledgeGraph;
use settled_reach_server::npc::relationships::{RelationshipEdge, RelationshipGraph};
@@ -459,19 +451,9 @@ fn setup_proof_room(
let mut registry = EntityRegistry::new(0);
// Player at (16,16) — archetype from StartupMessage (#587, D-053)
let profile = match archetype {
settled_reach_server::bridge::types::CharacterArchetype::Smuggler => {
MovementProfile::smuggler()
}
settled_reach_server::bridge::types::CharacterArchetype::Detective => {
MovementProfile::detective()
}
};
let monologue_state = MonologueState {
character: archetype.as_monologue_key().to_string(),
..Default::default()
};
// Player at (16,16). Archetype-specific spawn logic was removed in Sprint 37
// (D-032 purge); per-culture/per-role voice is reintroduced in Phase 6.
let profile = MovementProfile::default();
let player = app
.world_mut()
.spawn((
@@ -480,12 +462,11 @@ fn setup_proof_room(
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
monologue_state,
MonologueState::default(),
MonologueBuffer::default(),
SprintAnomalyQueue::default(),
CognitiveDelay::default(),
ListeningFocus::new(TilePosition::new(16, 16, 0)),
archetype,
profile,
profile.initial_stance(),
PlayerMoveCooldown::default(),
-1
View File
@@ -33,7 +33,6 @@ fn snapshot_roundtrip_over_unix_socket() {
let bridge = LocalBridge::accept(&server_path).expect("failed to accept");
let snapshot = ObserverSnapshot {
version: PROTOCOL_VERSION,
tick: 42,
game_time: GameTime {
day: 0,
-1
View File
@@ -19,7 +19,6 @@ fn snapshot_roundtrip_over_tcp() {
let bridge = TcpBridge::accept_on(listener).expect("failed to accept");
let snapshot = ObserverSnapshot {
version: PROTOCOL_VERSION,
tick: 42,
game_time: GameTime {
day: 0,
+2 -6
View File
@@ -99,13 +99,9 @@ fn ipc_round_trip_latency() {
let handshake_bytes = read_framed(&mut reader)
.expect("read handshake")
.expect("server closed before sending HandshakeMessage");
let handshake: HandshakeMessage =
// D-192: HandshakeMessage carries no version field. Verify it deserialises cleanly.
let _handshake: HandshakeMessage =
rmp_serde::from_slice(&handshake_bytes).expect("deserialize HandshakeMessage");
assert_eq!(
handshake.protocol_version, PROTOCOL_VERSION,
"handshake version mismatch: server={}, client={}",
handshake.protocol_version, PROTOCOL_VERSION
);
let make_input = |tick: u64| PlayerInput {
tick,