PlaybackState

This commit is contained in:
2026-02-19 10:08:30 -08:00
parent b42c0ea254
commit 144bfc7a2b

View File

@@ -1,8 +1,14 @@
use std::collections::HashMap;
use glam::Vec3Swizzles;
use strafesnet_common::timer::{Timer,Scaled};
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner};
use strafesnet_roblox_bot_file::v0::{EventType,Head};
use strafesnet_common::run;
use strafesnet_roblox_bot_file::v0::{EventType,Head,Timed};
use strafesnet_roblox_bot_file::v0;
use crate::bot::CompleteBot;
pub enum PlaybackInstructionInternal{
Sound
@@ -16,12 +22,168 @@ fn vector3_to_glam(v:&strafesnet_roblox_bot_file::v0::Vector3)->glam::Vec3{
glam::vec3(v.x,v.y,v.z)
}
struct Run{
run:run::RunState,
flag_reason:Option<v0::FlagReason>,
}
impl Run{
fn new()->Self{
Self{
run:run::RunState::Created,
flag_reason:None,
}
}
fn flag(&mut self,flag_reason:v0::FlagReason){
if self.flag_reason.is_none(){
self.flag_reason=Some(flag_reason);
}
}
fn is_invalid(&self)->bool{
self.flag_reason.is_some()
}
fn is_in_progress(&self)->bool{
matches!(&self.run,run::RunState::Started{..})
}
}
struct PlaybackState{
// EventType::Input
game_controls:v0::GameControls,
mouse_pos:v0::Vector2,
// EventType::Output
// EventType::Sound
// EventType::World
// EventType::Gravity
gravity:v0::Vector3,
// EventType::Run
runs:HashMap<v0::ModeID,Run>,
style:v0::Style,
// EventType::Camera
// TODO: camera punch
// EventType::Setting
absolute_sensitivity_enabled:bool,
fov_y:f64,
sens_x:f64,
vertical_sensitivity_multipler:f64,
turn_speed:f64,
}
impl PlaybackState{
fn new()->Self{
Self{
game_controls:v0::GameControls::empty(),
mouse_pos:v0::Vector2{x:0.0,y:0.0},
gravity:v0::Vector3{x:0.0,y:0.0,z:0.0},
runs:HashMap::new(),
style:v0::Style::Autohop,
absolute_sensitivity_enabled:false,
fov_y:1.0,
sens_x:1.0,
vertical_sensitivity_multipler:1.0,
turn_speed:1.0,
}
}
fn push_input(&mut self,event:&v0::InputEvent){
self.game_controls=event.game_controls;
self.mouse_pos=event.mouse_pos;
}
fn push_gravity(&mut self,event:&v0::GravityEvent){
self.gravity=event.gravity;
}
fn push_run(&mut self,event:&Timed<v0::RunEvent>){
match &event.event{
v0::RunEvent::Prepare(run_event_prepare)=>{
self.runs.insert(run_event_prepare.mode,Run::new());
self.style=run_event_prepare.style;
},
v0::RunEvent::Start(run_event_zone)=>{
let time=PhysicsTime::raw((event.time*PhysicsTime::ONE_SECOND.get() as f64) as i64);
if let Some(run)=self.runs.get_mut(&run_event_zone.mode){
_=run.run.start(time);
}
},
v0::RunEvent::Finish(run_event_zone)=>{
let time=PhysicsTime::raw((event.time*PhysicsTime::ONE_SECOND.get() as f64) as i64);
if let Some(run)=self.runs.get_mut(&run_event_zone.mode){
_=run.run.finish(time);
}
},
v0::RunEvent::Clear(run_event_clear)=>{
match run_event_clear.mode{
v0::ModeSpec::Exactly(mode_id)=>{
self.runs.remove(&mode_id);
},
v0::ModeSpec::All=>{
self.runs.clear();
},
v0::ModeSpec::Invalid=>{
self.runs.retain(|_,run|!run.is_invalid());
},
v0::ModeSpec::InProgress=>{
self.runs.retain(|_,run|!run.is_in_progress());
},
}
},
v0::RunEvent::Flag(run_event_flag)=>{
match run_event_flag.mode{
v0::ModeSpec::Exactly(mode_id)=>
if let Some(run)=self.runs.get_mut(&mode_id){
run.flag(run_event_flag.flag_reason);
},
v0::ModeSpec::All=>self.runs.clear(),
v0::ModeSpec::Invalid=>{
for run in self.runs.values_mut(){
if run.is_invalid(){
run.flag(run_event_flag.flag_reason);
}
}
},
v0::ModeSpec::InProgress=>{
for run in self.runs.values_mut(){
if run.is_in_progress(){
run.flag(run_event_flag.flag_reason);
}
}
},
}
},
// these should never appear in a uploaded bot file,
// they are just part of the network protocol for spectating
// someone in practice mode.
//
// Yes, this is a design mistake.
// I didn't understand Session vs Simulation when I rewrote bhop in 2022
v0::RunEvent::LoadState(_run_event_practice)=>{},
v0::RunEvent::SaveState(_run_event_practice)=>{},
}
}
fn push_setting(&mut self,event:&v0::SettingEvent){
match event{
v0::SettingEvent::FieldOfView(setting_event_field_of_view)=>{
self.fov_y=setting_event_field_of_view.fov;
},
v0::SettingEvent::Sensitivity(setting_event_sensitivity)=>{
self.sens_x=setting_event_sensitivity.sensitivity;
},
v0::SettingEvent::VerticalSensitivityMultiplier(setting_event_vertical_sensitivity_multiplier)=>{
self.vertical_sensitivity_multipler=setting_event_vertical_sensitivity_multiplier.multiplier;
},
v0::SettingEvent::AbsoluteSensitivity(setting_event_absolute_sensitivity)=>{
self.absolute_sensitivity_enabled=setting_event_absolute_sensitivity.enabled;
},
v0::SettingEvent::TurnSpeed(setting_event_turn_speed)=>{
self.turn_speed=setting_event_turn_speed.turn_speed;
},
}
}
}
/// A playback context. Advance time and then generate a camera position to pass to the renderer.
pub struct PlaybackHead{
//"Simulation"
head:Head,
offset:f64,
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
state:PlaybackState,
}
const HEAD_NO_CRASH:Head={
let mut head=Head::new();
@@ -36,16 +198,33 @@ impl PlaybackHead{
head:HEAD_NO_CRASH,
offset:0.0,
timer,
state:PlaybackState::new(),
}
}
pub fn advance_time(&mut self,bot:&crate::bot::CompleteBot,time:SessionTime){
pub fn next_event(&self,bot:&CompleteBot)->Option<Timed<EventType>>{
self.head.next_event(bot.timelines())
}
pub fn process_event(&mut self,bot:&CompleteBot,event_type:EventType){
match event_type{
EventType::Input=>self.state.push_input(&bot.timelines().input_events[self.head.get_event_index(event_type)].event),
EventType::Output=>{},
EventType::Sound=>{},
EventType::World=>{},
EventType::Gravity=>self.state.push_gravity(&bot.timelines().gravity_events[self.head.get_event_index(event_type)].event),
EventType::Run=>self.state.push_run(&bot.timelines().run_events[self.head.get_event_index(event_type)]),
EventType::Camera=>{},
EventType::Setting=>self.state.push_setting(&bot.timelines().setting_events[self.head.get_event_index(event_type)].event),
}
self.head.push(event_type);
}
pub fn advance_time(&mut self,bot:&CompleteBot,time:SessionTime){
let simulation_time=self.timer.time(time);
let mut time_float=simulation_time.get() as f64/PhysicsTime::ONE_SECOND.get() as f64+self.offset+bot.offset();
loop{
match self.head.next_event(bot.timelines()){
match self.next_event(bot){
Some(next_event)=>{
if next_event.time<time_float{
self.head.push(next_event.event);
self.process_event(bot,next_event.event);
}else{
break;
}
@@ -59,7 +238,7 @@ impl PlaybackHead{
}
}
}
pub fn get_position_angles(&self,bot:&crate::bot::CompleteBot,time:SessionTime)->(glam::Vec3,glam::Vec2){
pub fn get_position_angles(&self,bot:&CompleteBot,time:SessionTime)->(glam::Vec3,glam::Vec2){
let time=self.timer.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)];
@@ -83,6 +262,6 @@ impl PlaybackHead{
let angles1=vector3_to_glam(&event1.event.angles);
let angles=angles0.lerp(angles1,t);
(p+crate::bot::CompleteBot::CAMERA_OFFSET,angles.yx())
(p+CompleteBot::CAMERA_OFFSET,angles.yx())
}
}