introduce PlaybackTime to abstract bot file timestamps

This commit is contained in:
2026-02-26 08:31:11 -08:00
parent d03f84c893
commit 29e49587ff
5 changed files with 54 additions and 55 deletions

View File

@@ -1,11 +1,14 @@
use strafesnet_common::timer::{TimerFixed,Realtime,Unpaused};
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner};
use strafesnet_roblox_bot_file::v0;
use strafesnet_common::physics::{Time as PhysicsTime};
use crate::head::{Time as PlaybackTime,TimeInner as PlaybackTimeInner};
/// A loaded bot file.
pub struct CompleteBot{
//Instructions
timelines:v0::Block,
time_base:PhysicsTime,
timer:TimerFixed<Realtime<PlaybackTimeInner,PhysicsTimeInner>,Unpaused>,
duration:PhysicsTime,
}
impl CompleteBot{
@@ -13,16 +16,16 @@ impl CompleteBot{
pub fn new(
timelines:v0::Block,
)->Self{
let first=timelines.output_events.first().unwrap();
let last=timelines.output_events.last().unwrap();
let start=crate::time::from_float(timelines.output_events.first().unwrap().time).unwrap();
let end=crate::time::from_float(timelines.output_events.last().unwrap().time).unwrap();
Self{
time_base:crate::time::from_float(first.time).unwrap(),
duration:crate::time::from_float(last.time-first.time).unwrap(),
timer:TimerFixed::new(PlaybackTime::ZERO,start),
duration:end-start,
timelines,
}
}
pub const fn time_base(&self)->PhysicsTime{
self.time_base
pub fn time(&self,time:PlaybackTime)->PhysicsTime{
self.timer.time(time)
}
pub const fn duration(&self)->PhysicsTime{
self.duration

View File

@@ -1,7 +1,7 @@
use glam::Vec3Swizzles;
use strafesnet_common::timer::{Scaled,Timer,TimerState};
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner};
use strafesnet_common::physics::{Time as PhysicsTime};
use strafesnet_roblox_bot_file::v0::{EventType,Head,Timed};
use crate::bot::CompleteBot;
@@ -11,11 +11,14 @@ fn vector3_to_glam(v:&strafesnet_roblox_bot_file::v0::Vector3)->glam::Vec3{
glam::vec3(v.x,v.y,v.z)
}
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
pub enum TimeInner{}
pub type Time=strafesnet_common::integer::Time<TimeInner>;
/// A playback context. Advance time and then generate a camera position to pass to the renderer.
pub struct PlaybackHead{
head:Head,
loop_offset:PhysicsTime,
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
timer:Timer<Scaled<SessionTimeInner,TimeInner>>,
state:PlaybackState,
}
const HEAD_NO_CRASH:Head={
@@ -26,10 +29,9 @@ const HEAD_NO_CRASH:Head={
};
impl PlaybackHead{
pub fn new(time:SessionTime)->Self{
let timer=Timer::unpaused(time,PhysicsTime::ZERO);
let timer=Timer::unpaused(time,Time::ZERO);
Self{
head:HEAD_NO_CRASH,
loop_offset:PhysicsTime::ZERO,
timer,
state:PlaybackState::new(),
}
@@ -37,8 +39,8 @@ impl PlaybackHead{
pub const fn state(&self)->&PlaybackState{
&self.state
}
pub fn time(&self,bot:&CompleteBot,time:SessionTime)->PhysicsTime{
bot.time_base()+self.timer.time(time)+self.loop_offset
pub fn time(&self,time:SessionTime)->Time{
self.timer.time(time)
}
pub fn next_event(&self,bot:&CompleteBot)->Option<Timed<EventType>>{
self.head.next_event(bot.timelines())
@@ -50,31 +52,16 @@ impl PlaybackHead{
pub fn set_paused(&mut self,time:SessionTime,paused:bool){
_=self.timer.set_paused(time,paused);
}
pub fn seek_to(&mut self,time:SessionTime,new_time:PhysicsTime){
pub fn set_time(&mut self,time:SessionTime,new_time:Time){
self.timer.set_time(time,new_time);
self.loop_offset=PhysicsTime::ZERO;
// reset head
self.head=HEAD_NO_CRASH;
}
pub fn seek_backward(&mut self,time_offset:SessionTime){
let (mut state,paused)=self.timer.clone().into_state();
let offset=state.get_time(-time_offset).coerce();
state.set_offset(offset);
self.timer=Timer::from_state(state,paused);
// reset head
self.head=HEAD_NO_CRASH;
}
pub fn seek_forward(&mut self,time_offset:SessionTime){
let (mut state,paused)=self.timer.clone().into_state();
let offset=state.get_time(time_offset).coerce();
state.set_offset(offset);
self.timer=Timer::from_state(state,paused);
}
pub fn set_scale(&mut self,time:SessionTime,new_scale:strafesnet_common::integer::Ratio64){
self.timer.set_scale(time,new_scale);
}
pub fn advance_time(&mut self,bot:&CompleteBot,time:SessionTime){
let mut simulation_time=self.time(bot,time);
let mut simulation_time=bot.time(self.time(time));
let mut time_float=simulation_time.get() as f64/PhysicsTime::ONE_SECOND.get() as f64;
loop{
match self.next_event(bot){
@@ -88,7 +75,14 @@ impl PlaybackHead{
None=>{
//reset playback
self.head=HEAD_NO_CRASH;
self.loop_offset-=bot.duration();
// hack to wind back timer offset without precise session timestamp
let (mut state,paused)=self.timer.clone().into_state();
let offset=state.get_offset()-bot.duration().coerce();
state.set_offset(offset);
self.timer=Timer::from_state(state,paused);
// update loop variables
simulation_time-=bot.duration();
time_float=simulation_time.get() as f64/PhysicsTime::ONE_SECOND.get() as f64;
},
@@ -96,7 +90,7 @@ impl PlaybackHead{
}
}
pub fn get_position_angles(&self,bot:&CompleteBot,time:SessionTime)->(glam::Vec3,glam::Vec2){
let time=self.time(bot,time);
let time=bot.time(self.time(time));
let event0=&bot.timelines().output_events[self.head.get_event_index(EventType::Output)-1];
let event1=&bot.timelines().output_events[self.head.get_event_index(EventType::Output)];
let p0=vector3_to_glam(&event0.event.position);

View File

@@ -1,7 +1,6 @@
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::session::Time as SessionTime;
use strafesnet_common::physics::Time as PhysicsTime;
use strafesnet_roblox_bot_player::{bot::CompleteBot,graphics::Graphics,head::PlaybackHead};
use strafesnet_roblox_bot_player::{bot::CompleteBot,graphics::Graphics,head::{PlaybackHead,Time as PlaybackTime}};
pub enum SessionControlInstruction{
SetPaused(bool),
@@ -55,13 +54,15 @@ impl<'a> PlayerWorker<'a>{
self.playback_head.set_paused(ins.time,paused);
},
Instruction::SessionControl(SessionControlInstruction::Restart)=>{
self.playback_head.seek_to(ins.time,PhysicsTime::ZERO);
self.playback_head.set_time(ins.time,PlaybackTime::ZERO);
},
Instruction::SessionControl(SessionControlInstruction::SkipForward)=>{
self.playback_head.seek_forward(SessionTime::from_secs(2));
let head_time=self.playback_head.time(ins.time);
self.playback_head.set_time(ins.time,head_time+PlaybackTime::from_secs(2));
},
Instruction::SessionControl(SessionControlInstruction::SkipBack)=>{
self.playback_head.seek_backward(SessionTime::from_secs(2));
let head_time=self.playback_head.time(ins.time);
self.playback_head.set_time(ins.time,head_time-PlaybackTime::from_secs(2));
},
Instruction::SessionControl(SessionControlInstruction::DecreaseTimescale)=>{
self.playback_speed=self.playback_speed.saturating_sub(1).max(-27);

View File

@@ -118,9 +118,15 @@ impl PlaybackHead{
self.head.advance_time(&bot.bot,time);
}
#[wasm_bindgen]
pub fn get_head_time(&self,time:f64)->f64{
let time=time::from_float(time).unwrap();
let time=self.head.time(time);
time.get() as f64/strafesnet_common::run::Time::ONE_SECOND.get() as f64
}
#[wasm_bindgen]
pub fn get_run_time(&self,bot:&CompleteBot,time:f64,mode_id:u32)->Option<f64>{
let time=time::from_float(time).unwrap();
let time=self.head.time(&bot.bot,time);
let time=bot.bot.time(self.head.time(time));
let mode=v0::ModeID(mode_id);
let run_time=self.head.state().get_run(mode)?.time(time);
Some(run_time.get() as f64/strafesnet_common::run::Time::ONE_SECOND.get() as f64)
@@ -145,21 +151,12 @@ impl PlaybackHead{
let time=time::from_float(time).unwrap();
self.head.set_scale(time,scale.try_into().unwrap());
}
/// Set the playback head position to new_time.
#[wasm_bindgen]
pub fn seek_to(&mut self,time:f64,new_time:f64){
pub fn set_head_time(&mut self,time:f64,new_time:f64){
let time=time::from_float(time).unwrap();
let new_time=time::from_float(new_time).unwrap();
self.head.seek_to(time,new_time);
}
#[wasm_bindgen]
pub fn seek_forward(&mut self,time_offset:f64){
let time_offset=time::from_float(time_offset).unwrap();
self.head.seek_forward(time_offset);
}
#[wasm_bindgen]
pub fn seek_backward(&mut self,time_offset:f64){
let time_offset=time::from_float(time_offset).unwrap();
self.head.seek_backward(time_offset);
self.head.set_time(time,new_time);
}
#[wasm_bindgen]
pub fn get_fov_slope_y(&self)->f64{

View File

@@ -56,17 +56,21 @@ function set_scale(new_scale) {
// Controls
document.getElementById("control_reset").addEventListener("click", (e) => {
playback.seek_to(elapsed(), 0.0);
playback.set_head_time(elapsed(), 0.0);
});
document.getElementById("control_pause").addEventListener("click", (e) => {
paused = !paused;
playback.set_paused(elapsed(), paused);
});
document.getElementById("control_forward").addEventListener("click", (e) => {
playback.seek_forward(2.0);
const time_now = elapsed();
const playback_time = playback.get_head_time(time_now);
playback.set_head_time(time_now, playback_time + 2.0);
});
document.getElementById("control_backward").addEventListener("click", (e) => {
playback.seek_backward(2.0);
const time_now = elapsed();
const playback_time = playback.get_head_time(time_now);
playback.set_head_time(time_now, playback_time - 2.0);
});
document.getElementById("control_slower").addEventListener("click", (e) => {
set_scale((scale * 4) / 5);