feat(simulation): SQLite settings storage with IPC protocol v20 (#627)

Per-player settings via rusqlite (bundled, zero runtime dep). Server
owns the settings DB; client sends ChangeSettings commands over IPC.
Extensible key-value with typed columns (String/Int/Float/Bool).
Protocol bumped to v20 with settings_response in ObserverSnapshot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 09:11:53 +01:00
co-authored by Claude Opus 4.6
parent 1a4fd578cc
commit accbe579c9
24 changed files with 776 additions and 4 deletions
+26
View File
@@ -4,6 +4,7 @@
use crate::bridge::debug::DebugCommandBuffer;
use crate::bridge::types::{FacingDirection, ObjectType, PlayerAction, PlayerInput};
use crate::settings::{SettingsCommand, SettingsCommandBuffer};
use crate::knowledge::{EntityRegistry, StableId};
use crate::perception::vision_cone::{facing_from_delta, Facing};
use crate::simulation::interaction::{DoorInteractRequest, DoorState, TerminalInteractRequest};
@@ -100,6 +101,7 @@ pub fn process_player_input(
mut room_snapshots: Option<ResMut<RoomSnapshots>>,
mut save_load: Option<ResMut<SaveLoadPending>>,
mut debug_cmd_buffer: Option<ResMut<DebugCommandBuffer>>,
mut settings_cmd_buffer: Option<ResMut<SettingsCommandBuffer>>,
door_states: Query<&DoorState>,
object_types: Query<&ObjectType>,
) {
@@ -122,6 +124,9 @@ pub fn process_player_input(
| PlayerAction::SaveGame { .. }
| PlayerAction::LoadGame { .. }
| PlayerAction::DebugCommand(_)
| PlayerAction::ChangeSetting { .. }
| PlayerAction::RequestAllSettings
| PlayerAction::DeleteSetting { .. }
)
{
continue;
@@ -374,6 +379,27 @@ pub fn process_player_input(
tracing::warn!("DebugCommand received but DebugCommandBuffer not registered");
}
}
PlayerAction::ChangeSetting { key, value } => {
if let Some(ref mut buf) = settings_cmd_buffer {
buf.push(SettingsCommand::Change { key, value });
} else {
tracing::warn!("ChangeSetting received but SettingsCommandBuffer not registered");
}
}
PlayerAction::RequestAllSettings => {
if let Some(ref mut buf) = settings_cmd_buffer {
buf.push(SettingsCommand::RequestAll);
} else {
tracing::warn!("RequestAllSettings received but SettingsCommandBuffer not registered");
}
}
PlayerAction::DeleteSetting { key } => {
if let Some(ref mut buf) = settings_cmd_buffer {
buf.push(SettingsCommand::Delete { key });
} else {
tracing::warn!("DeleteSetting received but SettingsCommandBuffer not registered");
}
}
}
}