forked from StrafesNET/roblox-bot-player
253 lines
8.0 KiB
Rust
253 lines
8.0 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use strafesnet_common::run;
|
|
use strafesnet_common::physics::Time as PhysicsTime;
|
|
use strafesnet_roblox_bot_file::v0;
|
|
|
|
pub 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);
|
|
}
|
|
}
|
|
pub fn time(&self,time:PhysicsTime)->run::Time{
|
|
self.run.time(time)
|
|
}
|
|
pub fn is_invalid(&self)->bool{
|
|
self.flag_reason.is_some()
|
|
}
|
|
pub fn is_in_progress(&self)->bool{
|
|
matches!(&self.run,run::RunState::Started{..})
|
|
}
|
|
pub fn is_finished(&self)->bool{
|
|
matches!(&self.run,run::RunState::Finished{..})
|
|
}
|
|
pub fn get_flag_reason_text(&self)->Option<&'static str>{
|
|
Some(match self.flag_reason{
|
|
Some(v0::FlagReason::Anticheat)=>"Passed through anticheat zone.",
|
|
Some(v0::FlagReason::StyleChange)=>"Changed style.",
|
|
Some(v0::FlagReason::Clock)=>"Incorrect clock. (This can be caused by internet hiccups)",
|
|
Some(v0::FlagReason::Pause)=>"Pausing is not allowed in this style.",
|
|
Some(v0::FlagReason::Flying)=>"Flying is not allowed in this style.",
|
|
Some(v0::FlagReason::Gravity)=>"Gravity modification is not allowed in this style.",
|
|
Some(v0::FlagReason::Timescale)=>"Timescale is not allowed in this style.",
|
|
Some(v0::FlagReason::Timetravel)=>"Time travel is not allowed in this style.",
|
|
Some(v0::FlagReason::Teleport)=>"Illegal teleport.",
|
|
Some(v0::FlagReason::Practice)=>"Practice mode triggers invalidation.",
|
|
None=>return None,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct PlaybackState{
|
|
// EventType::Input
|
|
game_controls:v0::GameControls,
|
|
mouse_pos:v0::Vector2,
|
|
// EventType::Output
|
|
jump_count:u32,
|
|
// 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{
|
|
pub fn new()->Self{
|
|
Self{
|
|
game_controls:v0::GameControls::empty(),
|
|
mouse_pos:v0::Vector2{x:0.0,y:0.0},
|
|
jump_count: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:0.3,
|
|
vertical_sensitivity_multipler:1.0,
|
|
turn_speed:core::f64::consts::TAU/0.715588,
|
|
}
|
|
}
|
|
pub fn get_run(&self,mode:v0::ModeID)->Option<&Run>{
|
|
self.runs.get(&mode)
|
|
}
|
|
fn push_output(&mut self,event:&v0::OutputEvent){
|
|
if event.tick_info.contains(v0::TickInfo::Jump){
|
|
self.jump_count+=1;
|
|
}
|
|
}
|
|
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:&v0::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=crate::time::from_float(event.time).unwrap();
|
|
if let Some(run)=self.runs.get_mut(&run_event_zone.mode){
|
|
_=run.run.start(time);
|
|
}
|
|
},
|
|
v0::RunEvent::Finish(run_event_zone)=>{
|
|
let time=crate::time::from_float(event.time).unwrap();
|
|
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=>{
|
|
for run in self.runs.values_mut(){
|
|
run.flag(run_event_flag.flag_reason);
|
|
}
|
|
},
|
|
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*0.5).to_radians().tan();
|
|
},
|
|
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;
|
|
},
|
|
}
|
|
}
|
|
pub(crate) fn process_head(&mut self,block:&v0::Block,head:&v0::Head){
|
|
// The whole point of this is to avoid running the realtime events!
|
|
/*
|
|
for event in &block.input_events[0..head.get_event_index(v0::EventType::Input)]{
|
|
self.push_input(&event.event);
|
|
}
|
|
for event in &block.output_events[0..head.get_event_index(v0::EventType::Output)]{
|
|
self.push_output(&event.event);
|
|
}
|
|
for event in &bot.sound_events[0..head.get_event_index(v0::EventType::Sound)]{
|
|
self.push_sound(&event.event);
|
|
}
|
|
*/
|
|
// for event in &bot.world_events[0..head.get_event_index(v0::EventType::World)]{
|
|
// self.push_world(&event.event);
|
|
// }
|
|
for event in &block.gravity_events[0..head.get_event_index(v0::EventType::Gravity)]{
|
|
self.push_gravity(&event.event);
|
|
}
|
|
for event in &block.run_events[0..head.get_event_index(v0::EventType::Run)]{
|
|
self.push_run(event);
|
|
}
|
|
// for event in &bot.camera_events[0..head.get_event_index(v0::EventType::Camera)]{
|
|
// self.push_camera(&event.event);
|
|
// }
|
|
for event in &block.setting_events[0..head.get_event_index(v0::EventType::Setting)]{
|
|
self.push_setting(&event.event);
|
|
}
|
|
}
|
|
pub(crate) fn process_event(&mut self,block:&v0::Block,event_type:v0::EventType,event_index:usize){
|
|
match event_type{
|
|
v0::EventType::Input=>self.push_input(&block.input_events[event_index].event),
|
|
v0::EventType::Output=>self.push_output(&block.output_events[event_index].event),
|
|
v0::EventType::Sound=>{},
|
|
v0::EventType::World=>{},
|
|
v0::EventType::Gravity=>self.push_gravity(&block.gravity_events[event_index].event),
|
|
v0::EventType::Run=>self.push_run(&block.run_events[event_index]),
|
|
v0::EventType::Camera=>{},
|
|
v0::EventType::Setting=>self.push_setting(&block.setting_events[event_index].event),
|
|
}
|
|
}
|
|
pub fn get_fov_y(&self)->f64{
|
|
let zoom_enabled=self.game_controls.contains(v0::GameControls::Zoom);
|
|
if zoom_enabled{self.fov_y*0.2}else{self.fov_y}
|
|
}
|
|
pub fn get_sensitivity(&self)->(f64,f64){
|
|
if self.absolute_sensitivity_enabled{
|
|
(self.sens_x,self.sens_x*self.vertical_sensitivity_multipler)
|
|
}else{
|
|
let sens_x=self.sens_x*self.get_fov_y()/128.0;
|
|
(sens_x,sens_x*self.vertical_sensitivity_multipler)
|
|
}
|
|
}
|
|
pub const fn get_controls(&self)->v0::GameControls{
|
|
self.game_controls
|
|
}
|
|
}
|