feat(simulation): bookmark definition system with bridge protocol (#614)
BookmarkPlugin, BookmarkRegistry, SelectedBookmark resources. Tycoon bookmark defined; PROTOCOL_VERSION bumped to 22. RequestBookmarkCatalog + ConfirmBookmark actions wired into process_player_input via BookmarkInputParams SystemParam bundle (resolves Bevy's 16-system-param limit). build_catalog() accepts optional CultureResolver for D-128 location-culture mapping. Snapshot delivery at tick-0 via SnapshotBuffer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,12 @@
|
||||
// Timestamped player input events for deterministic simulation (D-010 principle 4)
|
||||
// PlayerInput: semantic actions (MoveNorth, Interact, UsePerceptionMode, ToggleStance)
|
||||
|
||||
use crate::bookmark::{BookmarkRegistry, SelectedBookmark};
|
||||
use crate::knowledge::CultureResolverResource;
|
||||
use crate::bridge::debug::DebugCommandBuffer;
|
||||
use crate::bridge::types::{FacingDirection, ObjectType, PlayerAction, PlayerInput};
|
||||
use crate::bridge::types::{
|
||||
FacingDirection, ObjectType, PlayerAction, PlayerInput, SimError, SimErrorKind, SnapshotBuffer,
|
||||
};
|
||||
use crate::knowledge::{EntityRegistry, StableId};
|
||||
use crate::perception::vision_cone::{facing_from_delta, Facing};
|
||||
use crate::settings::{SettingsCommand, SettingsCommandBuffer};
|
||||
@@ -18,6 +22,7 @@ use crate::simulation::stance::{PlayerMoveCooldown, Stance};
|
||||
use crate::simulation::time::{SimulationTime, TickRate};
|
||||
use crate::test_world::reset::{RoomResetTrigger, RoomSnapshots};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::system::SystemParam;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// Maximum number of inputs the queue will hold before dropping oldest.
|
||||
@@ -78,6 +83,19 @@ impl InputQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Bundled SystemParam for bookmark-related input handling.
|
||||
///
|
||||
/// Bevy's blanket `IntoSystem` impl covers functions up to 16 parameters.
|
||||
/// Bundling the 4 bookmark params keeps `process_player_input` at exactly 16.
|
||||
#[derive(SystemParam)]
|
||||
pub struct BookmarkInputParams<'w> {
|
||||
pub registry: Option<Res<'w, BookmarkRegistry>>,
|
||||
pub selected: Option<ResMut<'w, SelectedBookmark>>,
|
||||
pub snapshot_buf: Option<ResMut<'w, SnapshotBuffer>>,
|
||||
pub sim_error_buf: Option<ResMut<'w, crate::bridge::types::SimErrorBuffer>>,
|
||||
pub culture: Option<Res<'w, CultureResolverResource>>,
|
||||
}
|
||||
|
||||
/// Drains InputQueue for the current tick, converts PlayerActions to ECS components.
|
||||
/// Handles stance toggling (D-053), movement cooldown, Take/Place verbs (#424),
|
||||
/// and save/load commands (#553).
|
||||
@@ -106,6 +124,7 @@ pub fn process_player_input(
|
||||
mut econ_query_buf: Option<ResMut<EconQueryBuffer>>,
|
||||
door_states: Query<&DoorState>,
|
||||
object_types: Query<&ObjectType>,
|
||||
mut bookmark: BookmarkInputParams<'_>,
|
||||
) {
|
||||
let current_tick = time.tick;
|
||||
let paused = time.paused();
|
||||
@@ -130,6 +149,8 @@ pub fn process_player_input(
|
||||
| PlayerAction::RequestAllSettings
|
||||
| PlayerAction::DeleteSetting { .. }
|
||||
| PlayerAction::EconStateQuery { .. }
|
||||
| PlayerAction::RequestBookmarkCatalog
|
||||
| PlayerAction::ConfirmBookmark { .. }
|
||||
)
|
||||
{
|
||||
continue;
|
||||
@@ -420,6 +441,30 @@ pub fn process_player_input(
|
||||
tracing::debug!("EconStateQuery received but EconQueryBuffer not registered — economy not loaded");
|
||||
}
|
||||
}
|
||||
PlayerAction::RequestBookmarkCatalog => {
|
||||
if let (Some(ref registry), Some(ref mut buf)) =
|
||||
(&bookmark.registry, &mut bookmark.snapshot_buf)
|
||||
{
|
||||
let resolver = bookmark.culture.as_deref().map(|c| &c.0);
|
||||
buf.pending_bookmark_catalog = Some(registry.build_catalog(resolver));
|
||||
tracing::debug!("RequestBookmarkCatalog: catalog staged");
|
||||
} else {
|
||||
tracing::warn!("RequestBookmarkCatalog: BookmarkRegistry or SnapshotBuffer not available");
|
||||
}
|
||||
}
|
||||
PlayerAction::ConfirmBookmark {
|
||||
bookmark_id,
|
||||
starting_location_id,
|
||||
} => {
|
||||
handle_confirm_bookmark(
|
||||
bookmark_id,
|
||||
starting_location_id,
|
||||
&bookmark.registry,
|
||||
&mut bookmark.selected,
|
||||
&mut bookmark.sim_error_buf,
|
||||
current_tick,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1121,6 +1166,52 @@ fn handle_teleport_to_hub(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_confirm_bookmark(
|
||||
bookmark_id: String,
|
||||
starting_location_id: String,
|
||||
registry: &Option<Res<BookmarkRegistry>>,
|
||||
selected_bookmark: &mut Option<ResMut<SelectedBookmark>>,
|
||||
sim_error_buf: &mut Option<ResMut<crate::bridge::types::SimErrorBuffer>>,
|
||||
tick: u64,
|
||||
) {
|
||||
let Some(ref registry) = registry else {
|
||||
tracing::warn!("ConfirmBookmark: BookmarkRegistry not available");
|
||||
return;
|
||||
};
|
||||
let Some(defn) = registry.get(&bookmark_id) else {
|
||||
let msg = format!("ConfirmBookmark: unknown bookmark_id {:?}", bookmark_id);
|
||||
tracing::warn!("{}", msg);
|
||||
if let Some(ref mut buf) = sim_error_buf {
|
||||
buf.push(SimError {
|
||||
kind: SimErrorKind::ProtocolError,
|
||||
message: msg,
|
||||
tick,
|
||||
});
|
||||
}
|
||||
return;
|
||||
};
|
||||
if !defn.allowed_locations.contains(&starting_location_id) {
|
||||
let msg = format!(
|
||||
"ConfirmBookmark: starting_location_id {:?} not in allowed_locations for {:?}",
|
||||
starting_location_id, bookmark_id
|
||||
);
|
||||
tracing::warn!("{}", msg);
|
||||
if let Some(ref mut buf) = sim_error_buf {
|
||||
buf.push(SimError {
|
||||
kind: SimErrorKind::ProtocolError,
|
||||
message: msg,
|
||||
tick,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Some(ref mut sel) = selected_bookmark {
|
||||
sel.bookmark_id = Some(bookmark_id.clone());
|
||||
sel.starting_location_id = Some(starting_location_id.clone());
|
||||
tracing::info!(bookmark_id, starting_location_id, "ConfirmBookmark: selection recorded");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user