forked from StrafesNET/roblox-bot-player
Compare commits
6 Commits
xeno-debug
...
playback-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
7e46184566
|
|||
|
4877923655
|
|||
|
144bfc7a2b
|
|||
|
b42c0ea254
|
|||
|
56a7912dbf
|
|||
|
a9db4e38eb
|
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -483,8 +483,6 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
[[package]]
|
||||
name = "fixed_wide"
|
||||
version = "0.2.2"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "1397e01522f708e80dcf6c17db69139abb57c43212226b60b50fc09d91c607b5"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bnum",
|
||||
@@ -819,8 +817,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "linear_ops"
|
||||
version = "0.1.1"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "d6ea2e52a83eab4afe56536e6d27f8e815bd994111ccdc3e2c0aafce77014286"
|
||||
dependencies = [
|
||||
"fixed_wide",
|
||||
"paste",
|
||||
@@ -1410,8 +1406,6 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
|
||||
[[package]]
|
||||
name = "ratio_ops"
|
||||
version = "0.1.1"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "93f2dc5bfc9d878028a699e77c6f88ac59d23404218af9fcfbfc190610f49c80"
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
@@ -1633,8 +1627,6 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
[[package]]
|
||||
name = "strafesnet_common"
|
||||
version = "0.8.0"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "171f2b754a8c59b335578824d5465d9637fb41ec4906c6a8c1fd39206891b09c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 2.10.0",
|
||||
@@ -1648,8 +1640,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "strafesnet_graphics"
|
||||
version = "0.0.2"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "a0f063bd3579397908d411cef8ee3a755760f567ff354247c21d503c2a406669"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"ddsfile",
|
||||
@@ -1662,8 +1652,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "strafesnet_roblox_bot_file"
|
||||
version = "0.8.1"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "33d0fa524476d8b6cf23269b0c9cff6334b70585546b807cb8ec193858defecd"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
"bitflags 2.10.0",
|
||||
@@ -1714,8 +1702,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "strafesnet_snf"
|
||||
version = "0.3.2"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "aab96a189e3f5c4e5eca1feae704c0d6ceaa0de37a41e29ef3a89816e354292f"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
"id",
|
||||
|
||||
@@ -12,7 +12,7 @@ strip = true
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.dependencies]
|
||||
strafesnet_common = { version = "0.8.0", registry = "strafesnet" }
|
||||
strafesnet_graphics = { version = "0.0.2", registry = "strafesnet" }
|
||||
strafesnet_roblox_bot_file = { version = "0.8.1", registry = "strafesnet" }
|
||||
strafesnet_snf = { version = "0.3.2", registry = "strafesnet" }
|
||||
strafesnet_common = { version = "0.8.0", registry = "strafesnet", path = "../strafe-project/lib/common" }
|
||||
strafesnet_graphics = { version = "0.0.2", registry = "strafesnet", path = "../strafe-project/engine/graphics" }
|
||||
strafesnet_roblox_bot_file = { version = "0.8.1", registry = "strafesnet", path = "../roblox_bot_file" }
|
||||
strafesnet_snf = { version = "0.3.2", registry = "strafesnet", path = "../strafe-project/lib/snf" }
|
||||
|
||||
@@ -2,14 +2,10 @@ 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,Timed};
|
||||
|
||||
pub enum PlaybackInstructionInternal{
|
||||
Sound
|
||||
}
|
||||
pub enum PlaybackInstructionExternal{
|
||||
SetPaused(bool),
|
||||
Idle,
|
||||
}
|
||||
use crate::bot::CompleteBot;
|
||||
use crate::state::PlaybackState;
|
||||
|
||||
fn vector3_to_glam(v:&strafesnet_roblox_bot_file::v0::Vector3)->glam::Vec3{
|
||||
glam::vec3(v.x,v.y,v.z)
|
||||
@@ -17,45 +13,59 @@ fn vector3_to_glam(v:&strafesnet_roblox_bot_file::v0::Vector3)->glam::Vec3{
|
||||
|
||||
/// A playback context. Advance time and then generate a camera position to pass to the renderer.
|
||||
pub struct PlaybackHead{
|
||||
//"Simulation"
|
||||
event_id:usize,
|
||||
head:Head,
|
||||
offset:f64,
|
||||
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
|
||||
state:PlaybackState,
|
||||
}
|
||||
const HEAD_NO_CRASH:Head={
|
||||
let mut head=Head::new();
|
||||
// push one output event so that output-1 doesn't underflow
|
||||
head.push(EventType::Output);
|
||||
head
|
||||
};
|
||||
impl PlaybackHead{
|
||||
pub fn new(time:SessionTime)->Self{
|
||||
let timer=Timer::unpaused(time,PhysicsTime::ZERO);
|
||||
Self{
|
||||
event_id:0,
|
||||
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){
|
||||
self.state.process_event(bot,event_type,self.head.get_event_index(event_type));
|
||||
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 bot.timelines().output_events.get(self.event_id+1){
|
||||
match self.next_event(bot){
|
||||
Some(next_event)=>{
|
||||
if next_event.time<time_float{
|
||||
self.event_id+=1;
|
||||
self.process_event(bot,next_event.event);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
},
|
||||
None=>{
|
||||
//reset playback
|
||||
self.event_id=0;
|
||||
self.head=HEAD_NO_CRASH;
|
||||
self.offset-=bot.duration();
|
||||
time_float-=bot.duration();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
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.event_id];
|
||||
let event1=&bot.timelines().output_events[self.event_id+1];
|
||||
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);
|
||||
let p1=vector3_to_glam(&event1.event.position);
|
||||
// let v0=vector3_to_glam(&event0.event.velocity);
|
||||
@@ -76,6 +86,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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod bot;
|
||||
pub mod map;
|
||||
pub mod head;
|
||||
pub mod state;
|
||||
// pub mod surface;
|
||||
pub mod graphics;
|
||||
|
||||
|
||||
186
lib/src/state.rs
Normal file
186
lib/src/state.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use strafesnet_common::run;
|
||||
use strafesnet_common::physics::Time as PhysicsTime;
|
||||
use strafesnet_roblox_bot_file::v0;
|
||||
|
||||
use crate::bot::CompleteBot;
|
||||
|
||||
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{..})
|
||||
}
|
||||
}
|
||||
|
||||
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:1.0,
|
||||
vertical_sensitivity_multipler:1.0,
|
||||
turn_speed:1.0,
|
||||
}
|
||||
}
|
||||
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=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=>{
|
||||
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;
|
||||
},
|
||||
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_event(&mut self,bot:&CompleteBot,event_type:v0::EventType,event_index:usize){
|
||||
match event_type{
|
||||
v0::EventType::Input=>self.push_input(&bot.timelines().input_events[event_index].event),
|
||||
v0::EventType::Output=>self.push_output(&bot.timelines().output_events[event_index].event),
|
||||
v0::EventType::Sound=>{},
|
||||
v0::EventType::World=>{},
|
||||
v0::EventType::Gravity=>self.push_gravity(&bot.timelines().gravity_events[event_index].event),
|
||||
v0::EventType::Run=>self.push_run(&bot.timelines().run_events[event_index]),
|
||||
v0::EventType::Camera=>{},
|
||||
v0::EventType::Setting=>self.push_setting(&bot.timelines().setting_events[event_index].event),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user