diff --git a/src/graphics_worker.rs b/src/graphics_worker.rs index 3ece5ea8..6bf207bd 100644 --- a/src/graphics_worker.rs +++ b/src/graphics_worker.rs @@ -4,8 +4,7 @@ pub enum Instruction{ Render(crate::physics::PhysicsOutputState,integer::Time,glam::IVec2), //UpdateModel(crate::graphics::GraphicsModelUpdate), Resize(winit::dpi::PhysicalSize,crate::settings::UserSettings), - GenerateModels(strafesnet_common::map::CompleteMap), - ClearModels, + ChangeMap(strafesnet_common::map::CompleteMap), } //Ideally the graphics thread worker description is: @@ -27,11 +26,9 @@ pub fn new<'a>( let mut resize=None; crate::compat_worker::INWorker::new(move |ins:Instruction|{ match ins{ - Instruction::GenerateModels(map)=>{ - graphics.generate_models(&device,&queue,&map); - }, - Instruction::ClearModels=>{ + Instruction::ChangeMap(map)=>{ graphics.clear(); + graphics.generate_models(&device,&queue,&map); }, Instruction::Resize(size,user_settings)=>{ resize=Some((size,user_settings)); @@ -69,4 +66,4 @@ pub fn new<'a>( } } }) -} \ No newline at end of file +} diff --git a/src/physics.rs b/src/physics.rs index 5e821869..07de30db 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -1006,28 +1006,6 @@ impl instruction::InstructionEmitter for PhysicsCont } } impl PhysicsContext{ - pub fn clear(&mut self){ - self.state.clear(); - } - //TODO: remove non-standard interfaces to process_instruction - pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ - self.run_input_instruction(TimedInstruction{ - time:self.state.time, - instruction:PhysicsInputInstruction::SetSensitivity(user_settings.calculate_sensitivity()), - }); - } - pub fn restart(&mut self){ - self.run_input_instruction(TimedInstruction{ - time:self.state.time, - instruction:PhysicsInputInstruction::Restart, - }); - } - pub fn spawn(&mut self){ - self.run_input_instruction(TimedInstruction{ - time:self.state.time, - instruction:PhysicsInputInstruction::Spawn(gameplay_modes::ModeId::MAIN,StageId::FIRST), - }); - } pub const fn output(&self)->PhysicsOutputState{ PhysicsOutputState{ body:self.state.body, @@ -1039,7 +1017,9 @@ impl PhysicsContext{ pub const fn get_next_mouse(&self)->&MouseState{ self.state.input_state.get_next_mouse() } + /// use with caution, this is the only non-instruction way to mess with physics pub fn generate_models(&mut self,map:&map::CompleteMap){ + self.state.clear(); self.data.modes=map.modes.clone(); for mode in &mut self.data.modes.modes{ mode.denormalize_data(); diff --git a/src/physics_worker.rs b/src/physics_worker.rs index 7bd1d785..0a1aec33 100644 --- a/src/physics_worker.rs +++ b/src/physics_worker.rs @@ -3,6 +3,7 @@ use strafesnet_common::physics::Instruction as PhysicsInputInstruction; use strafesnet_common::integer::Time; use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::timer::{Scaled,Timer,TimerState}; +use mouse_interpolator::MouseInterpolator; #[derive(Debug)] pub enum InputInstruction{ @@ -15,27 +16,49 @@ pub enum InputInstruction{ MoveForward(bool), Jump(bool), Zoom(bool), - Restart, - Spawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId), + ResetAndRestart, + ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId), PracticeFly, } pub enum Instruction{ Input(InputInstruction), Render, - Resize(winit::dpi::PhysicalSize,crate::settings::UserSettings), - GenerateModels(strafesnet_common::map::CompleteMap), - ClearModels, + Resize(winit::dpi::PhysicalSize), + ChangeMap(strafesnet_common::map::CompleteMap), + //SetPaused is not an InputInstruction: the physics doesn't know that it's paused. SetPaused(bool), //Graphics(crate::graphics_worker::Instruction), } +mod mouse_interpolator{ + use super::*; + //TODO: move this or tab pub struct MouseInterpolator{ + //"PlayerController" + user_settings:crate::settings::UserSettings, + //"MouseInterpolator" timeline:std::collections::VecDeque>, last_mouse_time:Time,//this value is pre-transformed to simulation time mouse_blocking:bool, + //"Simulation" timer:Timer, + physics:crate::physics::PhysicsContext, + } impl MouseInterpolator{ - fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction,m:glam::IVec2){ + pub fn new( + physics:crate::physics::PhysicsContext, + user_settings:crate::settings::UserSettings, + )->MouseInterpolator{ + MouseInterpolator{ + mouse_blocking:true, + last_mouse_time:physics.get_next_mouse().time, + timeline:std::collections::VecDeque::new(), + timer:Timer::from_state(Scaled::identity(),false), + physics, + user_settings, + } + } + fn push_mouse_instruction(&mut self,ins:&TimedInstruction,m:glam::IVec2){ if self.mouse_blocking{ //tell the game state which is living in the past about its future self.timeline.push_front(TimedInstruction{ @@ -48,7 +71,7 @@ impl MouseInterpolator{ self.timeline.push_front(TimedInstruction{ time:self.last_mouse_time, instruction:PhysicsInputInstruction::ReplaceMouse( - MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos}, + MouseState{time:self.last_mouse_time,pos:self.physics.get_next_mouse().pos}, MouseState{time:self.timer.time(ins.time),pos:m} ), }); @@ -57,58 +80,84 @@ impl MouseInterpolator{ } self.last_mouse_time=self.timer.time(ins.time); } - /// returns the mapped physics input instruction + fn push(&mut self,time:Time,phys_input:PhysicsInputInstruction){ + //This is always a non-mouse event + self.timeline.push_back(TimedInstruction{ + time:self.timer.time(time), + instruction:phys_input, + }); + } + /// returns should_empty_queue /// may or may not mutate internal state XD! - fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction)->Option{ + fn map_instruction(&mut self,ins:&TimedInstruction)->bool{ + let mut update_mouse_blocking=true; match &ins.instruction{ Instruction::Input(input_instruction)=>match input_instruction{ &InputInstruction::MoveMouse(m)=>{ if !self.timer.is_paused(){ - self.push_mouse_instruction(physics,ins,m); + self.push_mouse_instruction(ins,m); } - None + update_mouse_blocking=false; }, - &InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), - &InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), - &InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), - &InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), - &InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), - &InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), - &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), - &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), - &InputInstruction::Spawn(mode_id,stage_id)=>Some(PhysicsInputInstruction::Spawn(mode_id,stage_id)), - InputInstruction::Restart=>Some(PhysicsInputInstruction::Restart), - InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly), + &InputInstruction::MoveForward(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveForward(s)), + &InputInstruction::MoveLeft(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveLeft(s)), + &InputInstruction::MoveBack(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveBack(s)), + &InputInstruction::MoveRight(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveRight(s)), + &InputInstruction::MoveUp(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveUp(s)), + &InputInstruction::MoveDown(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveDown(s)), + &InputInstruction::Jump(s)=>self.push(ins.time,PhysicsInputInstruction::SetJump(s)), + &InputInstruction::Zoom(s)=>self.push(ins.time,PhysicsInputInstruction::SetZoom(s)), + &InputInstruction::ResetAndSpawn(mode_id,stage_id)=>{ + self.push(ins.time,PhysicsInputInstruction::Reset); + self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity())); + self.push(ins.time,PhysicsInputInstruction::Spawn(mode_id,stage_id)); + }, + InputInstruction::ResetAndRestart=>{ + self.push(ins.time,PhysicsInputInstruction::Reset); + self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity())); + self.push(ins.time,PhysicsInputInstruction::Restart); + }, + InputInstruction::PracticeFly=>self.push(ins.time,PhysicsInputInstruction::PracticeFly), }, //do these really need to idle the physics? //sending None dumps the instruction queue - Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), - Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), - Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle), - Instruction::Render=>Some(PhysicsInputInstruction::Idle), + Instruction::ChangeMap(_)=>self.push(ins.time,PhysicsInputInstruction::Idle), + Instruction::Resize(_)=>self.push(ins.time,PhysicsInputInstruction::Idle), + Instruction::Render=>self.push(ins.time,PhysicsInputInstruction::Idle), &Instruction::SetPaused(paused)=>{ if let Err(e)=self.timer.set_paused(ins.time,paused){ println!("Cannot pause: {e}"); } - Some(PhysicsInputInstruction::Idle) + self.push(ins.time,PhysicsInputInstruction::Idle); }, } + if update_mouse_blocking{ + //this returns the bool for us + self.update_mouse_blocking(ins.time) + }else{ + //do flush that queue + true + } } - fn update_mouse_blocking(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction)->bool{ + /// must check if self.mouse_blocking==true before calling! + fn unblock_mouse(&mut self,time:Time){ + //push an event to extrapolate no movement from + self.timeline.push_front(TimedInstruction{ + time:self.last_mouse_time, + instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(time),pos:self.physics.get_next_mouse().pos}), + }); + self.last_mouse_time=self.timer.time(time); + //stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets. + self.mouse_blocking=false; + } + fn update_mouse_blocking(&mut self,time:Time)->bool{ if self.mouse_blocking{ //assume the mouse has stopped moving after 10ms. //shitty mice are 125Hz which is 8ms so this should cover that. //setting this to 100us still doesn't print even though it's 10x lower than the polling rate, //so mouse events are probably not handled separately from drawing and fire right before it :( - if Time::from_millis(10),phys_input_option:Option)->bool{ - if let Some(phys_input)=phys_input_option{ - //non-mouse event - self.timeline.push_back(TimedInstruction{ - time:self.timer.time(ins.time), - instruction:phys_input, - }); - - //this returns the bool for us - self.update_mouse_blocking(physics,ins) - }else{ - //mouse event - true - } - } - fn empty_queue(&mut self,physics:&mut crate::physics::PhysicsContext){ + fn empty_queue(&mut self){ while let Some(instruction)=self.timeline.pop_front(){ - physics.run_input_instruction(instruction); + self.physics.run_input_instruction(instruction); } } - fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:&TimedInstruction){ - let physics_input_option=self.map_instruction(physics,ins); - let should_empty_queue=self.handle_physics_input(physics,ins,physics_input_option); + pub fn handle_instruction(&mut self,ins:&TimedInstruction){ + let should_empty_queue=self.map_instruction(ins); if should_empty_queue{ - self.empty_queue(physics); + self.empty_queue(); } } + pub fn get_render_stuff(&self,time:Time)->(crate::physics::PhysicsOutputState,Time,glam::IVec2){ + (self.physics.output(),self.timer.time(time),self.physics.get_next_mouse().pos) + } + pub fn change_map(&mut self,time:Time,map:&strafesnet_common::map::CompleteMap){ + //dump any pending interpolation state + if self.mouse_blocking{ + self.unblock_mouse(time); + } + self.empty_queue(); + + //doing it like this to avoid doing PhysicsInstruction::ChangeMap(Rc) + self.physics.generate_models(&map); + + //use the standard input interface so the instructions are written out to bots + self.handle_instruction(&TimedInstruction{ + time:self.timer.time(time), + instruction:Instruction::Input(InputInstruction::ResetAndSpawn( + strafesnet_common::gameplay_modes::ModeId::MAIN, + strafesnet_common::gameplay_modes::StageId::FIRST, + )), + }); + } + pub const fn user_settings(&self)->&crate::settings::UserSettings{ + &self.user_settings + } +} } -pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ - let mut interpolator=MouseInterpolator{ - mouse_blocking:true, - last_mouse_time:physics.get_next_mouse().time, - timeline:std::collections::VecDeque::new(), - timer:Timer::from_state(Scaled::identity(),false), - }; +pub fn new<'a>( + mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>, + user_settings:crate::settings::UserSettings, +)->crate::compat_worker::QNWorker<'a,TimedInstruction>{ + let physics=crate::physics::PhysicsContext::default(); + let mut interpolator=MouseInterpolator::new( + physics, + user_settings + ); crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ - interpolator.handle_instruction(&mut physics,&ins); + interpolator.handle_instruction(&ins); match ins.instruction{ Instruction::Render=>{ - graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),interpolator.timer.time(ins.time),physics.get_next_mouse().pos)).unwrap(); + let (physics_output,time,mouse_pos)=interpolator.get_render_stuff(ins.time); + graphics_worker.send(crate::graphics_worker::Instruction::Render(physics_output,time,mouse_pos)).unwrap(); }, - Instruction::Resize(size,user_settings)=>{ - graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); + Instruction::Resize(size)=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,interpolator.user_settings().clone())).unwrap(); }, - Instruction::GenerateModels(map)=>{ - physics.generate_models(&map); - //important! - //bots will not work properly without this exact restart + spawn setup - //reset the physics state to start a new run on the new map - physics.restart(); - //generate a spawn event so bots work properly on the first run - //no run started so does not invalidate the run - physics.spawn(); - graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap(); + Instruction::ChangeMap(map)=>{ + interpolator.change_map(ins.time,&map); + graphics_worker.send(crate::graphics_worker::Instruction::ChangeMap(map)).unwrap(); }, - Instruction::ClearModels=>{ - physics.clear(); - graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap(); - }, - _=>(), + Instruction::Input(_)=>(), + Instruction::SetPaused(_)=>(), } }) } diff --git a/src/setup.rs b/src/setup.rs index 1e39d4eb..790352d2 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -213,9 +213,11 @@ pub fn setup_and_start(title:String){ //dedicated thread to ping request redraw back and resize the window doesn't seem logical - let window=crate::window::WindowContextSetup::new(&setup_context,&window); //the thread that spawns the physics thread - let mut window_thread=window.into_worker(setup_context); + let mut window_thread=crate::window::worker( + &window, + setup_context, + ); if let Some(arg)=std::env::args().nth(1){ let path=std::path::PathBuf::from(arg); diff --git a/src/window.rs b/src/window.rs index 14132278..4320c672 100644 --- a/src/window.rs +++ b/src/window.rs @@ -15,7 +15,6 @@ struct WindowContext<'a>{ manual_mouse_lock:bool, mouse:strafesnet_common::mouse::MouseState,//std::sync::Arc> screen_size:glam::UVec2, - user_settings:crate::settings::UserSettings, window:&'a winit::window::Window, physics_thread:crate::compat_worker::QNWorker<'a, TimedInstruction>, } @@ -28,10 +27,7 @@ impl WindowContext<'_>{ match event { winit::event::WindowEvent::DroppedFile(path)=>{ match crate::file::load(path.as_path()){ - Ok(map)=>{ - self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ClearModels}).unwrap(); - self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::GenerateModels(map)}).unwrap(); - }, + Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ChangeMap(map)}).unwrap(), Err(e)=>println!("Failed to load map: {e}"), } }, @@ -114,7 +110,7 @@ impl WindowContext<'_>{ "r"=>if s{ //mouse needs to be reset since the position is absolute self.mouse=strafesnet_common::mouse::MouseState::default(); - Some(InputInstruction::Restart) + Some(InputInstruction::ResetAndRestart) }else{None}, "f"=>if s{Some(InputInstruction::PracticeFly)}else{None}, _=>None, @@ -169,48 +165,32 @@ impl WindowContext<'_>{ } } } - -pub struct WindowContextSetup<'a>{ - user_settings:crate::settings::UserSettings, +pub fn worker<'a>( window:&'a winit::window::Window, - physics:crate::physics::PhysicsContext, - graphics:crate::graphics::GraphicsState, -} - -impl<'a> WindowContextSetup<'a>{ - pub fn new(context:&crate::setup::SetupContext,window:&'a winit::window::Window)->Self{ + setup_context:crate::setup::SetupContext<'a>, +)->crate::compat_worker::QNWorker<'a,TimedInstruction>{ + // WindowContextSetup::new let user_settings=crate::settings::read_user_settings(); - let mut physics=crate::physics::PhysicsContext::default(); - physics.load_user_settings(&user_settings); - - let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue,&context.config); + let mut graphics=crate::graphics::GraphicsState::new(&setup_context.device,&setup_context.queue,&setup_context.config); graphics.load_user_settings(&user_settings); - Self{ - user_settings, - window, - graphics, - physics, - } - } - - fn into_context(self,setup_context:crate::setup::SetupContext<'a>)->WindowContext<'a>{ + //WindowContextSetup::into_context let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height); - let graphics_thread=crate::graphics_worker::new(self.graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue); - WindowContext{ + let graphics_thread=crate::graphics_worker::new(graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue); + let mut window_context=WindowContext{ manual_mouse_lock:false, mouse:strafesnet_common::mouse::MouseState::default(), //make sure to update this!!!!! screen_size, - user_settings:self.user_settings, - window:self.window, - physics_thread:crate::physics_worker::new(self.physics,graphics_thread), - } - } + window, + physics_thread:crate::physics_worker::new( + graphics_thread, + user_settings, + ), + }; - pub fn into_worker(self,setup_context:crate::setup::SetupContext<'a>)->crate::compat_worker::QNWorker<'a,TimedInstruction>{ - let mut window_context=self.into_context(setup_context); + //WindowContextSetup::into_worker crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ match ins.instruction{ WindowInstruction::RequestRedraw=>{ @@ -226,7 +206,7 @@ impl<'a> WindowContextSetup<'a>{ window_context.physics_thread.send( TimedInstruction{ time:ins.time, - instruction:crate::physics_worker::Instruction::Resize(size,window_context.user_settings.clone()) + instruction:crate::physics_worker::Instruction::Resize(size) } ).unwrap(); } @@ -240,5 +220,4 @@ impl<'a> WindowContextSetup<'a>{ } } }) - } }