diff --git a/lib/common/src/physics.rs b/lib/common/src/physics.rs index f20273e..5f5fd77 100644 --- a/lib/common/src/physics.rs +++ b/lib/common/src/physics.rs @@ -4,17 +4,20 @@ use crate::mouse::MouseState; pub enum TimeInner{} pub type Time=crate::integer::Time; -#[derive(Clone,Debug)] -pub enum UnbufferedInstruction{ - MoveMouse(glam::IVec2), - Other(OtherInstruction), -} - #[derive(Clone,Debug)] pub enum Instruction{ Mouse(MouseInstruction), Other(OtherInstruction), } +impl Instruction{ + pub const IDLE:Self=Self::Other(OtherInstruction::Other(OtherOtherInstruction::Idle)); +} +#[derive(Clone,Debug)] +pub enum OtherInstruction{ + SetControl(SetControlInstruction), + Mode(ModeInstruction), + Other(OtherOtherInstruction), +} #[derive(Clone,Debug)] pub enum MouseInstruction{ /// Replace the entire interpolation state to avoid dividing by zero when replacing twice @@ -25,7 +28,7 @@ pub enum MouseInstruction{ SetNextMouse(MouseState), } #[derive(Clone,Debug)] -pub enum OtherInstruction{ +pub enum SetControlInstruction{ SetMoveRight(bool), SetMoveUp(bool), SetMoveBack(bool), @@ -34,6 +37,9 @@ pub enum OtherInstruction{ SetMoveForward(bool), SetJump(bool), SetZoom(bool), +} +#[derive(Clone,Debug)] +pub enum ModeInstruction{ /// Reset: fully replace the physics state. /// This forgets all inputs and settings which need to be reapplied. Reset, @@ -42,10 +48,11 @@ pub enum OtherInstruction{ /// Spawn: Teleport to a specific mode's spawn /// Sets current mode & spawn Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId), +} +#[derive(Clone,Debug)] +pub enum OtherOtherInstruction{ + /// Idle: there were no input events, but the simulation is safe to advance to this timestep Idle, - //Idle: there were no input events, but the simulation is safe to advance to this timestep - //for interpolation / networking / playback reasons, most playback heads will always want - //to be 1 instruction ahead to generate the next state for interpolation. PracticeFly, SetSensitivity(crate::integer::Ratio64Vec2), } diff --git a/strafe-client/src/mouse_interpolator.rs b/strafe-client/src/mouse_interpolator.rs index 8176b6d..b6f3f95 100644 --- a/strafe-client/src/mouse_interpolator.rs +++ b/strafe-client/src/mouse_interpolator.rs @@ -1,6 +1,5 @@ use strafesnet_common::mouse::MouseState; use strafesnet_common::physics::{ - UnbufferedInstruction as PhysicsUnbufferedInstruction, Instruction as PhysicsInputInstruction, Time as PhysicsTime, TimeInner as PhysicsTimeInner, @@ -11,11 +10,18 @@ use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInn use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,TimedInstruction}; type TimedPhysicsInstruction=TimedInstruction; -type PhysicsTimedUnbufferedInstruction=TimedInstruction; -type DoubleTimedUnbufferedInstruction=TimedInstruction; +type TimedUnbufferedInstruction=TimedInstruction; +type DoubleTimedUnbufferedInstruction=TimedInstruction; const MOUSE_TIMEOUT:SessionTime=SessionTime::from_millis(10); +/// To be fed into MouseInterpolator +#[derive(Clone,Debug)] +pub enum Instruction{ + MoveMouse(glam::IVec2), + Other(OtherInstruction), +} + pub enum StepInstruction{ Pop, Timeout, @@ -35,7 +41,7 @@ pub struct MouseInterpolator{ } // Maybe MouseInterpolator manipulation is better expressed using impls // and called from Instruction trait impls in session -impl InstructionConsumer for MouseInterpolator{ +impl InstructionConsumer for MouseInterpolator{ type TimeInner=SessionTimeInner; fn process_instruction(&mut self,ins:DoubleTimedUnbufferedInstruction){ self.push_unbuffered_input(ins) @@ -69,7 +75,7 @@ impl MouseInterpolator{ let (ins_mouse,ins_other)=replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{ let next_state=match buffer_state{ BufferState::Unbuffered=>{ - if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{ + if let Instruction::MoveMouse(pos)=ins.instruction.instruction{ return ((None,None),BufferState::Initializing(ins.time,MouseState{pos,time:ins.instruction.time})); } BufferState::Unbuffered @@ -87,7 +93,7 @@ impl MouseInterpolator{ }; return ((Some(ins_mouse),None),BufferState::Unbuffered); } - if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{ + if let Instruction::MoveMouse(pos)=ins.instruction.instruction{ let next_mouse_state=MouseState{pos,time:ins.instruction.time}; let ins_mouse=TimedInstruction{ time:mouse_state.time, @@ -114,7 +120,7 @@ impl MouseInterpolator{ }; return ((Some(ins_mouse),None),BufferState::Unbuffered); } - if let PhysicsUnbufferedInstruction::MoveMouse(pos)=ins.instruction.instruction{ + if let Instruction::MoveMouse(pos)=ins.instruction.instruction{ let next_mouse_state=MouseState{pos,time:ins.instruction.time}; let ins_mouse=TimedInstruction{ time:ins.instruction.time, @@ -126,8 +132,8 @@ impl MouseInterpolator{ }, }; let ins_other=match ins.instruction.instruction{ - PhysicsUnbufferedInstruction::MoveMouse(_)=>None, - PhysicsUnbufferedInstruction::Other(other_instruction)=>Some(TimedInstruction{ + Instruction::MoveMouse(_)=>None, + Instruction::Other(other_instruction)=>Some(TimedInstruction{ time:ins.instruction.time, instruction:other_instruction, }), @@ -208,6 +214,7 @@ impl MouseInterpolator{ // This should be simulation_timer.time(timeout) // but the timer is not accessible from this scope // and it's just here to say that the mouse isn't moving anyways. + // I think this is a divide by zero bug, two identical mouse_states will occupy the interpolation state time:mouse_state.time, instruction:PhysicsInputInstruction::Mouse( MouseInstruction::SetNextMouse(mouse_state) diff --git a/strafe-client/src/physics.rs b/strafe-client/src/physics.rs index ae55342..b176cd6 100644 --- a/strafe-client/src/physics.rs +++ b/strafe-client/src/physics.rs @@ -884,9 +884,7 @@ impl PhysicsState{ self.touching.clear(); } fn reset_to_default(&mut self){ - let mut new_state=Self::default(); - new_state.camera.sensitivity=self.camera.sensitivity; - *self=new_state; + *self=Self::default(); } fn next_move_instruction(&self)->Option>{ self.move_state.next_move_instruction(&self.style.strafe,self.time) @@ -1837,6 +1835,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI state.set_move_state(data,MoveState::Air); b_refresh_walk_target=false; } + // Spawn does not necessarily imply reset PhysicsInputInstruction::Other(PhysicsOtherInstruction::Spawn(mode_id,stage_id))=>{ //spawn at a particular stage if let Some(mode)=data.modes.get_mode(mode_id){ diff --git a/strafe-client/src/physics_worker.rs b/strafe-client/src/physics_worker.rs index 9c119de..225b07a 100644 --- a/strafe-client/src/physics_worker.rs +++ b/strafe-client/src/physics_worker.rs @@ -1,19 +1,19 @@ use crate::graphics_worker::Instruction as GraphicsInstruction; -use crate::session::{ExternalInstruction as SessionInstruction,Session, Simulation}; +use crate::session::{SessionInputInstruction,Instruction as SessionInstruction,Session,Simulation}; use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer}; use strafesnet_common::physics::Time as PhysicsTime; use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner}; use strafesnet_common::timer::Timer; pub enum Instruction{ - Input(strafesnet_common::physics::UnbufferedInstruction), + Input(SessionInputInstruction), SetPaused(bool), Render, Resize(winit::dpi::PhysicalSize), ChangeMap(strafesnet_common::map::CompleteMap), } -const SESSION_INSTRUCTION_IDLE:SessionInstruction=SessionInstruction::Input(strafesnet_common::physics::UnbufferedInstruction::Other(strafesnet_common::physics::OtherInstruction::Idle)); +const SESSION_INSTRUCTION_IDLE:SessionInstruction=SessionInstruction::Input(SessionInputInstruction::Other(strafesnet_common::physics::OtherOtherInstruction::Idle)); pub fn new<'a>( mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>, diff --git a/strafe-client/src/session.rs b/strafe-client/src/session.rs index 1490a28..27d659d 100644 --- a/strafe-client/src/session.rs +++ b/strafe-client/src/session.rs @@ -1,20 +1,39 @@ use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,InstructionFeedback,TimedInstruction}; // session represents the non-hardware state of the client. // Ideally it is a deterministic state which is atomically updated by instructions, same as the simulation state. -use strafesnet_common::physics::{UnbufferedInstruction as PhysicsUnbufferedInstruction,Instruction as PhysicsInputInstruction,TimeInner as PhysicsTimeInner,Time as PhysicsTime}; +use strafesnet_common::physics::{ + ModeInstruction,OtherInstruction,OtherOtherInstruction, + Instruction as PhysicsInputInstruction, + TimeInner as PhysicsTimeInner, + Time as PhysicsTime +}; use strafesnet_common::timer::{Scaled,Timer}; use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime}; -use crate::mouse_interpolator::{MouseInterpolator,StepInstruction}; +use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction}; use crate::settings::UserSettings; -pub enum ExternalInstruction<'a>{ - Input(PhysicsUnbufferedInstruction), +pub enum Instruction<'a>{ + Input(SessionInputInstruction), SetPaused(bool), ChangeMap(&'a strafesnet_common::map::CompleteMap), //Graphics(crate::graphics_worker::Instruction), } +pub enum SessionInputInstruction{ + Mouse(glam::IVec2), + SetControl(strafesnet_common::physics::SetControlInstruction), + Mode(ImplicitModeInstruction), + Other(strafesnet_common::physics::OtherOtherInstruction), +} +/// Implicit mode instruction are fed separately to session. +/// Session generates the explicit mode instructions interlaced with a SetSensitivity instruction +#[derive(Clone,Debug)] +pub enum ImplicitModeInstruction{ + ResetAndRestart, + ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId), +} + pub struct FrameState{ pub body:crate::physics::Body, pub camera:crate::physics::PhysicsCamera, @@ -99,25 +118,50 @@ impl Session{ // Session consumes DoStep -> forwards DoStep to mouseinterpolator // Session emits DoStep -impl InstructionConsumer> for Session{ +impl InstructionConsumer> for Session{ type TimeInner=SessionTimeInner; - fn process_instruction(&mut self,ins:TimedInstruction){ + fn process_instruction(&mut self,ins:TimedInstruction){ match ins.instruction{ // send it down to MouseInterpolator with two timestamps, SessionTime and PhysicsTime - ExternalInstruction::Input(instruction)=>self.mouse_interpolator.process_instruction(TimedInstruction{ - time:ins.time, - instruction:TimedInstruction{ - time:self.simulation.timer.time(ins.time), - instruction, - }, - }), - ExternalInstruction::SetPaused(paused)=>{ + Instruction::Input(instruction)=>{ + let instructions:&[MouseInterpolatorInstruction]=match instruction{ + SessionInputInstruction::Mouse(pos)=>&[ + MouseInterpolatorInstruction::MoveMouse(pos) + ], + SessionInputInstruction::SetControl(set_control_instruction)=>&[ + MouseInterpolatorInstruction::Other(OtherInstruction::SetControl(set_control_instruction)) + ], + SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndRestart)=>&[ + MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)), + MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))), + MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Restart)), + ], + SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(mode_id,spawn_id))=>&[ + MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)), + MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))), + MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id))), + ], + SessionInputInstruction::Other(other_other_instruction)=>&[ + MouseInterpolatorInstruction::Other(OtherInstruction::Other(other_other_instruction)) + ], + }; + for ins_interpolator in (*instructions).into_iter(){ + self.mouse_interpolator.process_instruction(TimedInstruction{ + time:ins.time, + instruction:TimedInstruction{ + time:self.simulation.timer.time(ins.time), + instruction:ins_interpolator, + }, + }); + } + }, + Instruction::SetPaused(paused)=>{ // don't flush the buffered instructions in the mouse interpolator // until the mouse is confirmed to be not moving at a later time // what if they pause for 5ms lmao _=self.simulation.timer.set_paused(ins.time,paused); } - ExternalInstruction::ChangeMap(complete_map)=>self.change_map(complete_map), + Instruction::ChangeMap(complete_map)=>self.change_map(complete_map), }; // run all buffered instruction produced self.process_exhaustive(ins.time); diff --git a/strafe-client/src/window.rs b/strafe-client/src/window.rs index 277f148..941eb71 100644 --- a/strafe-client/src/window.rs +++ b/strafe-client/src/window.rs @@ -1,7 +1,8 @@ use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner}; -use strafesnet_common::physics::{OtherInstruction,UnbufferedInstruction}; +use strafesnet_common::physics::{OtherInstruction,OtherOtherInstruction,SetControlInstruction}; use crate::physics_worker::Instruction as PhysicsWorkerInstruction; +use crate::session::SessionInputInstruction; pub enum Instruction{ Resize(winit::dpi::PhysicalSize), @@ -91,29 +92,29 @@ impl WindowContext<'_>{ }, (keycode,state)=>{ let s=state.is_pressed(); - if let Some(input_instruction)=match keycode{ - winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(OtherInstruction::SetJump(s)), + if let Some(session_input_instruction)=match keycode{ + winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(s))), winit::keyboard::Key::Character(key)=>match key.as_str(){ - "W"|"w"=>Some(OtherInstruction::SetMoveForward(s)), - "A"|"a"=>Some(OtherInstruction::SetMoveLeft(s)), - "S"|"s"=>Some(OtherInstruction::SetMoveBack(s)), - "D"|"d"=>Some(OtherInstruction::SetMoveRight(s)), - "E"|"e"=>Some(OtherInstruction::SetMoveUp(s)), - "Q"|"q"=>Some(OtherInstruction::SetMoveDown(s)), - "Z"|"z"=>Some(OtherInstruction::SetZoom(s)), - "R"|"r"=>if s{ + "W"|"w"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveForward(s))), + "A"|"a"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveLeft(s))), + "S"|"s"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveBack(s))), + "D"|"d"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveRight(s))), + "E"|"e"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveUp(s))), + "Q"|"q"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveDown(s))), + "Z"|"z"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetZoom(s))), + "R"|"r"=>s.then(||{ //mouse needs to be reset since the position is absolute self.mouse_pos=glam::DVec2::ZERO; - Some(OtherInstruction::ResetAndRestart) - }else{None}, - "F"|"f"=>if s{Some(OtherInstruction::PracticeFly)}else{None}, + SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndRestart) + }), + "F"|"f"=>s.then_some(SessionInputInstruction::Other(OtherOtherInstruction::PracticeFly)), _=>None, }, _=>None, }{ self.physics_thread.send(TimedInstruction{ time, - instruction:PhysicsWorkerInstruction::Input(UnbufferedInstruction::Other(input_instruction)), + instruction:PhysicsWorkerInstruction::Input(session_input_instruction), }).unwrap(); } }, @@ -137,7 +138,7 @@ impl WindowContext<'_>{ self.mouse_pos+=glam::dvec2(delta.0,delta.1); self.physics_thread.send(TimedInstruction{ time, - instruction:PhysicsWorkerInstruction::Input(UnbufferedInstruction::MoveMouse(self.mouse_pos.as_ivec2())), + instruction:PhysicsWorkerInstruction::Input(SessionInputInstruction::Mouse(self.mouse_pos.as_ivec2())), }).unwrap(); }, winit::event::DeviceEvent::MouseWheel { @@ -147,7 +148,7 @@ impl WindowContext<'_>{ if false{//self.physics.style.use_scroll{ self.physics_thread.send(TimedInstruction{ time, - instruction:PhysicsWorkerInstruction::Input(UnbufferedInstruction::Other(OtherInstruction::SetJump(true))),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump + instruction:PhysicsWorkerInstruction::Input(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(true))),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump }).unwrap(); } }, diff --git a/strafe-client/src/worker.rs b/strafe-client/src/worker.rs index 4373d56..52bb351 100644 --- a/strafe-client/src/worker.rs +++ b/strafe-client/src/worker.rs @@ -190,7 +190,7 @@ mod test{ for _ in 0..5 { let task = instruction::TimedInstruction{ time:strafesnet_common::physics::Time::ZERO, - instruction:strafesnet_common::physics::Instruction::Other(strafesnet_common::physics::OtherInstruction::Idle), + instruction:strafesnet_common::physics::Instruction::IDLE, }; worker.send(task).unwrap(); } @@ -204,7 +204,7 @@ mod test{ // Send a new task let task = instruction::TimedInstruction{ time:integer::Time::ZERO, - instruction:strafesnet_common::physics::Instruction::Other(strafesnet_common::physics::OtherInstruction::Idle), + instruction:strafesnet_common::physics::Instruction::IDLE, }; worker.send(task).unwrap();