diff --git a/client/src/graphics/window.rs b/client/src/graphics/window.rs index 43f512b5..49992096 100644 --- a/client/src/graphics/window.rs +++ b/client/src/graphics/window.rs @@ -266,6 +266,11 @@ impl Window { sim.toggle_no_clip(); } } + KeyCode::KeyB if state == ElementState::Pressed => { + if let Some(sim) = self.sim.as_mut() { + sim.debug_spawn_blinker(); + } + } KeyCode::F1 if state == ElementState::Pressed => { self.gui_state.toggle_gui(); } diff --git a/client/src/prediction.rs b/client/src/prediction.rs index 5d9c5ab7..b7ee98c3 100644 --- a/client/src/prediction.rs +++ b/client/src/prediction.rs @@ -119,6 +119,7 @@ mod tests { movement: na::Vector3::x(), jump: false, no_clip: true, + debug_spawn_blinker: false, block_update: None, }; diff --git a/client/src/sim.rs b/client/src/sim.rs index 983e818e..e39531ba 100644 --- a/client/src/sim.rs +++ b/client/src/sim.rs @@ -15,7 +15,7 @@ use common::{ graph_ray_casting, node::{populate_fresh_nodes, ChunkId, VoxelData}, proto::{ - self, BlockUpdate, Character, CharacterInput, CharacterState, Command, Component, + self, Blinker, BlockUpdate, Character, CharacterInput, CharacterState, Command, Component, Inventory, Position, }, sanitize_motion_input, @@ -64,6 +64,7 @@ pub struct Sim { no_clip: bool, /// Whether no_clip will be toggled next step toggle_no_clip: bool, + debug_spawn_blinker: bool, /// Whether the current step starts with a jump is_jumping: bool, /// Whether the jump button has been pressed since the last step @@ -101,6 +102,7 @@ impl Sim { average_movement_input: na::zero(), no_clip: true, toggle_no_clip: false, + debug_spawn_blinker: false, is_jumping: false, jump_pressed: false, jump_held: false, @@ -145,6 +147,11 @@ impl Sim { self.toggle_no_clip = true; } + pub fn debug_spawn_blinker(&mut self) { + // Note: the blinker currently does nothing but update internal state + self.debug_spawn_blinker = true; + } + pub fn set_jump_held(&mut self, jump_held: bool) { self.jump_held = jump_held; self.jump_pressed = jump_held || self.jump_pressed; @@ -287,6 +294,9 @@ impl Sim { for &(id, ref new_state) in &msg.character_states { self.update_character_state(id, new_state); } + for &(id, ref new_blinker) in &msg.blinker_states { + self.update_blinker_state(id, new_blinker); + } self.reconcile_prediction(msg.latest_input); } } @@ -322,6 +332,20 @@ impl Sim { } } + fn update_blinker_state(&mut self, id: EntityId, new_blinker: &Blinker) { + match self.entity_ids.get(&id) { + None => debug!(%id, "blinker state update for unknown entity"), + Some(&entity) => match self.world.get::<&mut Blinker>(entity) { + Ok(mut blinker) => { + *blinker = new_blinker.clone(); + } + Err(e) => { + error!(%id, "blinker state update error: {}", e) + } + }, + } + } + fn reconcile_prediction(&mut self, latest_input: u16) { let id = self.local_character_id; let Some(&entity) = self.entity_ids.get(&id) else { @@ -424,6 +448,9 @@ impl Sim { node = Some(x.node); builder.add(x); } + Blinker(x) => { + builder.add(x); + } Inventory(x) => { builder.add(x); } @@ -455,6 +482,7 @@ impl Sim { movement: sanitize_motion_input(orientation * self.average_movement_input), jump: self.is_jumping, no_clip: self.no_clip, + debug_spawn_blinker: self.debug_spawn_blinker, block_update: self.get_local_character_block_update(), }; let generation = self @@ -467,6 +495,7 @@ impl Sim { character_input, orientation: self.local_character_controller.orientation(), }); + self.debug_spawn_blinker = false; } fn update_view_position(&mut self) { @@ -488,6 +517,7 @@ impl Sim { / (self.since_input_sent.as_secs_f32() / self.cfg.step_interval.as_secs_f32()), jump: self.is_jumping, no_clip: self.no_clip, + debug_spawn_blinker: self.debug_spawn_blinker, block_update: None, }; character_controller::run_character_step( diff --git a/common/src/proto.rs b/common/src/proto.rs index b8db0c82..719e77f4 100644 --- a/common/src/proto.rs +++ b/common/src/proto.rs @@ -38,6 +38,7 @@ pub struct StateDelta { pub latest_input: u16, pub positions: Vec<(EntityId, Position)>, pub character_states: Vec<(EntityId, CharacterState)>, + pub blinker_states: Vec<(EntityId, Blinker)>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -72,6 +73,7 @@ pub struct CharacterInput { pub movement: na::Vector3, pub jump: bool, pub no_clip: bool, + pub debug_spawn_blinker: bool, pub block_update: Option, } @@ -93,6 +95,7 @@ pub struct SerializedVoxelData { pub enum Component { Character(Character), Position(Position), + Blinker(Blinker), Material(Material), Inventory(Inventory), } @@ -110,6 +113,11 @@ pub struct Character { pub state: CharacterState, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Blinker { + pub on: bool, +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Inventory { pub contents: Vec, diff --git a/server/src/sim.rs b/server/src/sim.rs index dac719b4..42f33e3b 100644 --- a/server/src/sim.rs +++ b/server/src/sim.rs @@ -19,8 +19,8 @@ use common::{ math, node::{populate_fresh_nodes, Chunk}, proto::{ - Character, CharacterInput, CharacterState, ClientHello, Command, Component, FreshNode, - Position, Spawns, StateDelta, + Blinker, Character, CharacterInput, CharacterState, ClientHello, Command, Component, + FreshNode, Position, Spawns, StateDelta, }, traversal::{ensure_nearby, nearby_nodes}, worldgen::ChunkParams, @@ -203,6 +203,7 @@ impl Sim { movement: na::Vector3::zeros(), jump: false, no_clip: true, + debug_spawn_blinker: false, block_update: None, }; self.spawn((position, character, inventory, initial_input)) @@ -289,6 +290,16 @@ impl Sim { let span = error_span!("step", step = self.step); let _guard = span.enter(); + for (_entity, blinker) in self.world.query::<&mut Blinker>().iter() { + blinker.on = !blinker.on; + + if blinker.on { + tracing::info!("Blinked ON"); + } else { + tracing::info!("Blinked OFF"); + } + } + // Extend graph structure for (_, (position, _)) in self.world.query::<(&mut Position, &mut Character)>().iter() { ensure_nearby(&mut self.graph, position, self.cfg.view_distance); @@ -341,6 +352,7 @@ impl Sim { } } + let mut pending_blinker_spawns: Vec<(Position, Blinker)> = Vec::new(); let mut pending_block_updates: Vec<(Entity, BlockUpdate)> = vec![]; // Simulate @@ -350,6 +362,10 @@ impl Sim { .iter() { let prev_node = position.node; + if input.debug_spawn_blinker { + let blinker: Blinker = Blinker { on: false }; + pending_blinker_spawns.push((*position, blinker)); + } character_controller::run_character_step( &self.cfg, &self.graph, @@ -370,6 +386,10 @@ impl Sim { self.dirty_nodes.insert(position.node); } + for (position, blinker) in pending_blinker_spawns { + self.spawn((position, blinker)); + } + for (entity, block_update) in pending_block_updates { let id = *self.world.get::<&EntityId>(entity).unwrap(); self.attempt_block_update(id, block_update); @@ -397,6 +417,12 @@ impl Sim { .iter() .map(|(_, (&id, ch))| (id, ch.state.clone())) .collect(), + blinker_states: self + .world + .query::<(&EntityId, &Blinker)>() + .iter() + .map(|(_, (&id, blinker))| (id, blinker.clone())) + .collect(), }; self.step += 1; @@ -495,6 +521,9 @@ fn dump_entity(world: &hecs::World, entity: Entity) -> Vec { if let Ok(x) = world.get::<&Character>(entity) { components.push(Component::Character((*x).clone())); } + if let Ok(x) = world.get::<&Blinker>(entity) { + components.push(Component::Blinker((*x).clone())); + } if let Ok(x) = world.get::<&Inventory>(entity) { components.push(Component::Inventory((*x).clone())); }