Compare commits
15 Commits
v1-dyn-siz
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
c807fd98e6
|
|||
|
d108697552
|
|||
|
0cd02fbb75
|
|||
|
3674bb38b3
|
|||
|
3323b0109a
|
|||
|
8c776f29b0
|
|||
|
87ba477f63
|
|||
|
508a0db66f
|
|||
|
e88d62c3ea
|
|||
|
1b6b02ef5a
|
|||
|
dc87100e91
|
|||
|
224a873d89
|
|||
|
3cf39032cc
|
|||
|
0c896f3ee1
|
|||
|
17edf88e05
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -85,7 +85,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_roblox_bot_file"
|
||||
version = "0.8.1"
|
||||
version = "0.9.4"
|
||||
dependencies = [
|
||||
"binrw",
|
||||
"bitflags",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "strafesnet_roblox_bot_file"
|
||||
version = "0.8.1"
|
||||
version = "0.9.4"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
290
src/v0.rs
290
src/v0.rs
@@ -5,26 +5,26 @@ use binrw::BinReaderExt;
|
||||
use crate::BinrwError;
|
||||
|
||||
// the bit chunks are deposited in reverse
|
||||
fn read_trey_float(bits:u32)->f32{
|
||||
const fn read_trey_float(bits:u32)->f32{
|
||||
let s=bits&1;
|
||||
let e=(bits>>1)&((1<<8)-1);
|
||||
let m=(bits>>(1+8))&((1<<23)-1);
|
||||
f32::from_bits(m|(e<<23)|(s<<31))
|
||||
}
|
||||
fn write_trey_float(value:&f32)->u32{
|
||||
const fn write_trey_float(value:&f32)->u32{
|
||||
let bits=value.to_bits();
|
||||
let s=(bits>>31)&1;
|
||||
let e=(bits>>23)&((1<<8)-1);
|
||||
let m=bits&((1<<23)-1);
|
||||
m<<(1+8)|(e<<1)|s
|
||||
}
|
||||
fn read_trey_double(bits:u64)->f64{
|
||||
const fn read_trey_double(bits:u64)->f64{
|
||||
let s=bits&1;
|
||||
let e=(bits>>1)&((1<<11)-1);
|
||||
let m=(bits>>(1+11))&((1<<52)-1);
|
||||
f64::from_bits(m|(e<<52)|(s<<63))
|
||||
}
|
||||
fn write_trey_double(value:&f64)->u64{
|
||||
const fn write_trey_double(value:&f64)->u64{
|
||||
let bits=value.to_bits();
|
||||
let s=(bits>>63)&1;
|
||||
let e=(bits>>52)&((1<<11)-1);
|
||||
@@ -34,7 +34,7 @@ fn write_trey_double(value:&f64)->u64{
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
pub struct Vector2{
|
||||
#[br(map=read_trey_float)]
|
||||
#[bw(map=write_trey_float)]
|
||||
@@ -45,7 +45,7 @@ pub struct Vector2{
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
pub struct Vector3{
|
||||
#[br(map=read_trey_float)]
|
||||
#[bw(map=write_trey_float)]
|
||||
@@ -350,6 +350,7 @@ pub enum FlagReason{
|
||||
#[brw(magic=9u32)]
|
||||
Practice,
|
||||
}
|
||||
/// Creates a new run when the player enters a start zone.
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
@@ -412,47 +413,91 @@ pub enum RunEvent{
|
||||
}
|
||||
|
||||
// camera
|
||||
/// Punches the camera when the player has an intense collision.
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub enum CameraEventType{
|
||||
#[brw(magic=0u32)]
|
||||
CameraPunch,
|
||||
#[brw(magic=1u32)]
|
||||
Transform,
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct CameraEventCameraPunch{
|
||||
pub rot_velocity:Vector3,
|
||||
}
|
||||
/// Rotates the camera when the player goes through a wormhole.
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct CameraEventTransform{
|
||||
pub axis_angle:Vector3,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct CameraEvent{
|
||||
pub camera_event_type:CameraEventType,
|
||||
pub value:Vector3,
|
||||
pub enum CameraEvent{
|
||||
#[brw(magic=0u32)]
|
||||
CameraPunch(CameraEventCameraPunch),
|
||||
#[brw(magic=1u32)]
|
||||
Transform(CameraEventTransform),
|
||||
}
|
||||
|
||||
// setting
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub enum SettingType{
|
||||
#[brw(magic=0u32)]
|
||||
FieldOfView,
|
||||
#[brw(magic=1u32)]
|
||||
Sensitivity,
|
||||
#[brw(magic=2u32)]
|
||||
VerticalSensitivityMultiplier,
|
||||
#[brw(magic=3u32)]
|
||||
AbsoluteSensitivity,
|
||||
#[brw(magic=4u32)]
|
||||
TurnSpeed,
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct SettingEventFieldOfView{
|
||||
#[br(map=read_trey_double)]
|
||||
#[bw(map=write_trey_double)]
|
||||
pub fov:f64,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct SettingEvent{
|
||||
pub setting_type:SettingType,
|
||||
pub struct SettingEventSensitivity{
|
||||
#[br(map=read_trey_double)]
|
||||
#[bw(map=write_trey_double)]
|
||||
pub value:f64,
|
||||
pub sensitivity:f64,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct SettingEventVerticalSensitivityMultiplier{
|
||||
#[br(map=read_trey_double)]
|
||||
#[bw(map=write_trey_double)]
|
||||
pub multiplier:f64,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct SettingEventAbsoluteSensitivity{
|
||||
#[br(map=|v:u64|read_trey_double(v)==1.0)]
|
||||
#[bw(map=|&enabled:&bool|
|
||||
if enabled{
|
||||
write_trey_double(&1.0)
|
||||
}else{
|
||||
write_trey_double(&0.0)
|
||||
}
|
||||
)]
|
||||
pub enabled:bool,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct SettingEventTurnSpeed{
|
||||
#[br(map=read_trey_double)]
|
||||
#[bw(map=write_trey_double)]
|
||||
pub turn_speed:f64,
|
||||
}
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum SettingEvent{
|
||||
#[brw(magic=0u32)]
|
||||
FieldOfView(SettingEventFieldOfView),
|
||||
#[brw(magic=1u32)]
|
||||
Sensitivity(SettingEventSensitivity),
|
||||
#[brw(magic=2u32)]
|
||||
VerticalSensitivityMultiplier(SettingEventVerticalSensitivityMultiplier),
|
||||
#[brw(magic=3u32)]
|
||||
AbsoluteSensitivity(SettingEventAbsoluteSensitivity),
|
||||
#[brw(magic=4u32)]
|
||||
TurnSpeed(SettingEventTurnSpeed),
|
||||
}
|
||||
|
||||
/// A segment of event timelines.
|
||||
@@ -472,7 +517,7 @@ pub struct Block{
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Clone,Copy)]
|
||||
enum EventType{
|
||||
pub enum EventType{
|
||||
#[brw(magic=1u32)]
|
||||
Input,
|
||||
#[brw(magic=2u32)]
|
||||
@@ -730,12 +775,96 @@ pub fn read_all_to_block<R:BinReaderExt>(mut data:R)->Result<Block,Error>{
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
const NUM_EVENT_TYPES:usize=8;
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Head([usize;NUM_EVENT_TYPES]);
|
||||
impl Head{
|
||||
pub const fn new()->Self{
|
||||
Self([0;NUM_EVENT_TYPES])
|
||||
}
|
||||
/// Use `Head::partition_point` instead.
|
||||
#[deprecated]
|
||||
pub fn after_time(block:&Block,time:f64)->Self{
|
||||
Self([
|
||||
block.input_events.partition_point(|event|event.time<=time),
|
||||
block.output_events.partition_point(|event|event.time<=time),
|
||||
block.sound_events.partition_point(|event|event.time<=time),
|
||||
block.world_events.partition_point(|event|event.time<=time),
|
||||
block.gravity_events.partition_point(|event|event.time<=time),
|
||||
block.run_events.partition_point(|event|event.time<=time),
|
||||
block.camera_events.partition_point(|event|event.time<=time),
|
||||
block.setting_events.partition_point(|event|event.time<=time),
|
||||
])
|
||||
}
|
||||
/// Uses a binary search to initialize the head positions according to a predicate.
|
||||
/// You probably want `|event_time|event_time<=time`
|
||||
pub fn partition_point(block:&Block,pred:impl Fn(f64)->bool)->Self{
|
||||
Self([
|
||||
block.input_events.partition_point(|event|pred(event.time)),
|
||||
block.output_events.partition_point(|event|pred(event.time)),
|
||||
block.sound_events.partition_point(|event|pred(event.time)),
|
||||
block.world_events.partition_point(|event|pred(event.time)),
|
||||
block.gravity_events.partition_point(|event|pred(event.time)),
|
||||
block.run_events.partition_point(|event|pred(event.time)),
|
||||
block.camera_events.partition_point(|event|pred(event.time)),
|
||||
block.setting_events.partition_point(|event|pred(event.time)),
|
||||
])
|
||||
}
|
||||
// compare an event at the head of the plan to the best event collected so far.
|
||||
fn collect_event<E>(
|
||||
&self,
|
||||
event_type:EventType,
|
||||
list:&[Timed<E>],
|
||||
best:&mut Option<Timed<EventType>>,
|
||||
)
|
||||
where
|
||||
E:for<'a>binrw::BinRead<Args<'a>=()>,
|
||||
E:for<'a>binrw::BinWrite<Args<'a>=()>,
|
||||
{
|
||||
if let Some(event)=list.get(self.get_event_index(event_type))
|
||||
&&best.as_ref().is_none_or(|b|event.time<b.time)
|
||||
{
|
||||
*best=Some(Timed{time:event.time,event:event_type});
|
||||
}
|
||||
}
|
||||
fn collect_offline(&self,block:&Block,next_event:&mut Option<Timed<EventType>>){
|
||||
self.collect_event(EventType::World,&block.world_events,next_event);
|
||||
self.collect_event(EventType::Gravity,&block.gravity_events,next_event);
|
||||
self.collect_event(EventType::Run,&block.run_events,next_event);
|
||||
self.collect_event(EventType::Camera,&block.camera_events,next_event);
|
||||
self.collect_event(EventType::Setting,&block.setting_events,next_event);
|
||||
}
|
||||
fn collect_realtime(&self,block:&Block,next_event:&mut Option<Timed<EventType>>){
|
||||
self.collect_event(EventType::Input,&block.input_events,next_event);
|
||||
self.collect_event(EventType::Output,&block.output_events,next_event);
|
||||
self.collect_event(EventType::Sound,&block.sound_events,next_event);
|
||||
}
|
||||
pub fn next_event(&self,block:&Block)->Option<Timed<EventType>>{
|
||||
let mut next_event=None;
|
||||
// This order is particular.
|
||||
// Setting must appear before Input for strafe client resimulation to work.
|
||||
self.collect_offline(block,&mut next_event);
|
||||
self.collect_realtime(block,&mut next_event);
|
||||
next_event
|
||||
}
|
||||
pub const fn get_event_index(&self,event_type:EventType)->usize{
|
||||
self.0[event_type as usize]
|
||||
}
|
||||
pub const fn set_event_index(&mut self,event_type:EventType,index:usize){
|
||||
self.0[event_type as usize]=index;
|
||||
}
|
||||
/// Add the new event.
|
||||
pub const fn push(&mut self,event_type:EventType){
|
||||
self.0[event_type as usize]+=1;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="itertools")]
|
||||
pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),BinrwError>{
|
||||
use std::ops::Range;
|
||||
const MAX_BLOCK_SIZE:usize=1<<14;
|
||||
const FILE_VERSION:u32=0;
|
||||
const EVENT_TYPES:[EventType;8]=[
|
||||
const EVENT_TYPES:[EventType;NUM_EVENT_TYPES]=[
|
||||
EventType::Input,
|
||||
EventType::Output,
|
||||
EventType::Sound,
|
||||
@@ -745,7 +874,7 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
EventType::Camera,
|
||||
EventType::Setting,
|
||||
];
|
||||
const EVENT_SIZE:[usize;8]=[
|
||||
const EVENT_SIZE:[usize;NUM_EVENT_TYPES]=[
|
||||
8+4+2*4, // Input
|
||||
8+4+4*3*4, // Output
|
||||
8+4+4, // Sound
|
||||
@@ -755,26 +884,12 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
8+4+3*4, // Camera
|
||||
8+4+8, // Setting
|
||||
];
|
||||
#[derive(Clone,Default)]
|
||||
struct Plan<T>([T;8]);
|
||||
// A plan of how many events of each type to include in a data block.
|
||||
impl Plan<usize>{
|
||||
/// Predict the size increment from adding a new event.
|
||||
fn size_increase(&self,event_type:EventType)->usize{
|
||||
let new_chunk_header=self.0[event_type as usize]==0;
|
||||
let mask=(-(new_chunk_header as isize)) as usize;
|
||||
EVENT_SIZE[event_type as usize]+(mask&size_of::<EventChunkHeader>())
|
||||
}
|
||||
/// Add the new event.
|
||||
fn accumulate(&mut self,event_type:EventType){
|
||||
self.0[event_type as usize]+=1;
|
||||
}
|
||||
fn range(&self,end:&Plan<usize>)->Plan<Range<usize>>{
|
||||
Plan(core::array::from_fn(|i|self.0[i]..end.0[i]))
|
||||
}
|
||||
}
|
||||
// A plan of what range of events to include in a data block.
|
||||
impl Plan<Range<usize>>{
|
||||
struct Plan([Range<usize>;NUM_EVENT_TYPES]);
|
||||
impl Plan{
|
||||
fn new(start:&Head,end:&Head)->Self{
|
||||
Plan(core::array::from_fn(|i|start.0[i]..end.0[i]))
|
||||
}
|
||||
/// Calculate the predicted size of the planned block.
|
||||
fn size(&self)->usize{
|
||||
self.0.iter()
|
||||
@@ -786,43 +901,42 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
// compare an event at the head of the plan to the best event collected so far.
|
||||
fn collect_event<E>(
|
||||
best:&mut Option<(f64,EventType)>,
|
||||
list:&[Timed<E>],
|
||||
plan:&Plan<usize>,
|
||||
event_type:EventType,
|
||||
)
|
||||
where
|
||||
E:for<'a>binrw::BinRead<Args<'a>=()>,
|
||||
E:for<'a>binrw::BinWrite<Args<'a>=()>,
|
||||
{
|
||||
if let Some(event)=list.get(plan.0[event_type as usize])
|
||||
&&best.is_none_or(|(time,_)|event.time<time)
|
||||
{
|
||||
*best=Some((event.time,event_type));
|
||||
impl IntoIterator for Plan{
|
||||
type IntoIter=core::iter::Zip<
|
||||
core::array::IntoIter<EventType,NUM_EVENT_TYPES>,
|
||||
core::array::IntoIter<Range<usize>,NUM_EVENT_TYPES>,
|
||||
>;
|
||||
type Item=(EventType,Range<usize>);
|
||||
fn into_iter(self)->Self::IntoIter{
|
||||
EVENT_TYPES.into_iter().zip(self.0)
|
||||
}
|
||||
}
|
||||
/// Predict the size increment from adding a new event.
|
||||
fn predict_size_increment(head:&Head,event_type:EventType)->usize{
|
||||
let new_chunk_header=head.get_event_index(event_type)==0;
|
||||
let mask=(-(new_chunk_header as isize)) as usize;
|
||||
EVENT_SIZE[event_type as usize]+(mask&size_of::<EventChunkHeader>())
|
||||
}
|
||||
// plan a single block: collect events until the block is full
|
||||
fn plan_block(plan:&mut Plan<usize>,next_event:impl Fn(&Plan<usize>)->Option<(f64,EventType)>)->Option<f64>{
|
||||
fn plan_block(head:&mut Head,next_event:impl Fn(&Head)->Option<Timed<EventType>>)->Option<f64>{
|
||||
let mut size=0;
|
||||
let (start_time,first_event)=next_event(plan)?;
|
||||
let first=next_event(head)?;
|
||||
|
||||
size+=plan.size_increase(first_event);
|
||||
size+=predict_size_increment(head,first.event);
|
||||
if MAX_BLOCK_SIZE<size{
|
||||
return None;
|
||||
}
|
||||
plan.accumulate(first_event);
|
||||
head.push(first.event);
|
||||
|
||||
while let Some((_,event_type))=next_event(plan){
|
||||
size+=plan.size_increase(event_type);
|
||||
while let Some(event)=next_event(head){
|
||||
size+=predict_size_increment(head,event.event);
|
||||
if MAX_BLOCK_SIZE<size{
|
||||
break;
|
||||
}
|
||||
plan.accumulate(event_type);
|
||||
head.push(event.event);
|
||||
}
|
||||
|
||||
Some(start_time)
|
||||
Some(first.time)
|
||||
}
|
||||
|
||||
struct PlannedBlock{
|
||||
@@ -830,24 +944,24 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
// It is list-local for both plan_offline and plan_realtime.
|
||||
index:usize,
|
||||
time:f64,
|
||||
plan:Plan<Range<usize>>,
|
||||
plan:Plan,
|
||||
}
|
||||
fn plan_timeline<F>(next_event:F)->std::collections::VecDeque<PlannedBlock>
|
||||
where
|
||||
F:Copy,
|
||||
F:Fn(&Plan<usize>)->Option<(f64,EventType)>
|
||||
F:Fn(&Head)->Option<Timed<EventType>>
|
||||
{
|
||||
let mut timeline=std::collections::VecDeque::new();
|
||||
let mut plan=Plan::default();
|
||||
let mut last_plan=plan.clone();
|
||||
let mut head=Head::new();
|
||||
let mut last_head=head.clone();
|
||||
let mut index=0;
|
||||
while let Some(time)=plan_block(&mut plan,next_event){
|
||||
while let Some(time)=plan_block(&mut head,next_event){
|
||||
timeline.push_back(PlannedBlock{
|
||||
index,
|
||||
time,
|
||||
plan:last_plan.range(&plan),
|
||||
plan:Plan::new(&last_head,&head),
|
||||
});
|
||||
last_plan=plan.clone();
|
||||
last_head=head.clone();
|
||||
index+=1;
|
||||
}
|
||||
timeline
|
||||
@@ -856,18 +970,12 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
// each plan describes the range of events included in the block.
|
||||
let mut plan_offline=plan_timeline(|plan|{
|
||||
let mut next_event=None;
|
||||
collect_event(&mut next_event,&block.world_events,plan,EventType::World);
|
||||
collect_event(&mut next_event,&block.gravity_events,plan,EventType::Gravity);
|
||||
collect_event(&mut next_event,&block.run_events,plan,EventType::Run);
|
||||
collect_event(&mut next_event,&block.camera_events,plan,EventType::Camera);
|
||||
collect_event(&mut next_event,&block.setting_events,plan,EventType::Setting);
|
||||
plan.collect_offline(block,&mut next_event);
|
||||
next_event
|
||||
});
|
||||
let mut plan_realtime=plan_timeline(|plan|{
|
||||
let mut next_event=None;
|
||||
collect_event(&mut next_event,&block.input_events,plan,EventType::Input);
|
||||
collect_event(&mut next_event,&block.output_events,plan,EventType::Output);
|
||||
collect_event(&mut next_event,&block.sound_events,plan,EventType::Sound);
|
||||
plan.collect_realtime(block,&mut next_event);
|
||||
next_event
|
||||
});
|
||||
|
||||
@@ -942,7 +1050,7 @@ pub fn serialize<W:binrw::BinWriterExt>(block:&Block,writer:&mut W)->Result<(),B
|
||||
file_header.write_le(writer)?;
|
||||
block_timelines.write_le(writer)?;
|
||||
for plan in plan_order{
|
||||
for (range,event_type) in plan.0.into_iter().zip(EVENT_TYPES){
|
||||
for (event_type,range) in plan{
|
||||
let num_events=range.len();
|
||||
if num_events==0{
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user