Compare commits

...

51 Commits

Author SHA1 Message Date
f98ffe6e0b fix spin bug edge case 2025-01-15 00:48:03 -08:00
f9d4ca8370 remove single use function 2025-01-15 00:36:15 -08:00
fea5bf4398 low polling rate edge case 2025-01-15 00:29:45 -08:00
d393f9f187 change some function names 2025-01-15 00:26:40 -08:00
ada34237c9 fix timeout timestamp 2025-01-15 00:02:26 -08:00
292df72709 transpose next buffer state calculation 2025-01-14 23:49:31 -08:00
7476d7cdc7 the mouse spin bug 2025-01-14 23:49:31 -08:00
6138d70a6f unify timeout 😍 2025-01-14 23:49:31 -08:00
cac4d698c3 fix overall correctness 2025-01-14 23:23:50 -08:00
e0ea0d6119 remove holdover case 2025-01-14 23:00:46 -08:00
80a4431ee8 test mouse_interpolator 2025-01-14 23:00:46 -08:00
80424cf24c spawn on map change 2025-01-14 21:41:55 -08:00
c338826513 finish 2025-01-14 21:16:36 -08:00
a6a242175b rewrite enums again 2025-01-14 20:44:17 -08:00
08bd57ffe1 remove incorrect comment 2025-01-14 19:02:34 -08:00
0d9c6648e2 accumulate mouse_pos as float 2025-01-14 19:00:17 -08:00
405cba3549 discover ternary method on bool 2025-01-14 18:39:38 -08:00
38d8dc1302 InstructionCache 2025-01-14 18:26:59 -08:00
33ccefc411 pop_buffered_instruction can be accomplished with mem::replace 2025-01-14 03:44:23 -08:00
93277c042b make pain code smaller 2025-01-14 01:45:09 -08:00
90f6437817 wrong instruction 2025-01-14 01:36:50 -08:00
29f9d5298f work 2025-01-14 01:34:26 -08:00
b0489a3746 work work work 2025-01-13 23:59:16 -08:00
a8847d3632 ruin physics code 2025-01-13 23:55:42 -08:00
fb8c2a619a rename fields in MouseInstruction::ReplaceMouse 2025-01-13 23:51:13 -08:00
6898302fa5 move code to more relevant location 2025-01-13 23:23:58 -08:00
52bbaaddc7 don't mutate physics_timeline on the fly 2025-01-13 23:23:58 -08:00
a8581a2a4f don't reconstruct MouseState struct with noop 2025-01-13 23:23:58 -08:00
c6ff11dd3e use replace_with to replace the enum variant in-place without cloning 2025-01-13 23:11:43 -08:00
844c7a08e1 add replace_with dep 2025-01-13 22:46:16 -08:00
bd61d03c91 work 2025-01-13 22:32:26 -08:00
b58ebb2775 todos 2025-01-11 01:50:06 -08:00
9095215cad write pop_buffered_instruction 2025-01-11 01:38:45 -08:00
92c30c3b87 cool changes 2025-01-11 01:07:06 -08:00
1b35c96f6e cook a bit 2025-01-10 23:18:53 -08:00
47bf9f1af3 pain 2025-01-10 22:08:54 -08:00
719c702b95 actually need ReplaceMouse because of OS level issue
The operating system does not report the timestamp at which it checks that the mouse was not moving, so the mouse interpolation will necessarily be incorrect for up to 1 polling period.  The alternative is to guess / make up a timestamp, but I don't want to do this.
2025-01-10 22:01:02 -08:00
ceb2499ad2 delete ReplaceMouse instruction 2025-01-10 20:59:25 -08:00
fe43ce9df6 progress 2025-01-10 20:03:53 -08:00
1fcd18bc45 how does it work 2025-01-09 21:20:25 -08:00
e371f95a4b a 2025-01-09 21:14:17 -08:00
b02c1bc7b4 idk if dropinstruction is gonan work 2025-01-09 21:14:15 -08:00
89446a933a a 2025-01-09 20:48:11 -08:00
0a3d965bb6 work 2025-01-09 20:48:11 -08:00
b6206d52c8 work 2025-01-09 20:48:11 -08:00
498c628280 asd 2025-01-09 20:48:11 -08:00
273e915f67 no 2025-01-09 20:48:11 -08:00
5072e5d7a8 yeah 2025-01-09 20:48:11 -08:00
3f0e3e0d3c update mouse interpolator code 2025-01-09 20:48:11 -08:00
2e88ae0612 wip 2025-01-09 20:48:11 -08:00
4c216a5b28 wip 2025-01-09 20:48:11 -08:00
14 changed files with 738 additions and 430 deletions

7
Cargo.lock generated
View File

@ -2019,6 +2019,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "replace_with"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690"
[[package]] [[package]]
name = "rmp" name = "rmp"
version = "0.8.14" version = "0.8.14"
@ -2236,6 +2242,7 @@ dependencies = [
"id", "id",
"parking_lot", "parking_lot",
"pollster", "pollster",
"replace_with",
"strafesnet_bsp_loader", "strafesnet_bsp_loader",
"strafesnet_common", "strafesnet_common",
"strafesnet_deferred_loader", "strafesnet_deferred_loader",

View File

@ -33,6 +33,42 @@ impl<I,T,X> InstructionFeedback<I,T> for X
X:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>, X:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>,
{} {}
pub struct InstructionCache<S,I,T>{
instruction_machine:S,
cached_instruction:Option<TimedInstruction<I,T>>,
time_limit:Time<T>,
}
impl<S,I,T> InstructionCache<S,I,T>
where
Time<T>:Copy+Ord,
Option<TimedInstruction<I,T>>:Clone,
S:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>
{
pub fn new(
instruction_machine:S,
)->Self{
Self{
instruction_machine,
cached_instruction:None,
time_limit:Time::MIN,
}
}
pub fn next_instruction_cached(&mut self,time_limit:Time<T>)->Option<TimedInstruction<I,T>>{
if time_limit<self.time_limit{
return self.cached_instruction.clone();
}
let next_instruction=self.instruction_machine.next_instruction(time_limit);
self.cached_instruction=next_instruction.clone();
self.time_limit=time_limit;
next_instruction
}
pub fn process_instruction(&mut self,instruction:TimedInstruction<I,T>){
// invalidate cache
self.time_limit=Time::MIN;
self.instruction_machine.process_instruction(instruction);
}
}
//PROPER PRIVATE FIELDS!!! //PROPER PRIVATE FIELDS!!!
pub struct InstructionCollector<I,T>{ pub struct InstructionCollector<I,T>{
time:Time<T>, time:Time<T>,

View File

@ -1,11 +1,34 @@
use crate::mouse::MouseState;
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)] #[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub enum TimeInner{} pub enum TimeInner{}
pub type Time=crate::integer::Time<TimeInner>; pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub enum Instruction{ pub enum Instruction{
ReplaceMouse(crate::mouse::MouseState<TimeInner>,crate::mouse::MouseState<TimeInner>), Mouse(MouseInstruction),
SetNextMouse(crate::mouse::MouseState<TimeInner>), 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
ReplaceMouse{
m0:MouseState<TimeInner>,
m1:MouseState<TimeInner>,
},
SetNextMouse(MouseState<TimeInner>),
}
#[derive(Clone,Debug)]
pub enum SetControlInstruction{
SetMoveRight(bool), SetMoveRight(bool),
SetMoveUp(bool), SetMoveUp(bool),
SetMoveBack(bool), SetMoveBack(bool),
@ -14,6 +37,9 @@ pub enum Instruction{
SetMoveForward(bool), SetMoveForward(bool),
SetJump(bool), SetJump(bool),
SetZoom(bool), SetZoom(bool),
}
#[derive(Clone,Debug)]
pub enum ModeInstruction{
/// Reset: fully replace the physics state. /// Reset: fully replace the physics state.
/// This forgets all inputs and settings which need to be reapplied. /// This forgets all inputs and settings which need to be reapplied.
Reset, Reset,
@ -22,10 +48,11 @@ pub enum Instruction{
/// Spawn: Teleport to a specific mode's spawn /// Spawn: Teleport to a specific mode's spawn
/// Sets current mode & spawn /// Sets current mode & spawn
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId), 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,
//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, PracticeFly,
SetSensitivity(crate::integer::Ratio64Vec2), SetSensitivity(crate::integer::Ratio64Vec2),
} }

View File

@ -23,6 +23,7 @@ glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
parking_lot = "0.12.1" parking_lot = "0.12.1"
pollster = "0.4.0" pollster = "0.4.0"
replace_with = "0.1.7"
strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true } strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true }
strafesnet_common = { path = "../lib/common", registry = "strafesnet" } strafesnet_common = { path = "../lib/common", registry = "strafesnet" }
strafesnet_deferred_loader = { path = "../lib/deferred_loader", features = ["legacy"], registry = "strafesnet", optional = true } strafesnet_deferred_loader = { path = "../lib/deferred_loader", features = ["legacy"], registry = "strafesnet", optional = true }

View File

@ -1,7 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::integer;
use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId}; use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId, RenderConfigId, TextureCoordinateId, VertexId};
use wgpu::{util::DeviceExt,AstcBlock,AstcChannel}; use wgpu::{util::DeviceExt,AstcBlock,AstcChannel};
use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex}; use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex};
@ -876,7 +875,7 @@ impl GraphicsState{
view:&wgpu::TextureView, view:&wgpu::TextureView,
device:&wgpu::Device, device:&wgpu::Device,
queue:&wgpu::Queue, queue:&wgpu::Queue,
frame_state:crate::physics_worker::FrameState, frame_state:crate::session::FrameState,
){ ){
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input //TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input

View File

@ -1,5 +1,5 @@
pub enum Instruction{ pub enum Instruction{
Render(crate::physics_worker::FrameState), Render(crate::session::FrameState),
//UpdateModel(crate::graphics::GraphicsModelUpdate), //UpdateModel(crate::graphics::GraphicsModelUpdate),
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings), Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
ChangeMap(strafesnet_common::map::CompleteMap), ChangeMap(strafesnet_common::map::CompleteMap),
@ -14,13 +14,13 @@ WorkerDescription{
*/ */
//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order //up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order
pub fn new<'a>( pub fn new(
mut graphics:crate::graphics::GraphicsState, mut graphics:crate::graphics::GraphicsState,
mut config:wgpu::SurfaceConfiguration, mut config:wgpu::SurfaceConfiguration,
surface:wgpu::Surface<'a>, surface:wgpu::Surface,
device:wgpu::Device, device:wgpu::Device,
queue:wgpu::Queue, queue:wgpu::Queue,
)->crate::compat_worker::INWorker<'a,Instruction>{ )->crate::compat_worker::INWorker<'_,Instruction>{
crate::compat_worker::INWorker::new(move |ins:Instruction|{ crate::compat_worker::INWorker::new(move |ins:Instruction|{
match ins{ match ins{
Instruction::ChangeMap(map)=>{ Instruction::ChangeMap(map)=>{

View File

@ -4,6 +4,7 @@ mod setup;
mod window; mod window;
mod worker; mod worker;
mod physics; mod physics;
mod session;
mod graphics; mod graphics;
mod settings; mod settings;
mod push_solve; mod push_solve;
@ -13,6 +14,7 @@ mod model_physics;
mod model_graphics; mod model_graphics;
mod physics_worker; mod physics_worker;
mod graphics_worker; mod graphics_worker;
mod mouse_interpolator;
const TITLE:&'static str=concat!("Strafe Client v",env!("CARGO_PKG_VERSION")); const TITLE:&'static str=concat!("Strafe Client v",env!("CARGO_PKG_VERSION"));

View File

@ -0,0 +1,245 @@
use strafesnet_common::mouse::MouseState;
use strafesnet_common::physics::{
Instruction as PhysicsInputInstruction,
TimeInner as PhysicsTimeInner,
Time as PhysicsTime,
MouseInstruction,
OtherInstruction,
};
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,TimedInstruction};
type TimedPhysicsInstruction=TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>;
type TimedUnbufferedInstruction=TimedInstruction<Instruction,PhysicsTimeInner>;
type DoubleTimedUnbufferedInstruction=TimedInstruction<TimedUnbufferedInstruction,SessionTimeInner>;
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,
}
#[derive(Clone,Debug)]
enum BufferState{
Unbuffered,
Initializing(SessionTime,MouseState<PhysicsTimeInner>),
Buffered(SessionTime,MouseState<PhysicsTimeInner>),
}
pub struct MouseInterpolator{
buffer_state:BufferState,
// double timestamped timeline?
buffer:std::collections::VecDeque<TimedPhysicsInstruction>,
output:std::collections::VecDeque<TimedPhysicsInstruction>,
}
// Maybe MouseInterpolator manipulation is better expressed using impls
// and called from Instruction trait impls in session
impl InstructionConsumer<TimedUnbufferedInstruction> for MouseInterpolator{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:DoubleTimedUnbufferedInstruction){
self.push_unbuffered_input(ins)
}
}
impl InstructionEmitter<StepInstruction> for MouseInterpolator{
type TimeInner=SessionTimeInner;
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::TimeInner>>{
self.buffered_instruction_with_timeout(time_limit)
}
}
impl MouseInterpolator{
pub fn new()->MouseInterpolator{
MouseInterpolator{
buffer_state:BufferState::Unbuffered,
buffer:std::collections::VecDeque::new(),
output:std::collections::VecDeque::new(),
}
}
fn push_mouse_and_flush_buffer(&mut self,ins:TimedInstruction<MouseInstruction,PhysicsTimeInner>){
self.buffer.push_front(TimedInstruction{
time:ins.time,
instruction:PhysicsInputInstruction::Mouse(ins.instruction),
});
// flush buffer to output
if self.output.len()==0{
// swap buffers
core::mem::swap(&mut self.buffer,&mut self.output);
}else{
// append buffer contents to output
self.output.append(&mut self.buffer);
}
}
fn get_mouse_timedout_at(&self,time_limit:SessionTime)->Option<SessionTime>{
match &self.buffer_state{
BufferState::Unbuffered=>None,
BufferState::Initializing(time,_mouse_state)
|BufferState::Buffered(time,_mouse_state)=>{
let timeout=*time+MOUSE_TIMEOUT;
(timeout<time_limit).then_some(timeout)
}
}
}
fn timeout_mouse(&mut self,time:PhysicsTime){
let buffer_state=core::mem::replace(&mut self.buffer_state,BufferState::Unbuffered);
match buffer_state{
BufferState::Unbuffered=>(),
BufferState::Initializing(_time,mouse_state)=>{
// only a single mouse move was sent in 10ms, this is very much an edge case!
self.push_mouse_and_flush_buffer(TimedInstruction{
time:mouse_state.time,
instruction:MouseInstruction::ReplaceMouse{
m1:MouseState{pos:mouse_state.pos,time},
m0:mouse_state,
},
});
}
BufferState::Buffered(_time,mouse_state)=>{
// convert to BufferState::Unbuffered
// use the first instruction which should be a mouse instruction
// to push a ReplaceMouse instruction
// duplicate the current mouse
self.push_mouse_and_flush_buffer(TimedInstruction{
// 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:MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time}),
});
},
}
}
pub fn push_unbuffered_input(&mut self,ins:DoubleTimedUnbufferedInstruction){
// new input
// if there is zero instruction buffered, it means the mouse is not moving
// case 1: unbuffered
// no mouse event is buffered
// - ins is mouse event? change to buffered
// - ins other -> write to timeline
// case 2: buffered
// a mouse event is buffered, and exists within the last 10ms
// case 3: stop
// a mouse event is buffered, but no mouse events have transpired within 10ms
// push buffered mouse instruction and flush buffer to output
if self.get_mouse_timedout_at(ins.time).is_some(){
self.timeout_mouse(ins.instruction.time);
}
// replace_with allows the enum variant to safely be replaced from behind a mutable reference
let (ins_mouse,ins_other)=replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{
match ins.instruction.instruction{
Instruction::MoveMouse(pos)=>{
let next_mouse_state=MouseState{pos,time:ins.instruction.time};
match buffer_state{
BufferState::Unbuffered=>{
((None,None),BufferState::Initializing(ins.time,next_mouse_state))
},
BufferState::Initializing(_time,mouse_state)=>{
let ins_mouse=TimedInstruction{
time:mouse_state.time,
instruction:MouseInstruction::ReplaceMouse{
m0:mouse_state,
m1:next_mouse_state.clone(),
},
};
((Some(ins_mouse),None),BufferState::Buffered(ins.time,next_mouse_state))
},
BufferState::Buffered(_time,mouse_state)=>{
let ins_mouse=TimedInstruction{
time:mouse_state.time,
instruction:MouseInstruction::SetNextMouse(next_mouse_state.clone()),
};
((Some(ins_mouse),None),BufferState::Buffered(ins.time,next_mouse_state))
},
}
},
Instruction::Other(other_instruction)=>((None,Some(TimedInstruction{
time:ins.instruction.time,
instruction:other_instruction,
})),buffer_state),
}
});
if let Some(ins)=ins_mouse{
self.push_mouse_and_flush_buffer(ins);
}
if let Some(ins)=ins_other{
let instruction=TimedInstruction{
time:ins.time,
instruction:PhysicsInputInstruction::Other(ins.instruction),
};
if matches!(self.buffer_state,BufferState::Unbuffered){
self.output.push_back(instruction);
}else{
self.buffer.push_back(instruction);
}
}
}
pub fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTimeInner>>{
match self.get_mouse_timedout_at(time_limit){
Some(timeout)=>Some(TimedInstruction{
time:timeout,
instruction:StepInstruction::Timeout,
}),
None=>(self.output.len()!=0).then_some(TimedInstruction{
// this timestamp should not matter
time:time_limit,
instruction:StepInstruction::Pop,
}),
}
}
pub fn pop_buffered_instruction(&mut self,ins:TimedInstruction<StepInstruction,PhysicsTimeInner>)->Option<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>{
match ins.instruction{
StepInstruction::Pop=>(),
StepInstruction::Timeout=>self.timeout_mouse(ins.time),
}
self.output.pop_front()
}
}
#[cfg(test)]
mod test{
use super::*;
#[test]
fn test(){
let mut interpolator=MouseInterpolator::new();
let timer=strafesnet_common::timer::Timer::<strafesnet_common::timer::Scaled<SessionTimeInner,PhysicsTimeInner>>::unpaused(SessionTime::ZERO,PhysicsTime::from_secs(1000));
macro_rules! push{
($time:expr,$ins:expr)=>{
println!("in={:?}",$ins);
interpolator.push_unbuffered_input(TimedInstruction{
time:$time,
instruction:TimedInstruction{
time:timer.time($time),
instruction:$ins,
}
});
while let Some(ins)=interpolator.buffered_instruction_with_timeout($time){
let ins_retimed=TimedInstruction{
time:timer.time(ins.time),
instruction:ins.instruction,
};
let out=interpolator.pop_buffered_instruction(ins_retimed);
println!("out={out:?}");
}
};
}
// test each buffer_state transition
let mut t=SessionTime::ZERO;
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(5);
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(5);
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(1);
}
}

View File

@ -19,25 +19,18 @@ type MouseState=strafesnet_common::mouse::MouseState<TimeInner>;
//external influence //external influence
//this is how you influence the physics from outside //this is how you influence the physics from outside
use strafesnet_common::physics::Instruction as PhysicsInputInstruction; use strafesnet_common::physics::{Instruction,OtherInstruction,MouseInstruction,ModeInstruction,OtherOtherInstruction,SetControlInstruction};
//internal influence //internal influence
//when the physics asks itself what happens next, this is how it's represented //when the physics asks itself what happens next, this is how it's represented
#[derive(Debug)] #[derive(Debug)]
pub enum PhysicsInternalInstruction{ pub enum InternalInstruction{
CollisionStart(Collision,model_physics::GigaTime), CollisionStart(Collision,model_physics::GigaTime),
CollisionEnd(Collision,model_physics::GigaTime), CollisionEnd(Collision,model_physics::GigaTime),
StrafeTick, StrafeTick,
ReachWalkTargetVelocity, ReachWalkTargetVelocity,
// Water, // Water,
} }
#[derive(Debug)]
pub enum PhysicsInstruction{
Internal(PhysicsInternalInstruction),
//InputInstructions conditionally activate RefreshWalkTarget
//(by doing what SetWalkTargetVelocity used to do and then flagging it)
Input(PhysicsInputInstruction),
}
#[derive(Clone,Debug,Default)] #[derive(Clone,Debug,Default)]
pub struct InputState{ pub struct InputState{
@ -558,13 +551,13 @@ impl MoveState{
=>None, =>None,
} }
} }
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{ fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
//check if you have a valid walk state and create an instruction //check if you have a valid walk state and create an instruction
match self{ match self{
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{ MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
&TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{ &TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{
time, time,
instruction:PhysicsInternalInstruction::ReachWalkTargetVelocity instruction:InternalInstruction::ReachWalkTargetVelocity
}), }),
TransientAcceleration::Unreachable{acceleration:_} TransientAcceleration::Unreachable{acceleration:_}
|TransientAcceleration::Reached |TransientAcceleration::Reached
@ -574,7 +567,7 @@ impl MoveState{
TimedInstruction{ TimedInstruction{
time:strafe.next_tick(time), time:strafe.next_tick(time),
//only poll the physics if there is a before and after mouse event //only poll the physics if there is a before and after mouse event
instruction:PhysicsInternalInstruction::StrafeTick instruction:InternalInstruction::StrafeTick
} }
}), }),
MoveState::Water=>None,//TODO MoveState::Water=>None,//TODO
@ -786,7 +779,7 @@ impl TouchingState{
}).collect(); }).collect();
*acceleration=crate::push_solve::push_solve(&contacts,*acceleration); *acceleration=crate::push_solve::push_solve(&contacts,*acceleration);
} }
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){ fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time); let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time);
for contact in &self.contacts{ for contact in &self.contacts{
//detect face slide off //detect face slide off
@ -795,7 +788,7 @@ impl TouchingState{
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
Collision::Contact(*contact), Collision::Contact(*contact),
time time
), ),
@ -809,7 +802,7 @@ impl TouchingState{
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
TimedInstruction{ TimedInstruction{
time:relative_body.time+time.into(), time:relative_body.time+time.into(),
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:InternalInstruction::CollisionEnd(
Collision::Intersect(*intersect), Collision::Intersect(*intersect),
time time
), ),
@ -882,11 +875,9 @@ impl PhysicsState{
self.touching.clear(); self.touching.clear();
} }
fn reset_to_default(&mut self){ fn reset_to_default(&mut self){
let mut new_state=Self::default(); *self=Self::default();
new_state.camera.sensitivity=self.camera.sensitivity;
*self=new_state;
} }
fn next_move_instruction(&self)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{ fn next_move_instruction(&self)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
self.move_state.next_move_instruction(&self.style.strafe,self.time) self.move_state.next_move_instruction(&self.style.strafe,self.time)
} }
fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){ fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){
@ -935,24 +926,24 @@ pub struct PhysicsContext{
state:PhysicsState,//this captures the entire state of the physics. state:PhysicsState,//this captures the entire state of the physics.
data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state. data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state.
} }
// the physics consumes both PhysicsInputInstruction and PhysicsInternalInstruction, // the physics consumes both Instruction and PhysicsInternalInstruction,
// but can only emit PhysicsInternalInstruction // but can only emit PhysicsInternalInstruction
impl InstructionConsumer<PhysicsInternalInstruction> for PhysicsContext{ impl InstructionConsumer<InternalInstruction> for PhysicsContext{
type TimeInner=TimeInner; type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInternalInstruction,TimeInner>){ fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,TimeInner>){
atomic_internal_instruction(&mut self.state,&self.data,ins) atomic_internal_instruction(&mut self.state,&self.data,ins)
} }
} }
impl InstructionConsumer<PhysicsInputInstruction> for PhysicsContext{ impl InstructionConsumer<Instruction> for PhysicsContext{
type TimeInner=TimeInner; type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInputInstruction,TimeInner>){ fn process_instruction(&mut self,ins:TimedInstruction<Instruction,TimeInner>){
atomic_input_instruction(&mut self.state,&self.data,ins) atomic_input_instruction(&mut self.state,&self.data,ins)
} }
} }
impl InstructionEmitter<PhysicsInternalInstruction> for PhysicsContext{ impl InstructionEmitter<InternalInstruction> for PhysicsContext{
type TimeInner=TimeInner; type TimeInner=TimeInner;
//this little next instruction function could cache its return value and invalidate the cached value by watching the State. //this little next instruction function could cache its return value and invalidate the cached value by watching the State.
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{ fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
next_instruction_internal(&self.state,&self.data,time_limit) next_instruction_internal(&self.state,&self.data,time_limit)
} }
} }
@ -1109,14 +1100,14 @@ impl PhysicsContext{
println!("Physics Objects: {}",model_count); println!("Physics Objects: {}",model_count);
} }
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction,TimeInner>){ pub fn run_input_instruction(&mut self,instruction:TimedInstruction<Instruction,TimeInner>){
self.process_exhaustive(instruction.time); self.process_exhaustive(instruction.time);
self.process_instruction(instruction); self.process_instruction(instruction);
} }
} }
//this is the one who asks //this is the one who asks
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{ fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
let mut collector = instruction::InstructionCollector::new(time_limit); let mut collector = instruction::InstructionCollector::new(time_limit);
@ -1140,11 +1131,11 @@ impl PhysicsContext{
.map_or(None,|(face,dt)|{ .map_or(None,|(face,dt)|{
// this must be rounded to avoid the infinite loop when hitting the start zone // this must be rounded to avoid the infinite loop when hitting the start zone
let time=relative_body.time+dt.into(); let time=relative_body.time+dt.into();
if time<=state.time{None}else{Some((time,face,dt))}}) (state.time<time).then_some((time,face,dt))
.map(|(time,face,dt)| }).map(|(time,face,dt)|
TimedInstruction{ TimedInstruction{
time, time,
instruction:PhysicsInternalInstruction::CollisionStart( instruction:InternalInstruction::CollisionStart(
Collision::new(convex_mesh_id,face), Collision::new(convex_mesh_id,face),
dt dt
) )
@ -1650,13 +1641,13 @@ fn collision_end_intersect(
} }
} }
} }
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction,TimeInner>){ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,TimeInner>){
state.time=ins.time; state.time=ins.time;
let (should_advance_body,goober_time)=match ins.instruction{ let (should_advance_body,goober_time)=match ins.instruction{
PhysicsInternalInstruction::CollisionStart(_,dt) InternalInstruction::CollisionStart(_,dt)
|PhysicsInternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)), |InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
PhysicsInternalInstruction::StrafeTick InternalInstruction::StrafeTick
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>(true,None), |InternalInstruction::ReachWalkTargetVelocity=>(true,None),
}; };
if should_advance_body{ if should_advance_body{
match goober_time{ match goober_time{
@ -1665,7 +1656,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
} }
} }
match ins.instruction{ match ins.instruction{
PhysicsInternalInstruction::CollisionStart(collision,_)=>{ InternalInstruction::CollisionStart(collision,_)=>{
let mode=data.modes.get_mode(state.mode_state.get_mode_id()); let mode=data.modes.get_mode(state.mode_state.get_mode_id());
match collision{ match collision{
Collision::Contact(contact)=>collision_start_contact( Collision::Contact(contact)=>collision_start_contact(
@ -1686,7 +1677,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
), ),
} }
}, },
PhysicsInternalInstruction::CollisionEnd(collision,_)=>match collision{ InternalInstruction::CollisionEnd(collision,_)=>match collision{
Collision::Contact(contact)=>collision_end_contact( Collision::Contact(contact)=>collision_end_contact(
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
data.models.contact_attr(contact.model_id), data.models.contact_attr(contact.model_id),
@ -1701,7 +1692,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
state.time state.time
), ),
}, },
PhysicsInternalInstruction::StrafeTick=>{ InternalInstruction::StrafeTick=>{
//TODO make this less huge //TODO make this less huge
if let Some(strafe_settings)=&state.style.strafe{ if let Some(strafe_settings)=&state.style.strafe{
let controls=state.input_state.controls; let controls=state.input_state.controls;
@ -1719,7 +1710,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
} }
} }
} }
PhysicsInternalInstruction::ReachWalkTargetVelocity=>{ InternalInstruction::ReachWalkTargetVelocity=>{
match &mut state.move_state{ match &mut state.move_state{
MoveState::Air MoveState::Air
|MoveState::Water |MoveState::Water
@ -1746,27 +1737,18 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
} }
} }
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInputInstruction,TimeInner>){ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<Instruction,TimeInner>){
state.time=ins.time; state.time=ins.time;
let should_advance_body=match ins.instruction{ let should_advance_body=match ins.instruction{
//the body may as well be a quantum wave function //the body may as well be a quantum wave function
//as far as these instruction are concerned (they don't care where it is) //as far as these instruction are concerned (they don't care where it is)
PhysicsInputInstruction::SetSensitivity(..) Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(..)))
|PhysicsInputInstruction::Reset |Instruction::Other(OtherInstruction::Mode(_))
|PhysicsInputInstruction::Restart |Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetZoom(..)))
|PhysicsInputInstruction::Spawn(..) |Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::Idle))=>false,
|PhysicsInputInstruction::SetZoom(..)
|PhysicsInputInstruction::Idle=>false,
//these controls only update the body if you are on the ground //these controls only update the body if you are on the ground
PhysicsInputInstruction::SetNextMouse(..) Instruction::Mouse(_)
|PhysicsInputInstruction::ReplaceMouse(..) |Instruction::Other(OtherInstruction::SetControl(_))=>{
|PhysicsInputInstruction::SetMoveForward(..)
|PhysicsInputInstruction::SetMoveLeft(..)
|PhysicsInputInstruction::SetMoveBack(..)
|PhysicsInputInstruction::SetMoveRight(..)
|PhysicsInputInstruction::SetMoveUp(..)
|PhysicsInputInstruction::SetMoveDown(..)
|PhysicsInputInstruction::SetJump(..)=>{
match &state.move_state{ match &state.move_state{
MoveState::Fly MoveState::Fly
|MoveState::Water |MoveState::Water
@ -1776,30 +1758,30 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
}, },
//the body must be updated unconditionally //the body must be updated unconditionally
PhysicsInputInstruction::PracticeFly=>true, Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::PracticeFly))=>true,
}; };
if should_advance_body{ if should_advance_body{
state.body.advance_time(state.time); state.body.advance_time(state.time);
} }
//TODO: UNTAB
let mut b_refresh_walk_target=true; let mut b_refresh_walk_target=true;
match ins.instruction{ match ins.instruction{
PhysicsInputInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity, Instruction::Mouse(MouseInstruction::SetNextMouse(m))=>{
PhysicsInputInstruction::SetNextMouse(m)=>{
state.camera.move_mouse(state.input_state.mouse_delta()); state.camera.move_mouse(state.input_state.mouse_delta());
state.input_state.set_next_mouse(m); state.input_state.set_next_mouse(m);
}, },
PhysicsInputInstruction::ReplaceMouse(m0,m1)=>{ Instruction::Mouse(MouseInstruction::ReplaceMouse{m0,m1})=>{
state.camera.move_mouse(m0.pos-state.input_state.mouse.pos); state.camera.move_mouse(m0.pos-state.input_state.mouse.pos);
state.input_state.replace_mouse(m0,m1); state.input_state.replace_mouse(m0,m1);
}, },
PhysicsInputInstruction::SetMoveForward(s)=>state.input_state.set_control(Controls::MoveForward,s), Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(sensitivity)))=>state.camera.sensitivity=sensitivity,
PhysicsInputInstruction::SetMoveLeft(s)=>state.input_state.set_control(Controls::MoveLeft,s), Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveForward(s)))=>state.input_state.set_control(Controls::MoveForward,s),
PhysicsInputInstruction::SetMoveBack(s)=>state.input_state.set_control(Controls::MoveBackward,s), Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveLeft(s)))=>state.input_state.set_control(Controls::MoveLeft,s),
PhysicsInputInstruction::SetMoveRight(s)=>state.input_state.set_control(Controls::MoveRight,s), Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveBack(s)))=>state.input_state.set_control(Controls::MoveBackward,s),
PhysicsInputInstruction::SetMoveUp(s)=>state.input_state.set_control(Controls::MoveUp,s), Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveRight(s)))=>state.input_state.set_control(Controls::MoveRight,s),
PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s), Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveUp(s)))=>state.input_state.set_control(Controls::MoveUp,s),
PhysicsInputInstruction::SetJump(s)=>{ Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveDown(s)))=>state.input_state.set_control(Controls::MoveDown,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetJump(s)))=>{
state.input_state.set_control(Controls::Jump,s); state.input_state.set_control(Controls::Jump,s);
if let Some(walk_state)=state.move_state.get_walk_state(){ if let Some(walk_state)=state.move_state.get_walk_state(){
if let Some(jump_settings)=&state.style.jump{ if let Some(jump_settings)=&state.style.jump{
@ -1811,16 +1793,16 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::SetZoom(s)=>{ Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetZoom(s)))=>{
state.input_state.set_control(Controls::Zoom,s); state.input_state.set_control(Controls::Zoom,s);
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::Reset=>{ Instruction::Other(OtherInstruction::Mode(ModeInstruction::Reset))=>{
//totally reset physics state //totally reset physics state
state.reset_to_default(); state.reset_to_default();
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::Restart=>{ Instruction::Other(OtherInstruction::Mode(ModeInstruction::Restart))=>{
//teleport to start zone //teleport to start zone
let mode=data.modes.get_mode(state.mode_state.get_mode_id()); let mode=data.modes.get_mode(state.mode_state.get_mode_id());
let spawn_point=mode.and_then(|mode| let spawn_point=mode.and_then(|mode|
@ -1835,7 +1817,8 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::Air);
b_refresh_walk_target=false; b_refresh_walk_target=false;
} }
PhysicsInputInstruction::Spawn(mode_id,stage_id)=>{ // Spawn does not necessarily imply reset
Instruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(mode_id,stage_id)))=>{
//spawn at a particular stage //spawn at a particular stage
if let Some(mode)=data.modes.get_mode(mode_id){ if let Some(mode)=data.modes.get_mode(mode_id){
if let Some(stage)=mode.get_stage(stage_id){ if let Some(stage)=mode.get_stage(stage_id){
@ -1849,7 +1832,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::PracticeFly=>{ Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::PracticeFly))=>{
match &state.move_state{ match &state.move_state{
MoveState::Fly=>{ MoveState::Fly=>{
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::Air);
@ -1860,7 +1843,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },
PhysicsInputInstruction::Idle=>{ Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::Idle))=>{
//literally idle! //literally idle!
b_refresh_walk_target=false; b_refresh_walk_target=false;
}, },

View File

@ -1,249 +1,67 @@
use strafesnet_common::mouse::MouseState; use crate::graphics_worker::Instruction as GraphicsInstruction;
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::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner,Instruction as PhysicsInputInstruction}; use strafesnet_common::timer::Timer;
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::timer::{Scaled,Timer,TimerState};
use mouse_interpolator::MouseInterpolator;
pub struct FrameState{
pub body:crate::physics::Body,
pub camera:crate::physics::PhysicsCamera,
pub time:PhysicsTime,
}
#[derive(Debug)]
pub enum InputInstruction{
MoveMouse(glam::IVec2),
MoveRight(bool),
MoveUp(bool),
MoveBack(bool),
MoveLeft(bool),
MoveDown(bool),
MoveForward(bool),
Jump(bool),
Zoom(bool),
ResetAndRestart,
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
PracticeFly,
}
pub enum Instruction{ pub enum Instruction{
Input(InputInstruction), Input(SessionInputInstruction),
SetPaused(bool),
Render, Render,
Resize(winit::dpi::PhysicalSize<u32>), Resize(winit::dpi::PhysicalSize<u32>),
ChangeMap(strafesnet_common::map::CompleteMap), 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<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
last_mouse_time:PhysicsTime,
mouse_blocking:bool,
//"Simulation"
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
} const SESSION_INSTRUCTION_IDLE:SessionInstruction=SessionInstruction::Input(SessionInputInstruction::Other(strafesnet_common::physics::OtherOtherInstruction::Idle));
impl MouseInterpolator{
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<Instruction,SessionTimeInner>,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{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}),
});
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.last_mouse_time,pos:self.physics.get_next_mouse().pos},
MouseState{time:self.timer.time(ins.time),pos:m}
),
});
//delay physics execution until we have an interpolation target
self.mouse_blocking=true;
}
self.last_mouse_time=self.timer.time(ins.time);
}
fn push(&mut self,time:SessionTime,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,ins:&TimedInstruction<Instruction,SessionTimeInner>)->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(ins,m);
}
update_mouse_blocking=false;
},
&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::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 SetPaused: {e}");
}
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
}
}
/// must check if self.mouse_blocking==true before calling!
fn unblock_mouse(&mut self,time:SessionTime){
//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:SessionTime)->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 PhysicsTime::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
self.unblock_mouse(time);
true
}else{
false
}
}else{
//keep this up to date so that it can be used as a known-timestamp
//that the mouse was not moving when the mouse starts moving again
self.last_mouse_time=self.timer.time(time);
true
}
}
fn empty_queue(&mut self){
while let Some(instruction)=self.timeline.pop_front(){
self.physics.run_input_instruction(instruction);
}
}
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>){
let should_empty_queue=self.map_instruction(ins);
if should_empty_queue{
self.empty_queue();
}
}
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
FrameState{
body:self.physics.camera_body(),
camera:self.physics.camera(),
time:self.timer.time(time),
}
}
pub fn change_map(&mut self,time:SessionTime,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<CompleteMap>)
self.physics.generate_models(&map);
//use the standard input interface so the instructions are written out to bots
self.handle_instruction(&TimedInstruction{
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<'a>( pub fn new<'a>(
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>, mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
user_settings:crate::settings::UserSettings, user_settings:crate::settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{ )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
let physics=crate::physics::PhysicsContext::default(); let physics=crate::physics::PhysicsContext::default();
let mut interpolator=MouseInterpolator::new( let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO);
physics, let simulation=Simulation::new(timer,physics);
user_settings let mut session=Session::new(
user_settings,
simulation,
); );
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{ crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
interpolator.handle_instruction(&ins); // excruciating pain
macro_rules! run_session_instruction{
($time:expr,$instruction:expr)=>{
session.process_instruction(TimedInstruction{
time:$time,
instruction:$instruction,
});
};
}
macro_rules! run_graphics_worker_instruction{
($instruction:expr)=>{
graphics_worker.send($instruction).unwrap();
};
}
match ins.instruction{ match ins.instruction{
Instruction::Input(unbuffered_instruction)=>{
run_session_instruction!(ins.time,SessionInstruction::Input(unbuffered_instruction));
},
Instruction::SetPaused(paused)=>{
run_session_instruction!(ins.time,SessionInstruction::SetPaused(paused));
},
Instruction::Render=>{ Instruction::Render=>{
let frame_state=interpolator.get_frame_state(ins.time); run_session_instruction!(ins.time,SESSION_INSTRUCTION_IDLE);
graphics_worker.send(crate::graphics_worker::Instruction::Render(frame_state)).unwrap(); let frame_state=session.get_frame_state(ins.time);
run_graphics_worker_instruction!(GraphicsInstruction::Render(frame_state));
}, },
Instruction::Resize(size)=>{ Instruction::Resize(physical_size)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,interpolator.user_settings().clone())).unwrap(); run_session_instruction!(ins.time,SESSION_INSTRUCTION_IDLE);
let user_settings=session.user_settings().clone();
run_graphics_worker_instruction!(GraphicsInstruction::Resize(physical_size,user_settings));
}, },
Instruction::ChangeMap(map)=>{ Instruction::ChangeMap(complete_map)=>{
interpolator.change_map(ins.time,&map); run_session_instruction!(ins.time,SessionInstruction::ChangeMap(&complete_map));
graphics_worker.send(crate::graphics_worker::Instruction::ChangeMap(map)).unwrap(); run_graphics_worker_instruction!(GraphicsInstruction::ChangeMap(complete_map));
}, },
Instruction::Input(_)=>(),
Instruction::SetPaused(_)=>(),
} }
}) })
} }

View File

@ -0,0 +1,193 @@
use strafesnet_common::gameplay_modes::{ModeId,StageId};
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::{
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,Instruction as MouseInterpolatorInstruction};
use crate::settings::UserSettings;
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,
pub time:PhysicsTime,
}
pub struct Simulation{
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
}
impl Simulation{
pub const fn new(
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
)->Self{
Self{
timer,
physics,
}
}
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
FrameState{
body:self.physics.camera_body(),
camera:self.physics.camera(),
time:self.timer.time(time),
}
}
}
pub struct Replay{
last_instruction_id:usize,
instructions:Vec<PhysicsInputInstruction>,
simulation:Simulation,
}
impl Replay{
pub const fn new(
instructions:Vec<PhysicsInputInstruction>,
simulation:Simulation,
)->Self{
Self{
last_instruction_id:0,
instructions,
simulation,
}
}
}
pub struct Session{
user_settings:UserSettings,
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator,
//gui:GuiState
simulation:Simulation,
replays:Vec<Replay>,
}
impl Session{
pub fn new(
user_settings:UserSettings,
simulation:Simulation,
)->Self{
Self{
user_settings,
mouse_interpolator:MouseInterpolator::new(),
simulation,
replays:Vec::new(),
}
}
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
self.simulation.physics.generate_models(map);
}
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
self.simulation.get_frame_state(time)
}
pub fn user_settings(&self)->&UserSettings{
&self.user_settings
}
}
// mouseinterpolator consumes RawInputInstruction
// mouseinterpolator emits PhysicsInputInstruction
// mouseinterpolator consumes DoStep to move on to the next emitted instruction
// Session comsumes SessionInstruction -> forwards RawInputInstruction to mouseinterpolator
// Session consumes DoStep -> forwards DoStep to mouseinterpolator
// Session emits DoStep
impl InstructionConsumer<Instruction<'_>> for Session{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Self::TimeInner>){
macro_rules! run_mouse_interpolator_instruction{
($instruction:expr)=>{
self.mouse_interpolator.process_instruction(TimedInstruction{
time:ins.time,
instruction:TimedInstruction{
time:self.simulation.timer.time(ins.time),
instruction:$instruction,
},
});
};
}
match ins.instruction{
// send it down to MouseInterpolator with two timestamps, SessionTime and PhysicsTime
Instruction::Input(SessionInputInstruction::Mouse(pos))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::MoveMouse(pos));
},
Instruction::Input(SessionInputInstruction::SetControl(set_control_instruction))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::SetControl(set_control_instruction)));
},
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndRestart))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Restart)));
},
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(mode_id,spawn_id)))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id))));
},
Instruction::Input(SessionInputInstruction::Other(other_other_instruction))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(other_other_instruction)));
},
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);
}
Instruction::ChangeMap(complete_map)=>{
self.change_map(complete_map);
// ResetAndSpawn
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(ModeId::MAIN,StageId::FIRST))));
},
};
// run all buffered instruction produced
self.process_exhaustive(ins.time);
}
}
impl InstructionConsumer<StepInstruction> for Session{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<StepInstruction,Self::TimeInner>){
// ins.time ignored???
let ins_retimed=TimedInstruction{
time:self.simulation.timer.time(ins.time),
instruction:ins.instruction,
};
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins_retimed){
self.simulation.physics.run_input_instruction(instruction);
}
}
}
impl InstructionEmitter<StepInstruction> for Session{
type TimeInner=SessionTimeInner;
fn next_instruction(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,Self::TimeInner>>{
self.mouse_interpolator.next_instruction(time_limit)
}
}

View File

@ -1,4 +1,4 @@
use crate::window::WindowInstruction; use crate::window::Instruction;
use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::integer; use strafesnet_common::integer;
use strafesnet_common::session::TimeInner as SessionTimeInner; use strafesnet_common::session::TimeInner as SessionTimeInner;
@ -224,7 +224,7 @@ pub fn setup_and_start(title:&str){
let path=std::path::PathBuf::from(arg); let path=std::path::PathBuf::from(arg);
window_thread.send(TimedInstruction{ window_thread.send(TimedInstruction{
time:integer::Time::ZERO, time:integer::Time::ZERO,
instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)), instruction:Instruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)),
}).unwrap(); }).unwrap();
}; };
@ -235,7 +235,7 @@ pub fn setup_and_start(title:&str){
fn run_event_loop( fn run_event_loop(
event_loop:winit::event_loop::EventLoop<()>, event_loop:winit::event_loop::EventLoop<()>,
mut window_thread:crate::compat_worker::QNWorker<TimedInstruction<WindowInstruction,SessionTimeInner>>, mut window_thread:crate::compat_worker::QNWorker<TimedInstruction<Instruction,SessionTimeInner>>,
root_time:std::time::Instant root_time:std::time::Instant
)->Result<(),winit::error::EventLoopError>{ )->Result<(),winit::error::EventLoopError>{
event_loop.run(move |event,elwt|{ event_loop.run(move |event,elwt|{
@ -247,7 +247,7 @@ fn run_event_loop(
// }; // };
match event{ match event{
winit::event::Event::AboutToWait=>{ winit::event::Event::AboutToWait=>{
window_thread.send(TimedInstruction{time,instruction:WindowInstruction::RequestRedraw}).unwrap(); window_thread.send(TimedInstruction{time,instruction:Instruction::RequestRedraw}).unwrap();
} }
winit::event::Event::WindowEvent { winit::event::Event::WindowEvent {
event: event:
@ -259,7 +259,7 @@ fn run_event_loop(
winit::event::WindowEvent::Resized(size),//ignoring scale factor changed for now because mutex bruh winit::event::WindowEvent::Resized(size),//ignoring scale factor changed for now because mutex bruh
window_id:_, window_id:_,
} => { } => {
window_thread.send(TimedInstruction{time,instruction:WindowInstruction::Resize(size)}).unwrap(); window_thread.send(TimedInstruction{time,instruction:Instruction::Resize(size)}).unwrap();
} }
winit::event::Event::WindowEvent{event,..}=>match event{ winit::event::Event::WindowEvent{event,..}=>match event{
winit::event::WindowEvent::KeyboardInput{ winit::event::WindowEvent::KeyboardInput{
@ -275,17 +275,17 @@ fn run_event_loop(
elwt.exit(); elwt.exit();
} }
winit::event::WindowEvent::RedrawRequested=>{ winit::event::WindowEvent::RedrawRequested=>{
window_thread.send(TimedInstruction{time,instruction:WindowInstruction::Render}).unwrap(); window_thread.send(TimedInstruction{time,instruction:Instruction::Render}).unwrap();
} }
_=>{ _=>{
window_thread.send(TimedInstruction{time,instruction:WindowInstruction::WindowEvent(event)}).unwrap(); window_thread.send(TimedInstruction{time,instruction:Instruction::WindowEvent(event)}).unwrap();
} }
}, },
winit::event::Event::DeviceEvent{ winit::event::Event::DeviceEvent{
event, event,
.. ..
} => { } => {
window_thread.send(TimedInstruction{time,instruction:WindowInstruction::DeviceEvent(event)}).unwrap(); window_thread.send(TimedInstruction{time,instruction:Instruction::DeviceEvent(event)}).unwrap();
}, },
_=>{} _=>{}
} }

View File

@ -1,9 +1,10 @@
use crate::physics_worker::InputInstruction;
use strafesnet_common::integer;
use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner}; use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{OtherInstruction,OtherOtherInstruction,SetControlInstruction};
use crate::physics_worker::Instruction as PhysicsWorkerInstruction;
use crate::session::SessionInputInstruction;
pub enum WindowInstruction{ pub enum Instruction{
Resize(winit::dpi::PhysicalSize<u32>), Resize(winit::dpi::PhysicalSize<u32>),
WindowEvent(winit::event::WindowEvent), WindowEvent(winit::event::WindowEvent),
DeviceEvent(winit::event::DeviceEvent), DeviceEvent(winit::event::DeviceEvent),
@ -14,10 +15,10 @@ pub enum WindowInstruction{
//holds thread handles to dispatch to //holds thread handles to dispatch to
struct WindowContext<'a>{ struct WindowContext<'a>{
manual_mouse_lock:bool, manual_mouse_lock:bool,
mouse:strafesnet_common::mouse::MouseState<SessionTimeInner>,//std::sync::Arc<std::sync::Mutex<>> mouse_pos:glam::DVec2,
screen_size:glam::UVec2, screen_size:glam::UVec2,
window:&'a winit::window::Window, window:&'a winit::window::Window,
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<crate::physics_worker::Instruction,SessionTimeInner>>, physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTimeInner>>,
} }
impl WindowContext<'_>{ impl WindowContext<'_>{
@ -28,7 +29,7 @@ impl WindowContext<'_>{
match event{ match event{
winit::event::WindowEvent::DroppedFile(path)=>{ winit::event::WindowEvent::DroppedFile(path)=>{
match crate::file::load(path.as_path()){ match crate::file::load(path.as_path()){
Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ChangeMap(map)}).unwrap(), Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:PhysicsWorkerInstruction::ChangeMap(map)}).unwrap(),
Err(e)=>println!("Failed to load map: {e}"), Err(e)=>println!("Failed to load map: {e}"),
} }
}, },
@ -36,7 +37,7 @@ impl WindowContext<'_>{
//pause unpause //pause unpause
self.physics_thread.send(TimedInstruction{ self.physics_thread.send(TimedInstruction{
time, time,
instruction:crate::physics_worker::Instruction::SetPaused(!state), instruction:PhysicsWorkerInstruction::SetPaused(!state),
}).unwrap(); }).unwrap();
//recalculate pressed keys on focus //recalculate pressed keys on focus
}, },
@ -91,29 +92,29 @@ impl WindowContext<'_>{
}, },
(keycode,state)=>{ (keycode,state)=>{
let s=state.is_pressed(); let s=state.is_pressed();
if let Some(input_instruction)=match keycode{ if let Some(session_input_instruction)=match keycode{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(InputInstruction::Jump(s)), winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(s))),
winit::keyboard::Key::Character(key)=>match key.as_str(){ winit::keyboard::Key::Character(key)=>match key.as_str(){
"W"|"w"=>Some(InputInstruction::MoveForward(s)), "W"|"w"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveForward(s))),
"A"|"a"=>Some(InputInstruction::MoveLeft(s)), "A"|"a"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveLeft(s))),
"S"|"s"=>Some(InputInstruction::MoveBack(s)), "S"|"s"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveBack(s))),
"D"|"d"=>Some(InputInstruction::MoveRight(s)), "D"|"d"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveRight(s))),
"E"|"e"=>Some(InputInstruction::MoveUp(s)), "E"|"e"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveUp(s))),
"Q"|"q"=>Some(InputInstruction::MoveDown(s)), "Q"|"q"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveDown(s))),
"Z"|"z"=>Some(InputInstruction::Zoom(s)), "Z"|"z"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetZoom(s))),
"R"|"r"=>if s{ "R"|"r"=>s.then(||{
//mouse needs to be reset since the position is absolute //mouse needs to be reset since the position is absolute
self.mouse=strafesnet_common::mouse::MouseState::default(); self.mouse_pos=glam::DVec2::ZERO;
Some(InputInstruction::ResetAndRestart) SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndRestart)
}else{None}, }),
"F"|"f"=>if s{Some(InputInstruction::PracticeFly)}else{None}, "F"|"f"=>s.then_some(SessionInputInstruction::Other(OtherOtherInstruction::PracticeFly)),
_=>None, _=>None,
}, },
_=>None, _=>None,
}{ }{
self.physics_thread.send(TimedInstruction{ self.physics_thread.send(TimedInstruction{
time, time,
instruction:crate::physics_worker::Instruction::Input(input_instruction), instruction:PhysicsWorkerInstruction::Input(session_input_instruction),
}).unwrap(); }).unwrap();
} }
}, },
@ -126,7 +127,7 @@ impl WindowContext<'_>{
fn device_event(&mut self,time:SessionTime,event: winit::event::DeviceEvent){ fn device_event(&mut self,time:SessionTime,event: winit::event::DeviceEvent){
match event{ match event{
winit::event::DeviceEvent::MouseMotion{ winit::event::DeviceEvent::MouseMotion{
delta,//these (f64,f64) are integers on my machine delta,
}=>{ }=>{
if self.manual_mouse_lock{ if self.manual_mouse_lock{
match self.window.set_cursor_position(self.get_middle_of_screen()){ match self.window.set_cursor_position(self.get_middle_of_screen()){
@ -134,14 +135,10 @@ impl WindowContext<'_>{
Err(e)=>println!("Could not set cursor position: {:?}",e), Err(e)=>println!("Could not set cursor position: {:?}",e),
} }
} }
//do not step the physics because the mouse polling rate is higher than the physics can run. self.mouse_pos+=glam::dvec2(delta.0,delta.1);
//essentially the previous input will be overwritten until a true step runs
//which is fine because they run all the time.
let delta=glam::ivec2(delta.0 as i32,delta.1 as i32);
self.mouse.pos+=delta;
self.physics_thread.send(TimedInstruction{ self.physics_thread.send(TimedInstruction{
time, time,
instruction:crate::physics_worker::Instruction::Input(InputInstruction::MoveMouse(self.mouse.pos)), instruction:PhysicsWorkerInstruction::Input(SessionInputInstruction::Mouse(self.mouse_pos.as_ivec2())),
}).unwrap(); }).unwrap();
}, },
winit::event::DeviceEvent::MouseWheel { winit::event::DeviceEvent::MouseWheel {
@ -151,7 +148,7 @@ impl WindowContext<'_>{
if false{//self.physics.style.use_scroll{ if false{//self.physics.style.use_scroll{
self.physics_thread.send(TimedInstruction{ self.physics_thread.send(TimedInstruction{
time, time,
instruction:crate::physics_worker::Instruction::Input(InputInstruction::Jump(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(); }).unwrap();
} }
}, },
@ -162,7 +159,7 @@ impl WindowContext<'_>{
pub fn worker<'a>( pub fn worker<'a>(
window:&'a winit::window::Window, window:&'a winit::window::Window,
setup_context:crate::setup::SetupContext<'a>, setup_context:crate::setup::SetupContext<'a>,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction,SessionTimeInner>>{ )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
// WindowContextSetup::new // WindowContextSetup::new
let user_settings=crate::settings::read_user_settings(); let user_settings=crate::settings::read_user_settings();
@ -174,7 +171,7 @@ pub fn worker<'a>(
let graphics_thread=crate::graphics_worker::new(graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue); 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{ let mut window_context=WindowContext{
manual_mouse_lock:false, manual_mouse_lock:false,
mouse:strafesnet_common::mouse::MouseState::default(), mouse_pos:glam::DVec2::ZERO,
//make sure to update this!!!!! //make sure to update this!!!!!
screen_size, screen_size,
window, window,
@ -185,30 +182,30 @@ pub fn worker<'a>(
}; };
//WindowContextSetup::into_worker //WindowContextSetup::into_worker
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction,SessionTimeInner>|{ crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
match ins.instruction{ match ins.instruction{
WindowInstruction::RequestRedraw=>{ Instruction::RequestRedraw=>{
window_context.window.request_redraw(); window_context.window.request_redraw();
} }
WindowInstruction::WindowEvent(window_event)=>{ Instruction::WindowEvent(window_event)=>{
window_context.window_event(ins.time,window_event); window_context.window_event(ins.time,window_event);
}, },
WindowInstruction::DeviceEvent(device_event)=>{ Instruction::DeviceEvent(device_event)=>{
window_context.device_event(ins.time,device_event); window_context.device_event(ins.time,device_event);
}, },
WindowInstruction::Resize(size)=>{ Instruction::Resize(size)=>{
window_context.physics_thread.send( window_context.physics_thread.send(
TimedInstruction{ TimedInstruction{
time:ins.time, time:ins.time,
instruction:crate::physics_worker::Instruction::Resize(size) instruction:PhysicsWorkerInstruction::Resize(size)
} }
).unwrap(); ).unwrap();
} }
WindowInstruction::Render=>{ Instruction::Render=>{
window_context.physics_thread.send( window_context.physics_thread.send(
TimedInstruction{ TimedInstruction{
time:ins.time, time:ins.time,
instruction:crate::physics_worker::Instruction::Render instruction:PhysicsWorkerInstruction::Render
} }
).unwrap(); ).unwrap();
} }

View File

@ -190,7 +190,7 @@ mod test{
for _ in 0..5 { for _ in 0..5 {
let task = instruction::TimedInstruction{ let task = instruction::TimedInstruction{
time:strafesnet_common::physics::Time::ZERO, time:strafesnet_common::physics::Time::ZERO,
instruction:strafesnet_common::physics::Instruction::Idle, instruction:strafesnet_common::physics::Instruction::IDLE,
}; };
worker.send(task).unwrap(); worker.send(task).unwrap();
} }
@ -204,7 +204,7 @@ mod test{
// Send a new task // Send a new task
let task = instruction::TimedInstruction{ let task = instruction::TimedInstruction{
time:integer::Time::ZERO, time:integer::Time::ZERO,
instruction:strafesnet_common::physics::Instruction::Idle, instruction:strafesnet_common::physics::Instruction::IDLE,
}; };
worker.send(task).unwrap(); worker.send(task).unwrap();