forked from StrafesNET/roblox-bot-player
236 lines
7.0 KiB
Rust
236 lines
7.0 KiB
Rust
use wasm_bindgen::prelude::wasm_bindgen;
|
|
use wasm_bindgen::JsError;
|
|
use strafesnet_roblox_bot_file::v0;
|
|
use strafesnet_roblox_bot_player::{bot,bvh,head,time,graphics};
|
|
use strafesnet_graphics::{setup,surface};
|
|
|
|
// Hack to keep the code compiling,
|
|
// SurfaceTarget::Canvas is not available in IDE for whatever reason.
|
|
struct ToSurfaceTarget(web_sys::HtmlCanvasElement);
|
|
impl From<ToSurfaceTarget> for wgpu::SurfaceTarget<'static>{
|
|
fn from(ToSurfaceTarget(canvas):ToSurfaceTarget)->Self{
|
|
#[cfg(target_arch = "wasm32")]
|
|
let target=wgpu::SurfaceTarget::Canvas(canvas);
|
|
#[expect(unused)]
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
let target=panic!("{canvas:?}");
|
|
#[allow(unused)]
|
|
target
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct Graphics{
|
|
graphics:graphics::Graphics,
|
|
surface:surface::Surface<'static>,
|
|
device:wgpu::Device,
|
|
queue:wgpu::Queue,
|
|
}
|
|
#[wasm_bindgen]
|
|
pub async fn setup_graphics(canvas:web_sys::HtmlCanvasElement)->Result<Graphics,JsError>{
|
|
let size=glam::uvec2(canvas.width(),canvas.height());
|
|
|
|
let instance_desc=wgpu::InstanceDescriptor::from_env_or_default();
|
|
let instance=wgpu::util::new_instance_with_webgpu_detection(&instance_desc).await;
|
|
let surface=setup::step2::create_surface(&instance,ToSurfaceTarget(canvas)).map_err(|e|JsError::new(&e.to_string()))?;
|
|
let adapter=instance.request_adapter(&wgpu::RequestAdapterOptions{
|
|
power_preference:wgpu::PowerPreference::HighPerformance,
|
|
force_fallback_adapter:false,
|
|
compatible_surface:Some(&surface),
|
|
}).await.map_err(|e|JsError::new(&e.to_string()))?;
|
|
let (device,queue)=setup::step4::request_device(&adapter).await.map_err(|e|JsError::new(&e.to_string()))?;
|
|
let surface=setup::step5::configure_surface(&adapter,&device,surface,(size.x,size.y)).map_err(|e|JsError::new(&e.to_string()))?;
|
|
Ok(Graphics{
|
|
graphics:graphics::Graphics::new(&device,&queue,size,surface.view_format()),
|
|
surface,
|
|
device,
|
|
queue,
|
|
})
|
|
}
|
|
#[wasm_bindgen]
|
|
impl Graphics{
|
|
#[wasm_bindgen]
|
|
pub fn render(&mut self,bot:&CompleteBot,head:&PlaybackHead,time:f64){
|
|
let time=time::from_float(time).unwrap();
|
|
let (pos,angles)=head.head.get_position_angles(&bot.bot,time);
|
|
let frame=self.surface.new_frame(&self.device);
|
|
let mut encoder=self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor{label:None});
|
|
self.graphics.encode_commands(&mut encoder,frame.view(),pos,angles);
|
|
self.queue.submit([encoder.finish()]);
|
|
frame.present();
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn resize(&mut self,width:u32,height:u32,fov_slope_x:f32,fov_slope_y:f32){
|
|
let size=[width,height].into();
|
|
self.surface.configure(&self.device,size);
|
|
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);
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct CompleteBot{
|
|
bot:bot::CompleteBot,
|
|
}
|
|
#[wasm_bindgen]
|
|
impl CompleteBot{
|
|
#[wasm_bindgen(constructor)]
|
|
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),
|
|
})
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn duration(&self)->f64{
|
|
self.bot.duration().into()
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn run_duration(&self,mode_id:u32)->Option<f64>{
|
|
let mode=v0::ModeID(mode_id);
|
|
Some(self.bot.run_duration(mode)?.into())
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct CompleteMap{
|
|
map:strafesnet_common::map::CompleteMap,
|
|
}
|
|
#[wasm_bindgen]
|
|
impl CompleteMap{
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(data:&[u8])->Result<Self,JsError>{
|
|
let map=strafesnet_snf::read_map(std::io::Cursor::new(data))
|
|
.map_err(|e|JsError::new(&e.to_string()))?
|
|
.into_complete_map()
|
|
.map_err(|e|JsError::new(&e.to_string()))?;
|
|
Ok(Self{
|
|
map,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct PlaybackHead{
|
|
head:head::PlaybackHead,
|
|
}
|
|
#[wasm_bindgen]
|
|
impl PlaybackHead{
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(bot:&CompleteBot,time:f64)->Self{
|
|
let time=time::from_float(time).unwrap();
|
|
Self{
|
|
head:head::PlaybackHead::new(&bot.bot,time),
|
|
}
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn advance_time(&mut self,bot:&CompleteBot,time:f64){
|
|
let time=time::from_float(time).unwrap();
|
|
self.head.advance_time(&bot.bot,time);
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn set_paused(&mut self,time:f64,paused:bool){
|
|
let time=time::from_float(time).unwrap();
|
|
self.head.set_paused(time,paused);
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_scale(&mut self)->f64{
|
|
let scale=self.head.get_scale();
|
|
scale.num() as f64/scale.den() as f64
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn set_scale(&mut self,time:f64,scale:f64){
|
|
let time=time::from_float(time).unwrap();
|
|
self.head.set_scale(time,scale.try_into().unwrap());
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_head_time(&self,time:f64)->f64{
|
|
let time=time::from_float(time).unwrap();
|
|
let time=self.head.time(time);
|
|
time.into()
|
|
}
|
|
/// Set the playback head position to new_time.
|
|
#[wasm_bindgen]
|
|
pub fn set_head_time(&mut self,bot:&CompleteBot,time:f64,new_time:f64){
|
|
let time=time::from_float(time).unwrap();
|
|
let new_time=time::from_float(new_time).unwrap();
|
|
self.head.set_time(&bot.bot,time,new_time);
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_run_time(&self,bot:&CompleteBot,time:f64,mode_id:u32)->Option<f64>{
|
|
let time=time::from_float(time).unwrap();
|
|
let time=bot.bot.time(self.head.time(time));
|
|
let mode=v0::ModeID(mode_id);
|
|
let run_time=self.head.state().get_run(mode)?.time(time);
|
|
Some(run_time.into())
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn is_run_in_progress(&self,mode_id:u32)->Option<bool>{
|
|
let mode=v0::ModeID(mode_id);
|
|
Some(self.head.state().get_run(mode)?.is_in_progress())
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn is_run_finished(&self,mode_id:u32)->Option<bool>{
|
|
let mode=v0::ModeID(mode_id);
|
|
Some(self.head.state().get_run(mode)?.is_finished())
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_fov_slope_y(&self)->f64{
|
|
self.head.state().get_fov_y()
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_speed(&self,bot:&CompleteBot,time:f64)->f32{
|
|
let time=time::from_float(time).unwrap();
|
|
let velocity=self.head.get_velocity(&bot.bot,time);
|
|
|
|
use glam::Vec3Swizzles;
|
|
velocity.xz().length()
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_game_controls(&self)->u32{
|
|
self.head.state().get_controls().bits()
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn get_position(&self,bot:&CompleteBot,time:f64)->Vector3{
|
|
let time=time::from_float(time).unwrap();
|
|
let position=self.head.get_position(&bot.bot,time);
|
|
Vector3(position)
|
|
}
|
|
/// Returns the camera angles yaw delta between the last game tick and the most recent game tick.
|
|
#[wasm_bindgen]
|
|
pub fn get_angles_yaw_delta(&self)->f32{
|
|
self.head.state().get_angles_delta().y
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct Vector3(glam::Vec3);
|
|
#[wasm_bindgen]
|
|
impl Vector3{
|
|
#[wasm_bindgen]
|
|
pub fn to_array(&self)->Vec<f32>{
|
|
self.0.to_array().to_vec()
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct Bvh{
|
|
bvh:bvh::Bvh,
|
|
}
|
|
#[wasm_bindgen]
|
|
impl Bvh{
|
|
#[wasm_bindgen(constructor)]
|
|
pub fn new(bot:&CompleteBot)->Self{
|
|
Self{
|
|
bvh:bvh::Bvh::new(&bot.bot),
|
|
}
|
|
}
|
|
#[wasm_bindgen]
|
|
pub fn closest_time_to_point(&self,bot:&CompleteBot,point:&Vector3)->Option<f64>{
|
|
Some(bot.bot.playback_time(self.bvh.closest_time_to_point(&bot.bot,point.0)?).into())
|
|
}
|
|
}
|