v0 serializer
This commit is contained in:
@@ -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
220
src/v0.rs
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user