v0 serializer

This commit is contained in:
2025-11-06 18:19:19 -08:00
parent 4ef063a474
commit 6038241d7a
2 changed files with 220 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
[package]
name = "strafesnet_roblox_bot_file"
version = "0.7.0"
edition = "2021"
edition = "2024"
[dependencies]
binrw = "0.15.0"

220
src/v0.rs
View File

@@ -1,7 +1,8 @@
use std::io::{SeekFrom,Error as IoError};
use std::ops::Range;
use binrw::binrw;
use binrw::io::{TakeSeek,TakeSeekExt};
use binrw::BinReaderExt;
use binrw::{BinReaderExt,BinWriterExt};
// the bit chunks are deposited in reverse
fn read_trey_float(bits:u32)->f32{
@@ -447,6 +448,7 @@ pub struct Block{
#[binrw]
#[brw(little)]
#[derive(Clone,Copy)]
enum EventType{
#[brw(magic=1u32)]
Input,
@@ -465,6 +467,22 @@ enum EventType{
#[brw(magic=8u32)]
Setting,
}
impl EventType{
// internal function meant for array indexing
fn from_usize(value:usize)->Self{
match value{
0=>Self::Input,
1=>Self::Output,
2=>Self::Sound,
3=>Self::World,
4=>Self::Gravity,
5=>Self::Run,
6=>Self::Camera,
7=>Self::Setting,
_=>panic!(),
}
}
}
#[binrw]
#[brw(little)]
struct EventChunkHeader{
@@ -680,3 +698,203 @@ pub fn read_all_to_block<R:BinReaderExt>(mut data:R)->Result<Block,Error>{
let block=read_to_block(data,&block_timelines,itertools::merge(block_timelines.offline_blocks(),block_timelines.realtime_blocks()))?;
Ok(block)
}
#[cfg(feature="itertools")]
pub fn serialize<W:BinWriterExt>(block:&Block,writer:&mut W)->Result<(),binrw::Error>{
const MAX_BLOCK_SIZE:usize=1<<14;
const FILE_VERSION:u32=0;
const EVENT_SIZE:[usize;8]=[
8+4+2*4, // Input
8+4+4*3*4, // Output
8+4+4, // Sound
8+4+12, // World
8+3*4, // Gravity
8+4+4+4, // Run
8+4+3*4, // Camera
8+4+8, // Setting
];
#[derive(Clone,Default)]
struct Plan<T>([T;8]);
impl Plan<usize>{
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]))
}
}
impl Plan<Range<usize>>{
fn size(&self)->usize{
self.0
.iter()
.enumerate()
.map(|(i,range)|
range.len()*EVENT_SIZE[i]
)
.sum()
}
}
macro_rules! collect_event{
($best:ident,$list:expr,$plan:ident,$event_type:expr)=>{
if let Some(event)=$list.get($plan.0[$event_type as usize])
&&$best.map_or(true,|(time,_)|event.time<time)
{
$best=Some((event.time,EventType::World));
}
};
}
fn next_offline_event(block:&Block,plan:&Plan<usize>)->Option<(f64,EventType)>{
let mut next_event=None;
collect_event!(next_event,block.world_events,plan,EventType::World);
collect_event!(next_event,block.gravity_events,plan,EventType::Gravity);
collect_event!(next_event,block.run_events,plan,EventType::Run);
collect_event!(next_event,block.camera_events,plan,EventType::Camera);
collect_event!(next_event,block.setting_events,plan,EventType::Setting);
next_event
}
fn next_realtime_event(block:&Block,plan:&Plan<usize>)->Option<(f64,EventType)>{
let mut next_event=None;
collect_event!(next_event,block.input_events,plan,EventType::Input);
collect_event!(next_event,block.output_events,plan,EventType::Output);
collect_event!(next_event,block.sound_events,plan,EventType::Sound);
next_event
}
fn plan_block(block:&Block,plan:&mut Plan<usize>,next_event:impl Fn(&Block,&Plan<usize>)->Option<(f64,EventType)>)->Option<f64>{
let mut size=0;
if let Some((start_time,first_event))=next_event(block,plan){
size+=EVENT_SIZE[first_event as usize];
if MAX_BLOCK_SIZE<size{
return None;
}
plan.accumulate(first_event);
while let Some((_,event_type))=next_event(block,plan){
size+=EVENT_SIZE[event_type as usize];
if MAX_BLOCK_SIZE<size{
break;
}
plan.accumulate(event_type);
}
return Some(start_time);
}
None
}
// plan events into segments without spilling over max size threshold
// each index describes the non-inclusive end index of the range of events included in the block.
let mut plan_offline=std::collections::VecDeque::new();
let mut plan=Plan::default();
let mut last_plan=plan.clone();
while let Some(time)=plan_block(block,&mut plan,next_offline_event){
plan_offline.push_front((time,last_plan.range(&plan)));
last_plan=plan.clone();
}
let mut plan_realtime=std::collections::VecDeque::new();
let mut plan=Plan::default();
let mut last_plan=plan.clone();
while let Some(time)=plan_block(block,&mut plan,next_realtime_event){
plan_realtime.push_front((time,last_plan.range(&plan)));
last_plan=plan.clone();
}
use binrw::BinWrite;
let file_header=FileHeader{
file_version:FILE_VERSION,
num_offline_blocks:plan_offline.len() as u32,
num_realtime_blocks:plan_realtime.len() as u32,
};
let mut chrono_plan=Vec::with_capacity(plan_offline.len()+plan_realtime.len());
let mut block_positions=Vec::with_capacity(file_header.block_position_count() as usize);
let mut offline_blocks_timeline=Vec::with_capacity(plan_offline.len());
let mut realtime_blocks_timeline=Vec::with_capacity(plan_realtime.len());
// position starts after the *predicted* end of the BlockTimelines
let mut position=file_header.block_timelines_info().end;
let mut block_id=0;
macro_rules! push_offline{
($time:expr,$plan:expr)=>{
block_positions.push(BlockPosition(position));
position+=$plan.size() as u32;
offline_blocks_timeline.push(Timed{
time:$time,
event:BlockId(block_id),
});
block_id+=1;
chrono_plan.push($plan);
}
}
macro_rules! push_realtime{
($time:expr,$plan:expr)=>{
block_positions.push(BlockPosition(position));
position+=$plan.size() as u32;
realtime_blocks_timeline.push(Timed{
time:$time,
event:BlockId(block_id),
});
block_id+=1;
chrono_plan.push($plan);
}
}
// push three important blocks
if let Some((time,plan))=plan_offline.pop_front(){
push_offline!(time,plan);
}
if let Some((time,plan))=plan_realtime.pop_front(){
push_realtime!(time,plan);
}
if let Some((time,plan))=plan_realtime.pop_back(){
push_realtime!(time,plan);
}
// push the remaining blocks in chronological order
for block_plan in itertools::merge_join_by(
plan_offline,
plan_realtime,
|&(offline,_),&(realtime,_)|offline<=realtime,
){
match block_plan{
itertools::Either::Left((time,plan))=>{push_offline!(time,plan);},
itertools::Either::Right((time,plan))=>{push_realtime!(time,plan);},
}
}
// final position
block_positions.push(BlockPosition(position));
let block_timelines=BlockTimelines{
block_positions,
offline_blocks_timeline,
realtime_blocks_timeline,
};
file_header.write_le(writer)?;
block_timelines.write_le(writer)?;
for plan in chrono_plan{
for (event_type_id,range) in plan.0.iter().enumerate(){
let event_type=EventType::from_usize(event_type_id);
let event_chunk_header=EventChunkHeader{
event_type,
num_events:range.len() as u32,
};
event_chunk_header.write_le(writer)?;
match event_type{
EventType::Input=>block.input_events.write_le(writer)?,
EventType::Output=>block.output_events.write_le(writer)?,
EventType::Sound=>block.sound_events.write_le(writer)?,
EventType::World=>block.world_events.write_le(writer)?,
EventType::Gravity=>block.gravity_events.write_le(writer)?,
EventType::Run=>block.run_events.write_le(writer)?,
EventType::Camera=>block.camera_events.write_le(writer)?,
EventType::Setting=>block.setting_events.write_le(writer)?,
}
}
}
Ok(())
}