expose Head mechanism for bot player

This commit is contained in:
2026-02-20 07:04:14 -08:00
parent dc87100e91
commit 1b6b02ef5a

154
src/v0.rs
View File

@@ -517,7 +517,7 @@ pub struct Block{
#[binrw]
#[brw(little)]
#[derive(Clone,Copy)]
enum EventType{
pub enum EventType{
#[brw(magic=1u32)]
Input,
#[brw(magic=2u32)]
@@ -775,12 +775,65 @@ 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])
}
// 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]
}
/// 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,
@@ -790,7 +843,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
@@ -800,26 +853,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()
@@ -831,43 +870,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{
@@ -875,24 +913,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
@@ -901,18 +939,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
});
@@ -987,7 +1019,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;