add proper errors

This commit is contained in:
2026-03-18 10:25:35 -07:00
parent 20b86ee3c0
commit a3d8f5773e
8 changed files with 59 additions and 26 deletions

1
Cargo.lock generated
View File

@@ -1938,6 +1938,7 @@ dependencies = [
"strafesnet_common",
"strafesnet_graphics",
"strafesnet_roblox_bot_file",
"thiserror 2.0.18",
"wgpu",
]

View File

@@ -6,7 +6,7 @@ use strafesnet_common::session::Time as SessionTime;
fn main(){
let bot=include_bytes!("../../web-demo/bhop_marble_7cf33a64-7120-4514-b9fa-4fe29d9523d.qbot");
let timelines=v0::read_all_to_block(std::io::Cursor::new(bot)).unwrap();
let bot=bot::CompleteBot::new(timelines);
let bot=bot::CompleteBot::new(timelines).unwrap();
let bvh=bvh::Bvh::new(&bot);
// sample the position at 0.24s
@@ -28,7 +28,7 @@ fn main(){
fn get_position_no_panic(){
let bot_file=include_bytes!("../../files/000002d3-852a-4e9f-b0c9-c95411683806.qbot");
let timelines=v0::read_all_to_block(std::io::Cursor::new(bot_file)).unwrap();
let bot=bot::CompleteBot::new(timelines);
let bot=bot::CompleteBot::new(timelines).unwrap();
let head=head::PlaybackHead::new(&bot,SessionTime::ZERO);
// This can panic if the head is mismanaged!
let _pos=head.get_position(&bot,SessionTime::ZERO);
@@ -38,7 +38,7 @@ fn get_position_no_panic(){
fn get_position_no_panic2(){
let bot_file=include_bytes!("../../files/03f3eb2c-d33d-44ea-ba60-67b685d1140d.qbot");
let timelines=v0::read_all_to_block(std::io::Cursor::new(bot_file)).unwrap();
let bot=bot::CompleteBot::new(timelines);
let bot=bot::CompleteBot::new(timelines).unwrap();
println!("duration={}",bot.duration());
println!("num_events={}",bot.timelines().output_events.len());
for event in &bot.timelines().output_events{

View File

@@ -9,3 +9,4 @@ wgpu.workspace = true
strafesnet_common.workspace = true
strafesnet_graphics.workspace = true
strafesnet_roblox_bot_file.workspace = true
thiserror = "2.0.18"

View File

@@ -4,6 +4,25 @@ use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInn
use strafesnet_roblox_bot_file::v0;
use crate::head::{Time as PlaybackTime,TimeInner as PlaybackTimeInner};
use crate::time;
#[derive(Debug,thiserror::Error)]
pub enum Error{
#[error("Bot output timeline has no events")]
NoOutputEvents,
#[error("Time conversion failed: {0}")]
Time(#[from]time::Error),
}
#[derive(Debug,thiserror::Error)]
pub enum RunDurationError{
#[error("Bot run timeline has no RunStart event")]
NoRunStart,
#[error("Bot run timeline has no RunFinish event")]
NoRunFinish,
#[error("Time conversion failed: {0}")]
Time(time::Error),
}
/// A loaded bot file.
pub struct CompleteBot{
@@ -17,19 +36,21 @@ impl CompleteBot{
pub(crate) const CAMERA_OFFSET:glam::Vec3=glam::vec3(0.0,2.0,0.0);
pub fn new(
timelines:v0::Block,
)->Self{
let start=crate::time::from_float(timelines.output_events.first().unwrap().time).unwrap();
let end=crate::time::from_float(timelines.output_events.last().unwrap().time).unwrap();
)->Result<Self,Error>{
let start_event=timelines.output_events.first().ok_or(Error::NoOutputEvents)?;
let end_event=timelines.output_events.last().ok_or(Error::NoOutputEvents)?;
let start=time::from_float(start_event.time).map_err(Error::Time)?;
let end=time::from_float(end_event.time).map_err(Error::Time)?;
let world_position=timelines.world_events.iter().find_map(|event|match &event.event{
v0::WorldEvent::Reset(world_reset_event)=>Some(world_reset_event.position),
_=>None,
}).expect("Map must contain a WorldReset event");
Self{
Ok(Self{
timer:TimerFixed::new(PlaybackTime::ZERO,start),
duration:end-start,
timelines,
world_offset:glam::vec3(world_position.x,world_position.y,world_position.z),
}
})
}
pub fn time(&self,time:PlaybackTime)->PhysicsTime{
self.timer.time(time)
@@ -47,18 +68,18 @@ impl CompleteBot{
pub const fn timelines(&self)->&v0::Block{
&self.timelines
}
pub fn run_duration(&self,mode_id:v0::ModeID)->Option<RunTime>{
pub fn run_duration(&self,mode_id:v0::ModeID)->Result<RunTime,RunDurationError>{
let mut it=self.timelines.run_events.iter().rev();
let end=it.find_map(|event|match &event.event{
v0::RunEvent::Finish(run_start_event) if run_start_event.mode==mode_id=>Some(event.time),
_=>None,
})?;
}).ok_or(RunDurationError::NoRunStart)?;
let start=it.find_map(|event|match &event.event{
v0::RunEvent::Start(run_start_event) if run_start_event.mode==mode_id=>Some(event.time),
_=>None,
})?;
let start=crate::time::from_float(start).unwrap();
let end=crate::time::from_float(end).unwrap();
Some(end-start)
}).ok_or(RunDurationError::NoRunFinish)?;
let start=time::from_float(start).map_err(RunDurationError::Time)?;
let end=time::from_float(end).map_err(RunDurationError::Time)?;
Ok(end-start)
}
}

View File

@@ -1,5 +1,13 @@
use strafesnet_graphics::graphics::GraphicsState;
#[derive(Debug,thiserror::Error)]
pub enum ChangeMapError{
#[error("Map does not have a main mode")]
NoMainMode,
#[error("Map does not have a start zone")]
NoStartZone,
}
/// The graphics state, essentially a handle to all the information on the GPU.
pub struct Graphics{
graphics:GraphicsState,
@@ -13,13 +21,14 @@ impl Graphics{
start_offset:glam::Vec3::ZERO,
}
}
pub fn change_map(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&strafesnet_common::map::CompleteMap){
pub fn change_map(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&strafesnet_common::map::CompleteMap)->Result<(),ChangeMapError>{
self.graphics.clear();
self.graphics.generate_models(device,queue,map);
let modes=map.modes.clone().denormalize();
let mode=modes.get_mode(strafesnet_common::gameplay_modes::ModeId::MAIN).expect("Map does not have a main mode");
let start_zone=map.models.get(mode.get_start().get() as usize).expect("Map does not have a start zone");
let mode=modes.get_mode(strafesnet_common::gameplay_modes::ModeId::MAIN).ok_or(ChangeMapError::NoMainMode)?;
let start_zone=map.models.get(mode.get_start().get() as usize).ok_or(ChangeMapError::NoStartZone)?;
self.start_offset=glam::Vec3::from_array(start_zone.transform.translation.map(|f|f.into()).to_array());
Ok(())
}
pub fn resize(&mut self,device:&wgpu::Device,size:glam::UVec2,fov:glam::Vec2){
self.graphics.resize(device,size,fov);

View File

@@ -99,10 +99,10 @@ impl<'a> PlayerWorker<'a>{
self.graphics_thread.resize(device,size,fov);
},
Instruction::ChangeMap(complete_map)=>{
self.graphics_thread.change_map(device,queue,&complete_map);
self.graphics_thread.change_map(device,queue,&complete_map).unwrap();
},
Instruction::LoadReplay(bot)=>{
let bot=CompleteBot::new(bot);
let bot=CompleteBot::new(bot).unwrap();
let playback_head=PlaybackHead::new(&bot,SessionTime::ZERO);
self.playback=Some(Playback{
bot,

View File

@@ -126,7 +126,7 @@ fn encode(params:EncodeParams)->Result<(),EncodeError>{
.map_err(EncodeError::CreateDevice)?;
// playback
let bot=strafesnet_roblox_bot_player::bot::CompleteBot::new(timelines);
let bot=strafesnet_roblox_bot_player::bot::CompleteBot::new(timelines).unwrap();
let mut playback_head=strafesnet_roblox_bot_player::head::PlaybackHead::new(&bot,SessionTime::ZERO);
let mut wgpu_state = WgpuState::new(
@@ -371,7 +371,7 @@ impl WgpuState {
}
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
self.graphics.change_map(&self.device,&self.queue,map);
self.graphics.change_map(&self.device,&self.queue,map).unwrap();
}
fn render(&mut self,pos:glam::Vec3,angles:glam::Vec2) {

View File

@@ -66,8 +66,9 @@ impl Graphics{
self.graphics.resize(&self.device,size,[fov_slope_x as f32,fov_slope_y as f32].into());
}
#[wasm_bindgen]
pub fn change_map(&mut self,map:&CompleteMap){
self.graphics.change_map(&self.device,&self.queue,&map.map);
pub fn change_map(&mut self,map:&CompleteMap)->Result<(),JsError>{
self.graphics.change_map(&self.device,&self.queue,&map.map).map_err(|e|JsError::new(&e.to_string()))?;
Ok(())
}
}
@@ -81,7 +82,7 @@ impl CompleteBot{
pub fn new(data:&[u8])->Result<Self,JsError>{
let timelines=v0::read_all_to_block(std::io::Cursor::new(data)).map_err(|e|JsError::new(&e.to_string()))?;
Ok(Self{
bot:bot::CompleteBot::new(timelines),
bot:bot::CompleteBot::new(timelines).map_err(|e|JsError::new(&e.to_string()))?,
})
}
#[wasm_bindgen]
@@ -89,9 +90,9 @@ impl CompleteBot{
self.bot.duration().into()
}
#[wasm_bindgen]
pub fn run_duration(&self,mode_id:u32)->Option<f64>{
pub fn run_duration(&self,mode_id:u32)->Result<f64,JsError>{
let mode=v0::ModeID(mode_id);
Some(self.bot.run_duration(mode)?.into())
Ok(self.bot.run_duration(mode).map_err(|e|JsError::new(&e.to_string()))?.into())
}
}