forked from StrafesNET/strafe-client
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
64e1e762a1 | |||
ad862ae8c9 | |||
0ee17ac3d9 |
@ -7,6 +7,7 @@ use instruction::{TimedInstruction, InstructionConsumer};
|
||||
mod bvh;
|
||||
mod aabb;
|
||||
mod model;
|
||||
mod timers;
|
||||
mod zeroes;
|
||||
mod worker;
|
||||
mod physics;
|
||||
|
@ -42,12 +42,13 @@ pub enum InputInstruction {
|
||||
Jump(bool),
|
||||
Zoom(bool),
|
||||
Reset,
|
||||
SetPaused(bool),
|
||||
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.
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Body {
|
||||
position: glam::Vec3,//I64 where 2^32 = 1 u
|
||||
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
|
||||
@ -453,6 +454,7 @@ impl PhysicsState {
|
||||
pub fn into_worker(mut self)->crate::worker::CompatWorker<TimedInstruction<InputInstruction>,PhysicsOutputState,Box<dyn FnMut(TimedInstruction<InputInstruction>)->PhysicsOutputState>>{
|
||||
let mut mouse_blocking=true;
|
||||
let mut last_mouse_time=self.next_mouse.time;
|
||||
let mut simulation_timer=crate::timers::UnscaledTimer::unpaused();
|
||||
let mut timeline=std::collections::VecDeque::new();
|
||||
crate::worker::CompatWorker::new(self.output(),Box::new(move |ins:TimedInstruction<InputInstruction>|{
|
||||
if if let Some(phys_input)=match ins.instruction{
|
||||
@ -460,17 +462,17 @@ impl PhysicsState {
|
||||
if mouse_blocking{
|
||||
//tell the game state which is living in the past about its future
|
||||
timeline.push_front(TimedInstruction{
|
||||
time:last_mouse_time,
|
||||
time:simulation_timer.time(last_mouse_time),
|
||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{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
|
||||
timeline.push_front(TimedInstruction{
|
||||
time:last_mouse_time,
|
||||
time:simulation_timer.time(last_mouse_time),
|
||||
instruction:PhysicsInputInstruction::ReplaceMouse(
|
||||
MouseState{time:last_mouse_time,pos:self.next_mouse.pos},
|
||||
MouseState{time:ins.time,pos:m}
|
||||
MouseState{time:simulation_timer.time(last_mouse_time),pos:self.next_mouse.pos},
|
||||
MouseState{time:simulation_timer.time(ins.time),pos:m}
|
||||
),
|
||||
});
|
||||
//delay physics execution until we have an interpolation target
|
||||
@ -479,6 +481,14 @@ impl PhysicsState {
|
||||
last_mouse_time=ins.time;
|
||||
None
|
||||
},
|
||||
InputInstruction::SetPaused(s)=>{
|
||||
if s{
|
||||
simulation_timer.pause(ins.time);
|
||||
}else{
|
||||
simulation_timer.unpause(ins.time);
|
||||
}
|
||||
Some(PhysicsInputInstruction::Idle)
|
||||
}
|
||||
InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
|
||||
InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
|
||||
InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
|
||||
@ -492,7 +502,7 @@ impl PhysicsState {
|
||||
}{
|
||||
//non-mouse event
|
||||
timeline.push_back(TimedInstruction{
|
||||
time:ins.time,
|
||||
time:simulation_timer.time(ins.time),
|
||||
instruction:phys_input,
|
||||
});
|
||||
|
||||
@ -504,7 +514,7 @@ impl PhysicsState {
|
||||
if 10_000_000<ins.time-self.next_mouse.time{
|
||||
//push an event to extrapolate no movement from
|
||||
timeline.push_front(TimedInstruction{
|
||||
time:last_mouse_time,
|
||||
time:simulation_timer.time(last_mouse_time),
|
||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:self.next_mouse.pos}),
|
||||
});
|
||||
last_mouse_time=ins.time;
|
||||
@ -526,9 +536,10 @@ impl PhysicsState {
|
||||
}{
|
||||
//empty queue
|
||||
while let Some(instruction)=timeline.pop_front(){
|
||||
self.run(instruction.time);
|
||||
let simulation_time=simulation_timer.time(instruction.time);
|
||||
self.run(simulation_time);
|
||||
self.process_instruction(TimedInstruction{
|
||||
time:instruction.time,
|
||||
time:simulation_time,
|
||||
instruction:PhysicsInstruction::Input(instruction.instruction),
|
||||
});
|
||||
}
|
||||
|
237
src/timers.rs
Normal file
237
src/timers.rs
Normal file
@ -0,0 +1,237 @@
|
||||
type TIME=crate::physics::TIME;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Timescale{
|
||||
num:i64,
|
||||
den:std::num::NonZeroU64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Paused{}
|
||||
#[derive(Clone)]
|
||||
pub struct Unpaused{}
|
||||
#[derive(Clone)]
|
||||
pub struct PausedScaled{scale:Timescale}
|
||||
#[derive(Clone)]
|
||||
pub struct UnpausedScaled{scale:Timescale}
|
||||
|
||||
pub trait TimerState{}
|
||||
impl TimerState for Paused{}
|
||||
impl TimerState for Unpaused{}
|
||||
impl TimerState for PausedScaled{}
|
||||
impl TimerState for UnpausedScaled{}
|
||||
|
||||
pub trait IsPaused{}
|
||||
impl IsPaused for Paused{}
|
||||
impl IsPaused for PausedScaled{}
|
||||
|
||||
pub trait IsUnpaused{}
|
||||
impl IsUnpaused for Unpaused{}
|
||||
impl IsUnpaused for UnpausedScaled{}
|
||||
|
||||
pub trait IsScaled{}
|
||||
impl IsScaled for PausedScaled{}
|
||||
impl IsScaled for UnpausedScaled{}
|
||||
|
||||
pub trait IsUnscaled{}
|
||||
impl IsUnscaled for Paused{}
|
||||
impl IsUnscaled for Unpaused{}
|
||||
|
||||
//scaled timer wrapper
|
||||
enum Scaled{
|
||||
Paused(Timer<PausedScaled>),
|
||||
Unpaused(Timer<UnpausedScaled>),
|
||||
}
|
||||
pub struct ScaledTimer{
|
||||
timer:Scaled,
|
||||
}
|
||||
impl ScaledTimer{
|
||||
pub fn unpaused()->Self{
|
||||
Self{
|
||||
timer:Scaled::Unpaused(unpaused_scaled(Timescale{num:1,den:std::num::NonZeroU64::new(1).unwrap()}))
|
||||
}
|
||||
}
|
||||
pub fn time(&self,time:TIME)->TIME{
|
||||
match &self.timer{
|
||||
Scaled::Paused(timer)=>timer.time(),
|
||||
Scaled::Unpaused(timer)=>timer.time(time),
|
||||
}
|
||||
}
|
||||
pub fn pause(&mut self,time:TIME){
|
||||
match &self.timer{
|
||||
Scaled::Paused(_)=>(),
|
||||
Scaled::Unpaused(timer)=>self.timer=Scaled::Paused(timer.clone().pause(time)),
|
||||
};
|
||||
}
|
||||
pub fn unpause(&mut self,time:TIME){
|
||||
match &self.timer{
|
||||
Scaled::Paused(timer)=>self.timer=Scaled::Unpaused(timer.clone().unpause(time)),
|
||||
Scaled::Unpaused(_)=>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//unscaled timer wrapper
|
||||
enum Unscaled{
|
||||
Paused(Timer<Paused>),
|
||||
Unpaused(Timer<Unpaused>),
|
||||
}
|
||||
pub struct UnscaledTimer{
|
||||
timer:Unscaled,
|
||||
}
|
||||
|
||||
impl UnscaledTimer{
|
||||
pub fn unpaused()->Self{
|
||||
Self{
|
||||
timer:Unscaled::Unpaused(unpaused())
|
||||
}
|
||||
}
|
||||
pub fn time(&self,time:TIME)->TIME{
|
||||
match &self.timer{
|
||||
Unscaled::Paused(timer)=>timer.time(),
|
||||
Unscaled::Unpaused(timer)=>timer.time(time),
|
||||
}
|
||||
}
|
||||
pub fn pause(&mut self,time:TIME){
|
||||
match &self.timer{
|
||||
Unscaled::Paused(_)=>(),
|
||||
Unscaled::Unpaused(timer)=>self.timer=Unscaled::Paused(timer.clone().pause(time)),
|
||||
};
|
||||
}
|
||||
pub fn unpause(&mut self,time:TIME){
|
||||
match &self.timer{
|
||||
Unscaled::Paused(timer)=>self.timer=Unscaled::Unpaused(timer.clone().unpause(time)),
|
||||
Unscaled::Unpaused(_)=>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Timer<State:TimerState>{
|
||||
offset:crate::physics::TIME,
|
||||
state:State,
|
||||
}
|
||||
|
||||
fn get_offset(time:TIME,write_time:TIME)->TIME{
|
||||
write_time-time
|
||||
}
|
||||
fn get_offset_scaled(time:TIME,write_time:TIME,scale:&Timescale)->TIME{
|
||||
write_time-time*scale.num/scale.den.get() as i64
|
||||
}
|
||||
|
||||
fn paused()->Timer<Paused>{
|
||||
Timer{
|
||||
offset:0,
|
||||
state:Paused{},
|
||||
}
|
||||
}
|
||||
fn unpaused()->Timer<Unpaused>{
|
||||
Timer{
|
||||
offset:0,
|
||||
state:Unpaused{},
|
||||
}
|
||||
}
|
||||
fn paused_scaled(scale:Timescale)->Timer<PausedScaled>{
|
||||
Timer{
|
||||
offset:0,
|
||||
state:PausedScaled{scale},
|
||||
}
|
||||
}
|
||||
fn unpaused_scaled(scale:Timescale)->Timer<UnpausedScaled>{
|
||||
Timer{
|
||||
offset:0,
|
||||
state:UnpausedScaled{scale},
|
||||
}
|
||||
}
|
||||
impl Timer<Paused>{
|
||||
pub fn time(&self)->TIME{
|
||||
self.offset
|
||||
}
|
||||
pub fn unpause(self,time:TIME)->Timer<Unpaused>{
|
||||
Timer{
|
||||
offset:get_offset(time,self.time()),
|
||||
state:Unpaused{},
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||
self.offset=get_offset(time,write_time);
|
||||
}
|
||||
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||
Timer{
|
||||
offset:get_offset_scaled(time,self.time(),&scale),
|
||||
state:PausedScaled{scale},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Timer<Unpaused>{
|
||||
pub fn time(&self,time:TIME)->TIME{
|
||||
self.offset+time
|
||||
}
|
||||
pub fn pause(self,time:TIME)->Timer<Paused>{
|
||||
Timer{
|
||||
offset:self.time(time),
|
||||
state:Paused{},
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||
self.offset=get_offset(time,write_time);
|
||||
}
|
||||
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||
Timer{
|
||||
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||
state:UnpausedScaled{scale},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Timer<PausedScaled>{
|
||||
pub fn time(&self)->TIME{
|
||||
self.offset
|
||||
}
|
||||
pub fn unpause(self,time:TIME)->Timer<UnpausedScaled>{
|
||||
Timer{
|
||||
offset:get_offset_scaled(time,self.time(),&self.state.scale),
|
||||
state:UnpausedScaled{scale:self.state.scale},
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||
}
|
||||
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||
Timer{
|
||||
offset:get_offset_scaled(time,self.time(),&scale),
|
||||
state:PausedScaled{scale},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Timer<UnpausedScaled>{
|
||||
pub fn time(&self,time:TIME)->TIME{
|
||||
self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64
|
||||
}
|
||||
pub fn pause(self,time:TIME)->Timer<PausedScaled>{
|
||||
Timer{
|
||||
offset:self.time(time),
|
||||
state:PausedScaled{scale:self.state.scale},
|
||||
}
|
||||
}
|
||||
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||
}
|
||||
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||
Timer{
|
||||
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||
//self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64-time*scale.num/scale.den.get() as i64
|
||||
state:UnpausedScaled{scale},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timer_unscaled(){
|
||||
const ONE_SECOND:TIME=1_000_000_000;
|
||||
let run_prepare=paused();
|
||||
|
||||
let run_start=run_prepare.unpause(ONE_SECOND);
|
||||
let run_finish=run_start.pause(11*ONE_SECOND);
|
||||
|
||||
assert_eq!(run_finish.time(),10*ONE_SECOND);
|
||||
}
|
Loading…
Reference in New Issue
Block a user