forked from StrafesNET/strafe-client
Compare commits
13 Commits
master
...
mouse-inte
Author | SHA1 | Date | |
---|---|---|---|
ead0e33546 | |||
b376c03bde | |||
d7dc08092a | |||
d898d7cf67 | |||
7f68fd9b21 | |||
0b8a433640 | |||
9646804dcd | |||
9cd72d5809 | |||
9ecfe26a0c | |||
fbb2ba369c | |||
d1f78b6e18 | |||
8cade8134f | |||
3268d92d24 |
@ -1,5 +1,6 @@
|
|||||||
mod file;
|
mod file;
|
||||||
mod setup;
|
mod setup;
|
||||||
|
mod timer;
|
||||||
mod window;
|
mod window;
|
||||||
mod worker;
|
mod worker;
|
||||||
mod physics;
|
mod physics;
|
||||||
|
@ -104,7 +104,8 @@ impl InputState{
|
|||||||
&self.next_mouse
|
&self.next_mouse
|
||||||
}
|
}
|
||||||
fn set_next_mouse(&mut self,next_mouse:MouseState){
|
fn set_next_mouse(&mut self,next_mouse:MouseState){
|
||||||
(self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone());
|
//I like your functions magic language
|
||||||
|
self.mouse=std::mem::replace(&mut self.next_mouse,next_mouse);
|
||||||
}
|
}
|
||||||
fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){
|
fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){
|
||||||
(self.next_mouse,self.mouse)=(next_mouse,mouse);
|
(self.next_mouse,self.mouse)=(next_mouse,mouse);
|
||||||
|
@ -16,7 +16,10 @@ pub enum InputInstruction{
|
|||||||
PracticeFly,
|
PracticeFly,
|
||||||
}
|
}
|
||||||
pub enum Instruction{
|
pub enum Instruction{
|
||||||
Input(InputInstruction),
|
Passthrough(PassthroughInstruction),
|
||||||
|
Interpolate(InputInstruction),
|
||||||
|
}
|
||||||
|
pub enum PassthroughInstruction{
|
||||||
Render,
|
Render,
|
||||||
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
|
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
|
||||||
GenerateModels(strafesnet_common::map::CompleteMap),
|
GenerateModels(strafesnet_common::map::CompleteMap),
|
||||||
@ -24,140 +27,179 @@ pub enum Instruction{
|
|||||||
//Graphics(crate::graphics_worker::Instruction),
|
//Graphics(crate::graphics_worker::Instruction),
|
||||||
}
|
}
|
||||||
pub struct MouseInterpolator{
|
pub struct MouseInterpolator{
|
||||||
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
|
queue:std::collections::VecDeque<TimedInstruction<InputInstruction>>,
|
||||||
last_mouse_time:Time,
|
}
|
||||||
mouse_blocking:bool,
|
fn drain_queue(physics:&mut crate::physics::PhysicsContext,iterable:impl IntoIterator<Item=TimedInstruction<InputInstruction>>){
|
||||||
|
for ins in iterable{
|
||||||
|
let physics_input=match &ins.instruction{
|
||||||
|
InputInstruction::MoveMouse(_)=>panic!("Queue was confirmed to contain no MoveMouse events1"),
|
||||||
|
&InputInstruction::MoveForward(s)=>PhysicsInputInstruction::SetMoveForward(s),
|
||||||
|
&InputInstruction::MoveLeft(s)=>PhysicsInputInstruction::SetMoveLeft(s),
|
||||||
|
&InputInstruction::MoveBack(s)=>PhysicsInputInstruction::SetMoveBack(s),
|
||||||
|
&InputInstruction::MoveRight(s)=>PhysicsInputInstruction::SetMoveRight(s),
|
||||||
|
&InputInstruction::MoveUp(s)=>PhysicsInputInstruction::SetMoveUp(s),
|
||||||
|
&InputInstruction::MoveDown(s)=>PhysicsInputInstruction::SetMoveDown(s),
|
||||||
|
&InputInstruction::Jump(s)=>PhysicsInputInstruction::SetJump(s),
|
||||||
|
&InputInstruction::Zoom(s)=>PhysicsInputInstruction::SetZoom(s),
|
||||||
|
InputInstruction::Reset=>PhysicsInputInstruction::Reset,
|
||||||
|
InputInstruction::PracticeFly=>PhysicsInputInstruction::PracticeFly,
|
||||||
|
};
|
||||||
|
physics.run_input_instruction(TimedInstruction{
|
||||||
|
time:ins.time,
|
||||||
|
instruction:physics_input,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl MouseInterpolator{
|
impl MouseInterpolator{
|
||||||
fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,m:glam::IVec2){
|
fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:TimedInstruction<InputInstruction>){
|
||||||
if self.mouse_blocking{
|
//need to handle the case where mouse polling rate is less than 100hz
|
||||||
//tell the game state which is living in the past about its future
|
//also the whole thing is probably wrong lol
|
||||||
self.timeline.push_front(TimedInstruction{
|
let is_inserting_mouse_instruction=matches!(ins.instruction,InputInstruction::MoveMouse(_));
|
||||||
time:self.last_mouse_time,
|
self.queue.push_back(ins);
|
||||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}),
|
//We just pushed an element.
|
||||||
});
|
//The first element is guaranteed to exist.
|
||||||
}else{
|
let mut iter=self.queue.iter();
|
||||||
//mouse has just started moving again after being still for longer than 10ms.
|
//find a mouse input
|
||||||
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
|
'outer:loop{
|
||||||
self.timeline.push_front(TimedInstruction{
|
match iter.next(){
|
||||||
time:self.last_mouse_time,
|
Some(ins0)=>{
|
||||||
instruction:PhysicsInputInstruction::ReplaceMouse(
|
let physics_input=match &ins0.instruction{
|
||||||
MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos},
|
&InputInstruction::MoveMouse(mut mouse0)=>{
|
||||||
MouseState{time:ins.time,pos:m}
|
//mouse instruction found.
|
||||||
),
|
//enter a new loop with different behaviour
|
||||||
});
|
//we have to wait for the next mouse event
|
||||||
//delay physics execution until we have an interpolation target
|
//so there is a before and after interpolation target
|
||||||
self.mouse_blocking=true;
|
//write down ins0.time to appease the borrow checker
|
||||||
}
|
let mut t0=ins0.time;
|
||||||
self.last_mouse_time=ins.time;
|
'inner:loop{
|
||||||
}
|
match iter.next(){
|
||||||
/// returns the mapped physics input instruction
|
Some(ins1)=>match &ins1.instruction{
|
||||||
/// may or may not mutate internal state XD!
|
&InputInstruction::MoveMouse(mouse1)=>{
|
||||||
fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->Option<PhysicsInputInstruction>{
|
//we found two mouse events to interpolate between
|
||||||
match &ins.instruction{
|
let consume_count=self.queue.len()-iter.len()-1;//don't consume the mouse1 instruction
|
||||||
Instruction::Input(input_instruction)=>match input_instruction{
|
//fire off a mouse instruction
|
||||||
&InputInstruction::MoveMouse(m)=>{
|
physics.run_input_instruction(TimedInstruction{
|
||||||
self.push_mouse_instruction(physics,ins,m);
|
time:t0,
|
||||||
None
|
instruction:PhysicsInputInstruction::SetNextMouse(
|
||||||
|
MouseState{time:ins1.time,pos:mouse1}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
//update inner loop state
|
||||||
|
mouse0=mouse1;
|
||||||
|
t0=ins1.time;
|
||||||
|
//drain and handle the elements from the front
|
||||||
|
std::mem::drop(iter);
|
||||||
|
let mut hot_queue=self.queue.drain(0..consume_count);
|
||||||
|
hot_queue.next();
|
||||||
|
drain_queue(physics,hot_queue);
|
||||||
|
iter=self.queue.iter();
|
||||||
|
//keep looking for another mouse instruction in the inner loop
|
||||||
|
continue 'inner;
|
||||||
|
},
|
||||||
|
_=>if Time::from_millis(10)<ins1.time-t0{
|
||||||
|
//we have passed more than 10ms of instructions and have not seen a mouse event.
|
||||||
|
let consume_count=self.queue.len()-iter.len();
|
||||||
|
//run an event to extrapolate no movement from
|
||||||
|
let last_mouse=physics.get_next_mouse();
|
||||||
|
physics.run_input_instruction(TimedInstruction{
|
||||||
|
time:last_mouse.time,
|
||||||
|
instruction:PhysicsInputInstruction::SetNextMouse(
|
||||||
|
MouseState{time:ins1.time,pos:last_mouse.pos}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
//drop the iterator so we can consume the queue up to this point
|
||||||
|
std::mem::drop(iter);
|
||||||
|
//consume queue up to the scanned point
|
||||||
|
let mut hot_queue=self.queue.drain(0..consume_count);
|
||||||
|
//the first element is always the last mouse instruction (last_mouse above)
|
||||||
|
hot_queue.next();
|
||||||
|
drain_queue(physics,hot_queue);
|
||||||
|
//make a new iterator starting from the new beginning
|
||||||
|
//and continue looping like nothing happened
|
||||||
|
iter=self.queue.iter();
|
||||||
|
continue 'outer;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None=>{
|
||||||
|
if is_inserting_mouse_instruction{
|
||||||
|
//the mouse started moving again after being still for over 10ms.
|
||||||
|
//replace the entire mouse state
|
||||||
|
physics.run_input_instruction(TimedInstruction{
|
||||||
|
time:physics.get_next_mouse().time,
|
||||||
|
instruction:PhysicsInputInstruction::ReplaceMouse(
|
||||||
|
physics.get_next_mouse().clone(),
|
||||||
|
MouseState{time:t0,pos:mouse0}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&InputInstruction::MoveForward(s)=>PhysicsInputInstruction::SetMoveForward(s),
|
||||||
|
&InputInstruction::MoveLeft(s)=>PhysicsInputInstruction::SetMoveLeft(s),
|
||||||
|
&InputInstruction::MoveBack(s)=>PhysicsInputInstruction::SetMoveBack(s),
|
||||||
|
&InputInstruction::MoveRight(s)=>PhysicsInputInstruction::SetMoveRight(s),
|
||||||
|
&InputInstruction::MoveUp(s)=>PhysicsInputInstruction::SetMoveUp(s),
|
||||||
|
&InputInstruction::MoveDown(s)=>PhysicsInputInstruction::SetMoveDown(s),
|
||||||
|
&InputInstruction::Jump(s)=>PhysicsInputInstruction::SetJump(s),
|
||||||
|
&InputInstruction::Zoom(s)=>PhysicsInputInstruction::SetZoom(s),
|
||||||
|
InputInstruction::Reset=>PhysicsInputInstruction::Reset,
|
||||||
|
InputInstruction::PracticeFly=>PhysicsInputInstruction::PracticeFly,
|
||||||
|
};
|
||||||
|
//handle each event immediately, we are not waiting for mouse
|
||||||
|
physics.run_input_instruction(TimedInstruction{
|
||||||
|
time:ins0.time,
|
||||||
|
instruction:physics_input,
|
||||||
|
});
|
||||||
|
//drop it and pop it! consume one element and continue the loop
|
||||||
|
std::mem::drop(iter);
|
||||||
|
self.queue.pop_front();
|
||||||
|
iter=self.queue.iter();
|
||||||
},
|
},
|
||||||
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
|
None=>{
|
||||||
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
|
//if mouse0 is never found and the loop ends, we can drain the entire queue
|
||||||
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
|
//because we are not waiting for mouse events.
|
||||||
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)),
|
drain_queue(physics,self.queue.drain(..));
|
||||||
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)),
|
break 'outer;
|
||||||
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)),
|
}
|
||||||
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)),
|
|
||||||
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)),
|
|
||||||
InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset),
|
|
||||||
InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly),
|
|
||||||
},
|
|
||||||
Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle),
|
|
||||||
Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle),
|
|
||||||
Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle),
|
|
||||||
Instruction::Render=>Some(PhysicsInputInstruction::Idle),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn update_mouse_blocking(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->bool{
|
|
||||||
if self.mouse_blocking{
|
|
||||||
//assume the mouse has stopped moving after 10ms.
|
|
||||||
//shitty mice are 125Hz which is 8ms so this should cover that.
|
|
||||||
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
|
|
||||||
//so mouse events are probably not handled separately from drawing and fire right before it :(
|
|
||||||
if Time::from_millis(10)<ins.time-physics.get_next_mouse().time{
|
|
||||||
//push an event to extrapolate no movement from
|
|
||||||
self.timeline.push_front(TimedInstruction{
|
|
||||||
time:self.last_mouse_time,
|
|
||||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:physics.get_next_mouse().pos}),
|
|
||||||
});
|
|
||||||
self.last_mouse_time=ins.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;
|
|
||||||
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=ins.time;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// returns whether or not to empty the instruction queue
|
|
||||||
fn handle_physics_input(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,phys_input_option:Option<PhysicsInputInstruction>)->bool{
|
|
||||||
if let Some(phys_input)=phys_input_option{
|
|
||||||
//non-mouse event
|
|
||||||
self.timeline.push_back(TimedInstruction{
|
|
||||||
time:ins.time,
|
|
||||||
instruction:phys_input,
|
|
||||||
});
|
|
||||||
|
|
||||||
//this returns the bool for us
|
|
||||||
self.update_mouse_blocking(physics,ins)
|
|
||||||
}else{
|
|
||||||
//mouse event
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn empty_queue(&mut self,physics:&mut crate::physics::PhysicsContext){
|
|
||||||
while let Some(instruction)=self.timeline.pop_front(){
|
|
||||||
physics.run_input_instruction(instruction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>){
|
|
||||||
let physics_input_option=self.map_instruction(physics,ins);
|
|
||||||
let should_empty_queue=self.handle_physics_input(physics,ins,physics_input_option);
|
|
||||||
if should_empty_queue{
|
|
||||||
self.empty_queue(physics);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{
|
pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{
|
||||||
let mut interpolator=MouseInterpolator{
|
let mut interpolator=MouseInterpolator{
|
||||||
mouse_blocking:true,
|
queue:std::collections::VecDeque::new(),
|
||||||
last_mouse_time:physics.get_next_mouse().time,
|
|
||||||
timeline:std::collections::VecDeque::new(),
|
|
||||||
};
|
};
|
||||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
|
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
|
||||||
interpolator.handle_instruction(&mut physics,&ins);
|
let passthrough_instruction=match ins.instruction{
|
||||||
match ins.instruction{
|
Instruction::Passthrough(passthrough_instruction)=>passthrough_instruction,
|
||||||
Instruction::Render=>{
|
Instruction::Interpolate(input_instruction)=>{
|
||||||
|
interpolator.handle_instruction(&mut physics,TimedInstruction{
|
||||||
|
instruction:input_instruction,
|
||||||
|
time:ins.time,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match passthrough_instruction{
|
||||||
|
PassthroughInstruction::Render=>{
|
||||||
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap();
|
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap();
|
||||||
},
|
},
|
||||||
Instruction::Resize(size,user_settings)=>{
|
PassthroughInstruction::Resize(size,user_settings)=>{
|
||||||
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
|
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
|
||||||
},
|
},
|
||||||
Instruction::GenerateModels(map)=>{
|
PassthroughInstruction::GenerateModels(map)=>{
|
||||||
physics.generate_models(&map);
|
physics.generate_models(&map);
|
||||||
physics.spawn();
|
physics.spawn();
|
||||||
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
|
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
|
||||||
},
|
},
|
||||||
Instruction::ClearModels=>{
|
PassthroughInstruction::ClearModels=>{
|
||||||
physics.clear();
|
physics.clear();
|
||||||
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
|
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
|
||||||
},
|
},
|
||||||
_=>(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
179
src/timer.rs
Normal file
179
src/timer.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
use strafesnet_common::integer::{Time,Ratio64};
|
||||||
|
|
||||||
|
pub trait TimerState:Copy{
|
||||||
|
fn get_time(&self,time:Time)->Time;
|
||||||
|
fn set_time(&mut self,time:Time,new_time:Time);
|
||||||
|
fn get_offset(&self)->Time;
|
||||||
|
fn set_offset(&mut self,offset:Time);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
|
struct Scaled{
|
||||||
|
scale:Ratio64,
|
||||||
|
offset:Time,
|
||||||
|
}
|
||||||
|
impl Scaled{
|
||||||
|
fn scale(&self,time:Time)->Time{
|
||||||
|
Time::raw(self.scale.mul_int(time.get()))
|
||||||
|
}
|
||||||
|
fn get_scale(&self)->Ratio64{
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
fn set_scale(&mut self,time:Time,new_scale:Ratio64){
|
||||||
|
let new_time=self.get_time(time);
|
||||||
|
self.scale=new_scale;
|
||||||
|
self.set_time(time,new_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl TimerState for Scaled{
|
||||||
|
fn get_time(&self,time:Time)->Time{
|
||||||
|
self.scale(time)+self.offset
|
||||||
|
}
|
||||||
|
fn set_time(&mut self,time:Time,new_time:Time){
|
||||||
|
self.offset=new_time-self.scale(time);
|
||||||
|
}
|
||||||
|
fn get_offset(&self)->Time{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
fn set_offset(&mut self,offset:Time){
|
||||||
|
self.offset=offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
|
struct Realtime{
|
||||||
|
offset:Time,
|
||||||
|
}
|
||||||
|
impl TimerState for Realtime{
|
||||||
|
fn get_time(&self,time:Time)->Time{
|
||||||
|
time+self.offset
|
||||||
|
}
|
||||||
|
fn set_time(&mut self,time:Time,new_time:Time){
|
||||||
|
self.offset=new_time-time;
|
||||||
|
}
|
||||||
|
fn get_offset(&self)->Time{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
fn set_offset(&mut self,offset:Time){
|
||||||
|
self.offset=offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Debug)]
|
||||||
|
pub struct Timer<T>{
|
||||||
|
state:T,
|
||||||
|
paused:bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer<Realtime>{
|
||||||
|
pub fn realtime(offset:Time)->Self{
|
||||||
|
Self{
|
||||||
|
state:Realtime{offset},
|
||||||
|
paused:false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn realtime_paused(offset:Time)->Self{
|
||||||
|
Self{
|
||||||
|
state:Realtime{offset},
|
||||||
|
paused:true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
AlreadyPaused,
|
||||||
|
AlreadyUnpaused,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
|
||||||
|
impl Timer<Scaled>{
|
||||||
|
pub fn scaled(scale:Ratio64,offset:Time)->Self{
|
||||||
|
Self{
|
||||||
|
state:Scaled{scale,offset},
|
||||||
|
paused:false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn scaled_paused(scale:Ratio64,offset:Time)->Self{
|
||||||
|
Self{
|
||||||
|
state:Scaled{scale,offset},
|
||||||
|
paused:true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_scale(&mut self)->Ratio64{
|
||||||
|
self.state.get_scale()
|
||||||
|
}
|
||||||
|
pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
|
||||||
|
self.state.set_scale(time,new_scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:TimerState> Timer<T>{
|
||||||
|
pub fn time(&self,time:Time)->Time{
|
||||||
|
match self.paused{
|
||||||
|
true=>self.state.get_offset(),
|
||||||
|
false=>self.state.get_time(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:Time,new_time:Time){
|
||||||
|
match self.paused{
|
||||||
|
true=>self.state.set_offset(new_time),
|
||||||
|
false=>self.state.set_time(time,new_time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pause(&mut self,time:Time)->Result<(),Error>{
|
||||||
|
match self.paused{
|
||||||
|
true=>Err(Error::AlreadyPaused),
|
||||||
|
false=>{
|
||||||
|
let new_time=self.time(time);
|
||||||
|
self.state.set_offset(new_time);
|
||||||
|
self.paused=true;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn unpause(&mut self,time:Time)->Result<(),Error>{
|
||||||
|
match self.paused{
|
||||||
|
true=>{
|
||||||
|
let new_time=self.time(time);
|
||||||
|
self.state.set_time(time,new_time);
|
||||||
|
self.paused=false;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
false=>Err(Error::AlreadyUnpaused),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test{
|
||||||
|
use super::{Time,Timer,Error};
|
||||||
|
macro_rules! sec {
|
||||||
|
($s: expr) => {
|
||||||
|
Time::from_secs($s)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_timer()->Result<(),Error>{
|
||||||
|
//create a paused timer that reads 0s
|
||||||
|
let mut timer=Timer::realtime_paused(sec!(0));
|
||||||
|
//the paused timer at 1 second should read 0s
|
||||||
|
assert_eq!(timer.time(sec!(1)),sec!(0));
|
||||||
|
|
||||||
|
//unpause it after one second
|
||||||
|
timer.unpause(sec!(1))?;
|
||||||
|
//the timer at 6 seconds should read 5s
|
||||||
|
assert_eq!(timer.time(sec!(6)),sec!(5));
|
||||||
|
|
||||||
|
//pause the timer after 11 seconds
|
||||||
|
timer.pause(sec!(11))?;
|
||||||
|
//the paused timer at 20 seconds should read 10s
|
||||||
|
assert_eq!(timer.time(sec!(20)),sec!(10));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -29,8 +29,12 @@ impl WindowContext<'_>{
|
|||||||
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)=>{
|
Ok(map)=>{
|
||||||
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ClearModels}).unwrap();
|
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::Passthrough(
|
||||||
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::GenerateModels(map)}).unwrap();
|
crate::physics_worker::PassthroughInstruction::ClearModels
|
||||||
|
)}).unwrap();
|
||||||
|
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::Passthrough(
|
||||||
|
crate::physics_worker::PassthroughInstruction::GenerateModels(map)
|
||||||
|
)}).unwrap();
|
||||||
},
|
},
|
||||||
Err(e)=>println!("Failed to load map: {e}"),
|
Err(e)=>println!("Failed to load map: {e}"),
|
||||||
}
|
}
|
||||||
@ -115,7 +119,7 @@ impl WindowContext<'_>{
|
|||||||
}{
|
}{
|
||||||
self.physics_thread.send(TimedInstruction{
|
self.physics_thread.send(TimedInstruction{
|
||||||
time,
|
time,
|
||||||
instruction:crate::physics_worker::Instruction::Input(input_instruction),
|
instruction:crate::physics_worker::Instruction::Interpolate(input_instruction),
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -143,7 +147,7 @@ impl WindowContext<'_>{
|
|||||||
self.mouse.pos+=delta;
|
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:crate::physics_worker::Instruction::Interpolate(InputInstruction::MoveMouse(self.mouse.pos)),
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
},
|
},
|
||||||
winit::event::DeviceEvent::MouseWheel {
|
winit::event::DeviceEvent::MouseWheel {
|
||||||
@ -153,7 +157,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:crate::physics_worker::Instruction::Interpolate(InputInstruction::Jump(true)),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +222,9 @@ impl<'a> WindowContextSetup<'a>{
|
|||||||
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,window_context.user_settings.clone())
|
instruction:crate::physics_worker::Instruction::Passthrough(
|
||||||
|
crate::physics_worker::PassthroughInstruction::Resize(size,window_context.user_settings.clone())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
@ -226,7 +232,9 @@ impl<'a> WindowContextSetup<'a>{
|
|||||||
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:crate::physics_worker::Instruction::Passthrough(
|
||||||
|
crate::physics_worker::PassthroughInstruction::Render
|
||||||
|
)
|
||||||
}
|
}
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user