diff --git a/Cargo.toml b/Cargo.toml index e749130..3d84482 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,16 +10,14 @@ exclude = [".git*", "CMake*", "collision_meshes", "rustfmt.toml", "*.py", "pytho # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -autocxx = "0.26.0" cxx = { version = "1.0.83", features = ["c++20"] } glam = { version = "0.27.0", optional = true } serde = { version = "1.0.195", optional = true, features = ["derive"] } [build-dependencies] glob = "0.3.0" -autocxx-build = "0.26.0" -miette = { version = "5", features = ["fancy"] } cc = { version = "1.0.83", features = ["parallel"] } +cxx-build = "1.0.122" [profile.release] lto = true diff --git a/README.md b/README.md index 61efab9..b5043ae 100644 --- a/README.md +++ b/README.md @@ -93,3 +93,32 @@ fn main() { println!("New ball location: {}", arena.pin_mut().get_ball().pos); } ``` + +## Benchmarks + +Numbers are from a system running Ubuntu 23.10 with a Ryzen 9 5900X and 3600MHz CL18 RAM. + +Numbers _will_ vary depending on your system. Only default features are enabled. + +- `real_bench`: + + ```bash + Running on 24 threads + Simulated 2.78 hours in 1.485 seconds + FPS: 808348.4 + ``` + +- `cpu_bench`: + + ```bash + Running on 24 threads + Simulated 55.56 hours in 0.893 seconds + FPS: 26883894 + ``` + +- `thread_bench` (1 thread): + + ```bash + Simulated 0.58 hours in 3.556 seconds + FPS: 70312.516 + ``` diff --git a/arenar/arenar.h b/arenar/arenar.h index 704c83c..6014dca 100644 --- a/arenar/arenar.h +++ b/arenar/arenar.h @@ -1,7 +1,7 @@ #pragma once #include "RocketSim.h" -#include "cxx.h" +#include "rust/cxx.h" using namespace RocketSim; diff --git a/build.rs b/build.rs index c864b03..a3da452 100644 --- a/build.rs +++ b/build.rs @@ -1,11 +1,30 @@ -use autocxx_build::Builder; +use cxx_build::bridges; use glob::glob; -use miette::{IntoDiagnostic, Result}; +use std::path::PathBuf; -fn main() -> Result<()> { - let mut builder = Builder::new("src/lib.rs", ["RocketSim/src/", "arenar/"]) - .extra_clang_args(&["-std=c++20", "-march=native"]) - .build()?; +fn main() { + println!("cargo:rerun-if-changed=src/lib.rs"); + + let cpp_files = glob("RocketSim/libsrc/bullet3-3.24/**/*.cpp") + .unwrap() + .chain(glob("RocketSim/src/**/*.cpp").unwrap()) + .flatten() + .chain([PathBuf::from("arenar/arenar.cpp")]) + .collect::>(); + + for file in &cpp_files { + println!("cargo:rerun-if-changed={}", file.display()); + } + + let rust_files: Vec = glob("src/sim/*.rs") + .unwrap() + .chain(glob("RocketSim/src/**/*.rs").unwrap()) + .flatten() + .filter(|path| !path.ends_with("mod.rs")) + .chain([PathBuf::from("src/math.rs"), PathBuf::from("src/lib.rs")]) + .collect::>(); + + let mut builder = bridges(rust_files); if !cfg!(debug_assertions) || !cfg!(feature = "debug_logging") { builder.define("RS_DONT_LOG", "1"); @@ -16,18 +35,12 @@ fn main() -> Result<()> { } builder + .includes(["RocketSim/src/", "arenar/"]) + .std("c++20") .use_plt(false) .flag_if_supported("-march=native") - .flag_if_supported("-std=c++20") - .flag_if_supported("/std:c++20") .flag_if_supported("-w") - .files(glob("RocketSim/libsrc/bullet3-3.24/**/*.cpp").into_diagnostic()?.flatten()) - .files(glob("RocketSim/src/**/*.cpp").into_diagnostic()?.flatten()) - .file("arenar/arenar.cpp") + .files(cpp_files) .warnings(false) .compile("rocketsim"); - - println!("cargo:rerun-if-changed=src/lib.rs"); - - Ok(()) } diff --git a/examples/cpu_bench.rs b/examples/cpu_bench.rs index 5bd0112..6058870 100644 --- a/examples/cpu_bench.rs +++ b/examples/cpu_bench.rs @@ -5,7 +5,7 @@ use std::{ }; fn main() { - const TICKS: u32 = 600_000; + const TICKS: u32 = 1_000_000; rocketsim_rs::init(None); diff --git a/examples/thread_bench.rs b/examples/thread_bench.rs index d0f4737..be39f42 100644 --- a/examples/thread_bench.rs +++ b/examples/thread_bench.rs @@ -2,7 +2,7 @@ use rocketsim_rs::sim::{Arena, CarConfig, Team}; use std::time::Instant; fn main() { - const TICKS: u32 = 50000; + const TICKS: u32 = 250_000; // load in assets rocketsim_rs::init(None); diff --git a/src/ext.rs b/src/ext.rs index cd68fd1..c5dc617 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -1,7 +1,5 @@ use crate::{ - consts, - extra::AngleFromRotMat, - extra::CreateArena, + base, consts, math::{Angle, RotMat, Vec3}, sim::{ Arena, ArenaConfig, ArenaMemWeightMode, BallHitInfo, BallState, BoostPadState, CarConfig, CarContact, CarControls, @@ -17,6 +15,44 @@ use crate::serde_utils; #[cfg(feature = "serde_utils")] use serde::{Deserialize, Serialize}; +impl CarConfig { + #[inline] + #[must_use] + pub fn octane() -> &'static Self { + base::get_octane() + } + + #[inline] + #[must_use] + pub fn dominus() -> &'static Self { + base::get_dominus() + } + + #[inline] + #[must_use] + pub fn plank() -> &'static Self { + base::get_plank() + } + + #[inline] + #[must_use] + pub fn breakout() -> &'static Self { + base::get_breakout() + } + + #[inline] + #[must_use] + pub fn hybrid() -> &'static Self { + base::get_hybrid() + } + + #[inline] + #[must_use] + pub fn merc() -> &'static Self { + base::get_merc() + } +} + impl Default for ArenaConfig { fn default() -> Self { Self { @@ -149,7 +185,7 @@ impl Arena { /// /// Tick rate MUST be equal to or between 15 and 120 pub fn new(game_mode: GameMode, config: ArenaConfig, tick_rate: u8) -> UniquePtr { - CreateArena(game_mode, config, tick_rate) + base::CreateArena(game_mode, config, tick_rate) } #[inline] @@ -480,7 +516,7 @@ impl Angle { #[inline] #[must_use] pub fn from_rotmat(rot_mat: RotMat) -> Self { - AngleFromRotMat(rot_mat) + base::AngleFromRotMat(rot_mat) } } diff --git a/src/lib.rs b/src/lib.rs index 32775ae..f7b0297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,19 +16,14 @@ mod serde_utils; pub use serde; pub mod consts; +pub mod math; +pub mod sim; mod ext; -pub use autocxx; pub use cxx; pub use ext::*; -// dummy gen so that the build script doesn't fail -autocxx::include_cpp! { - #include "arenar.h" - name!(base) -} - #[repr(u8)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum Stages { @@ -45,7 +40,7 @@ unsafe impl cxx::ExternType for Stages { } #[cxx::bridge] -mod extra { +mod base { unsafe extern "C++" { include!("arenar.h"); @@ -102,652 +97,10 @@ mod extra { } } -pub use extra::{get_stage, init_from_mem}; +pub use base::{get_stage, init_from_mem}; #[inline] /// Initializes the collision mesh system for `RocketSim` pub fn init(collision_meshes_folder: Option<&str>) { - extra::Init(collision_meshes_folder.unwrap_or("collision_meshes")); -} - -impl sim::CarConfig { - #[inline] - #[must_use] - pub fn octane() -> &'static Self { - extra::get_octane() - } - - #[inline] - #[must_use] - pub fn dominus() -> &'static Self { - extra::get_dominus() - } - - #[inline] - #[must_use] - pub fn plank() -> &'static Self { - extra::get_plank() - } - - #[inline] - #[must_use] - pub fn breakout() -> &'static Self { - extra::get_breakout() - } - - #[inline] - #[must_use] - pub fn hybrid() -> &'static Self { - extra::get_hybrid() - } - - #[inline] - #[must_use] - pub fn merc() -> &'static Self { - extra::get_merc() - } -} - -pub mod sim { - #[cxx::bridge(namespace = "RocketSim")] - mod carcontrols { - unsafe extern "C++" { - include!("Sim/CarControls.h"); - - type CarControls; - } - - #[derive(Clone, Copy, Debug, Default)] - struct CarControls { - pub throttle: f32, - pub steer: f32, - pub pitch: f32, - pub yaw: f32, - pub roll: f32, - pub boost: bool, - pub jump: bool, - pub handbrake: bool, - } - } - - pub use carcontrols::CarControls; - - #[cxx::bridge(namespace = "RocketSim")] - mod arenaconfig { - unsafe extern "C++" { - include!("Sim/Arena/ArenaConfig/ArenaConfig.h"); - - type ArenaConfig; - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - type ArenaMemWeightMode = crate::sim::ArenaMemWeightMode; - } - - #[derive(Clone, Copy, Debug)] - struct ArenaConfig { - mem_weight_mode: ArenaMemWeightMode, - min_pos: Vec3, - max_pos: Vec3, - max_aabb_len: f32, - no_ball_rot: bool, - use_custom_broadphase: bool, - max_objects: u32, - } - } - - pub use arenaconfig::ArenaConfig; - - #[repr(u8)] - #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] - pub enum GameMode { - #[default] - Soccar, - Hoops, - Heatseeker, - Snowday, - TheVoid, - } - - unsafe impl cxx::ExternType for GameMode { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::GameMode"); - type Kind = cxx::kind::Trivial; - } - - #[repr(u8)] - #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] - pub enum ArenaMemWeightMode { - #[default] - Heavy, - Light, - } - - unsafe impl cxx::ExternType for ArenaMemWeightMode { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::ArenaMemWeightMode"); - type Kind = cxx::kind::Trivial; - } - - #[cxx::bridge] - mod arena_cxx { - unsafe extern "C++" { - include!("arenar.h"); - - #[rust_name = "Arena"] - type Arenar; - - #[namespace = "RocketSim"] - type CarState = crate::sim::CarState; - #[namespace = "RocketSim"] - type BallState = crate::sim::BallState; - #[cxx_name = "EBoostPadState"] - type BoostPadState = crate::sim::BoostPadState; - #[namespace = "RocketSim"] - type CarConfig = crate::sim::CarConfig; - #[namespace = "RocketSim"] - type CarControls = crate::sim::CarControls; - #[namespace = "RocketSim"] - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - #[namespace = "RocketSim"] - type Team = crate::sim::Team; - #[namespace = "RocketSim"] - type MutatorConfig = crate::sim::MutatorConfig; - #[namespace = "RocketSim"] - type GameMode = crate::sim::GameMode; - #[allow(dead_code)] - #[namespace = "RocketSim"] - type ArenaMemWeightMode = crate::sim::ArenaMemWeightMode; - - #[must_use] - #[doc(hidden)] - #[rust_name = "rsc"] - fn SetCar(self: Pin<&mut Arena>, car_id: u32, car: CarState) -> bool; - - #[must_use] - #[doc(hidden)] - #[rust_name = "rscc"] - fn SetCarControls(self: Pin<&mut Arena>, car_id: u32, car_controls: CarControls) -> bool; - - #[doc(hidden)] - #[rust_name = "rmvc"] - fn RemoveCar(self: Pin<&mut Arena>, car_id: u32) -> bool; - - #[doc(hidden)] - #[rust_name = "rtrk"] - fn ResetToRandomKickoff(self: Pin<&mut Arena>, seed: i32); - - #[doc(hidden)] - #[rust_name = "dc"] - fn DemolishCar(self: Pin<&mut Arena>, car_id: u32) -> bool; - - #[doc(hidden)] - #[rust_name = "rspc"] - fn RespawnCar(self: Pin<&mut Arena>, car_id: u32, seed: i32, boost_amount: f32) -> bool; - - #[doc(hidden)] - #[rust_name = "ibpgi"] - fn IsBallProbablyGoingIn(self: &Arena, max_time: f32, extra_margin: f32) -> bool; - - /// Returns all of the car ids" - #[must_use] - #[cxx_name = "GetCars"] - fn get_cars(self: &Arena) -> Vec; - - /// Returns the car state of the car with the given id - #[must_use] - #[cxx_name = "GetCar"] - fn get_car(self: Pin<&mut Arena>, car_id: u32) -> CarState; - - /// Adds a car to the arena with the given team and car config - #[must_use] - #[cxx_name = "AddCar"] - fn add_car(self: Pin<&mut Arena>, team: Team, car_config: &CarConfig) -> u32; - - /// Returns the ball state - #[must_use] - #[cxx_name = "GetBall"] - fn get_ball(self: Pin<&mut Arena>) -> BallState; - /// Sets the ball state - - #[cxx_name = "SetBall"] - fn set_ball(self: Pin<&mut Arena>, ball: BallState); - - /// Returns the position of the pad with the given index - #[must_use] - #[cxx_name = "GetPadPos"] - fn get_pad_pos(self: &Arena, index: usize) -> Vec3; - /// Sets the state of the pad with the given index - - #[cxx_name = "SetPadState"] - fn set_pad_state(self: Pin<&mut Arena>, index: usize, pad: BoostPadState); - - /// Returns the state of the pad with the given index - #[must_use] - #[cxx_name = "GetPadState"] - fn get_pad_state(self: &Arena, index: usize) -> BoostPadState; - - /// Returns the car config of the car with the given id - #[must_use] - #[cxx_name = "GetCarConfig"] - fn get_car_config(self: &Arena, id: u32) -> CarConfig; - - /// Returns the team of the car with the given id - #[must_use] - #[cxx_name = "GetCarTeam"] - fn get_car_team(self: &Arena, id: u32) -> Team; - /// Sets the goal scored callback - - #[cxx_name = "SetGoalScoreCallback"] - fn set_goal_scored_callback( - self: Pin<&mut Arena>, - callback: fn(arena: Pin<&mut Arena>, car_team: Team, user_data: usize), - user_data: usize, - ); - /// Sets the car bump callback - - #[cxx_name = "SetCarBumpCallback"] - fn set_car_bump_callback( - self: Pin<&mut Arena>, - callback: fn(arena: Pin<&mut Arena>, bumper: u32, victim: u32, is_demo: bool, user_data: usize), - user_data: usize, - ); - - /// Returns the mutator config - #[must_use] - #[cxx_name = "GetMutatorConfig"] - fn get_mutator_config(self: &Arena) -> MutatorConfig; - - /// Sets the mutator config - #[cxx_name = "SetMutatorConfig"] - fn set_mutator_config(self: Pin<&mut Arena>, config: MutatorConfig); - - /// Deep clone the arena, optionally copying the callbacks - /// - /// If `copy_callbacks` is true, the callbacks will be copied, - /// otherwise the new arena will have no callbacks - #[must_use] - #[cxx_name = "Clone"] - fn clone(self: &Arena, copy_callbacks: bool) -> UniquePtr; - - /// Returns the number of cars in the arena - #[cxx_name = "NumCars"] - fn num_cars(self: &Arena) -> usize; - - /// Returns the radius of the ball - #[cxx_name = "GetBallRadius"] - fn get_ball_radius(self: &Arena) -> f32; - - /// Returns the number of pads in the arena - #[cxx_name = "NumPads"] - fn num_pads(self: &Arena) -> usize; - - /// Returns if the pad with the given index is big (gives 100 boost instead of 12) - #[cxx_name = "GetPadIsBig"] - fn get_pad_is_big(self: &Arena, index: usize) -> bool; - - /// Resets the tick count - #[cxx_name = "ResetTickCount"] - fn reset_tick_count(self: Pin<&mut Arena>); - - /// Returns the tick count - #[cxx_name = "GetTickCount"] - fn get_tick_count(self: &Arena) -> u64; - - /// Returns the tick rate (i.e. `0.008333` aka `1 / 120`) - #[cxx_name = "GetTickRate"] - fn get_tick_rate(self: &Arena) -> f32; - - /// Returns the game mode - #[cxx_name = "GetGameMode"] - fn get_game_mode(self: &Arena) -> GameMode; - - /// Steps the simulation by the given number of ticks - #[cxx_name = "Step"] - fn step(self: Pin<&mut Arena>, num_ticks: u32); - - /// Returns if the ball is within a goal - #[cxx_name = "IsBallScored"] - fn is_ball_scored(self: &Arena) -> bool; - } - - impl UniquePtr {} - } - - pub use arena_cxx::Arena; - - unsafe impl Send for Arena {} - - #[cxx::bridge(namespace = "RocketSim")] - mod ballhitinfo { - unsafe extern "C++" { - include!("Sim/BallHitInfo/BallHitInfo.h"); - - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - type BallHitInfo; - } - - #[derive(Clone, Copy, Debug, Default)] - struct BallHitInfo { - is_valid: bool, - relative_pos_on_ball: Vec3, - ball_pos: Vec3, - extra_hit_vel: Vec3, - tick_count_when_hit: u64, - tick_count_when_extra_impulse_applied: u64, - } - } - - pub use ballhitinfo::BallHitInfo; - - #[derive(Clone, Copy, Debug)] - pub struct HeatseekerInfo { - /// Which net the ball should seek towards; - /// When 0, no net - pub y_target_dir: f32, - pub cur_target_speed: f32, - pub time_since_hit: f32, - } - - unsafe impl cxx::ExternType for HeatseekerInfo { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::BallState::HeatseekerInfo"); - type Kind = cxx::kind::Trivial; - } - - #[cxx::bridge(namespace = "RocketSim")] - mod ballstate { - unsafe extern "C++" { - include!("Sim/Ball/Ball.h"); - - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - type RotMat = crate::math::RotMat; - type BallState; - #[namespace = "RocketSim::BallState"] - type HeatseekerInfo = crate::sim::HeatseekerInfo; - } - - #[derive(Clone, Copy, Debug)] - struct BallState { - pos: Vec3, - rot_mat: RotMat, - vel: Vec3, - ang_vel: Vec3, - update_counter: u64, - hs_info: HeatseekerInfo, - } - } - - pub use ballstate::BallState; - - #[repr(u8)] - #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] - pub enum Team { - #[default] - Blue, - Orange, - } - - unsafe impl cxx::ExternType for Team { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::Team"); - type Kind = cxx::kind::Trivial; - } - - #[cxx::bridge(namespace = "RocketSim")] - mod carstate { - unsafe extern "C++" { - include!("Sim/Car/Car.h"); - - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - type RotMat = crate::math::RotMat; - type CarControls = crate::sim::CarControls; - type BallHitInfo = crate::sim::BallHitInfo; - - type CarState; - - #[cxx_name = "HasFlipOrJump"] - /// Returns if the car has flipped or jumped - fn has_flip_or_jump(self: &CarState) -> bool; - - #[cxx_name = "HasFlipReset"] - /// Returns if the car has a flip reset - fn has_flip_reset(self: &CarState) -> bool; - - #[cxx_name = "GotFlipReset"] - /// Returns if the car got a flip reset - fn got_flip_reset(self: &CarState) -> bool; - } - - #[derive(Clone, Copy, Debug)] - struct WorldContact { - has_contact: bool, - contact_normal: Vec3, - } - - #[derive(Clone, Copy, Debug)] - struct CarContact { - other_car_id: u32, - cooldown_timer: f32, - } - - #[derive(Clone, Copy, Debug)] - struct CarState { - pos: Vec3, - rot_mat: RotMat, - vel: Vec3, - ang_vel: Vec3, - update_counter: u64, - is_on_ground: bool, - wheels_with_contact: [bool; 4], - has_jumped: bool, - has_double_jumped: bool, - has_flipped: bool, - flip_rel_torque: Vec3, - jump_time: f32, - flip_time: f32, - is_flipping: bool, - is_jumping: bool, - air_time: f32, - air_time_since_jump: f32, - boost: f32, - time_spent_boosting: f32, - is_supersonic: bool, - supersonic_time: f32, - handbrake_val: f32, - is_auto_flipping: bool, - auto_flip_timer: f32, - auto_flip_torque_scale: f32, - world_contact: WorldContact, - car_contact: CarContact, - is_demoed: bool, - demo_respawn_timer: f32, - ball_hit_info: BallHitInfo, - last_controls: CarControls, - } - - impl UniquePtr {} - impl CxxVector {} - } - - pub use carstate::{CarContact, CarState, WorldContact}; - - #[cxx::bridge(namespace = "RocketSim")] - mod carconfig { - unsafe extern "C++" { - include!("Sim/Car/CarConfig/CarConfig.h"); - - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - - type WheelPairConfig; - type CarConfig; - } - - #[derive(Clone, Copy, Debug, Default)] - struct WheelPairConfig { - wheel_radius: f32, - suspension_rest_length: f32, - connection_point_offset: Vec3, - } - - #[derive(Clone, Copy, Debug, Default)] - struct CarConfig { - hitbox_size: Vec3, - hitbox_pos_offset: Vec3, - front_wheels: WheelPairConfig, - back_wheels: WheelPairConfig, - dodge_deadzone: f32, - } - } - - pub use carconfig::{CarConfig, WheelPairConfig}; - - #[cxx::bridge] - mod boostpadstate { - unsafe extern "C++" { - include!("arenar.h"); - - type EBoostPadState; - } - - #[derive(Clone, Copy, Debug, Default)] - struct EBoostPadState { - is_active: bool, - cooldown: f32, - cur_locked_car_id: u32, - prev_locked_car_id: u32, - } - } - - pub use boostpadstate::EBoostPadState as BoostPadState; - - #[repr(u8)] - #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] - pub enum DemoMode { - #[default] - Normal = 0, - OnContact = 1, - Disabled = 2, - } - - unsafe impl cxx::ExternType for DemoMode { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::DemoMode"); - type Kind = cxx::kind::Trivial; - } - - #[cxx::bridge(namespace = "RocketSim")] - mod mutators { - unsafe extern "C++" { - include!("Sim/MutatorConfig/MutatorConfig.h"); - - #[rust_name = "Vec3"] - type Vec = crate::math::Vec3; - type DemoMode = crate::sim::DemoMode; - - type MutatorConfig; - } - - #[derive(Clone, Copy, Debug)] - struct MutatorConfig { - gravity: Vec3, - car_mass: f32, - car_world_friction: f32, - car_world_restitution: f32, - ball_mass: f32, - ball_max_speed: f32, - ball_drag: f32, - ball_world_friction: f32, - ball_world_restitution: f32, - jump_accel: f32, - jump_immediate_force: f32, - boost_accel: f32, - boost_used_per_second: f32, - respawn_delay: f32, - bump_cooldown_time: f32, - boost_pad_cooldown_big: f32, - boost_pad_cooldown_small: f32, - car_spawn_boost_amount: f32, - ball_hit_extra_force_scale: f32, - bump_force_scale: f32, - ball_radius: f32, - unlimited_flips: bool, - unlimited_double_jumps: bool, - demo_mode: DemoMode, - enable_team_demos: bool, - } - } - - pub use mutators::MutatorConfig; -} - -pub mod math { - #[cfg(feature = "serde_utils")] - use serde::{Deserialize, Serialize}; - - #[repr(C, align(16))] - #[derive(Clone, Copy, Debug, Default, PartialEq)] - #[cfg_attr(feature = "serde_utils", derive(Serialize, Deserialize))] - pub struct Vec3 { - pub x: f32, - pub y: f32, - pub z: f32, - pub _w: f32, - } - - unsafe impl cxx::ExternType for Vec3 { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::Vec"); - type Kind = cxx::kind::Trivial; - } - - #[repr(C, align(16))] - #[derive(Clone, Copy, Debug, Default, PartialEq)] - #[cfg_attr(feature = "serde_utils", derive(Serialize, Deserialize))] - pub struct RotMat { - pub forward: Vec3, - pub right: Vec3, - pub up: Vec3, - } - - unsafe impl cxx::ExternType for RotMat { - #[allow(unused_attributes)] - #[doc(hidden)] - type Id = cxx::type_id!("RocketSim::RotMat"); - type Kind = cxx::kind::Trivial; - } - - #[cxx::bridge(namespace = "RocketSim")] - mod inner_math { - unsafe extern "C++" { - include!("Math/MathTypes/MathTypes.h"); - - type Angle; - type RotMat = crate::math::RotMat; - - #[must_use] - #[cxx_name = "ToRotMat"] - /// Converts the angle to a RocketSim rotation matrix - fn to_rotmat(self: &Angle) -> RotMat; - } - - #[derive(Clone, Copy, Debug, Default)] - struct Angle { - yaw: f32, - pitch: f32, - roll: f32, - } - } - - pub use inner_math::Angle; + base::Init(collision_meshes_folder.unwrap_or("collision_meshes")); } diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..62b6ba2 --- /dev/null +++ b/src/math.rs @@ -0,0 +1,59 @@ +#[cfg(feature = "serde_utils")] +use serde::{Deserialize, Serialize}; + +#[repr(C, align(16))] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde_utils", derive(Serialize, Deserialize))] +pub struct Vec3 { + pub x: f32, + pub y: f32, + pub z: f32, + pub _w: f32, +} + +unsafe impl cxx::ExternType for Vec3 { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::Vec"); + type Kind = cxx::kind::Trivial; +} + +#[repr(C, align(16))] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde_utils", derive(Serialize, Deserialize))] +pub struct RotMat { + pub forward: Vec3, + pub right: Vec3, + pub up: Vec3, +} + +unsafe impl cxx::ExternType for RotMat { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::RotMat"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge(namespace = "RocketSim")] +mod inner_math { + unsafe extern "C++" { + include!("Math/MathTypes/MathTypes.h"); + + type Angle; + type RotMat = crate::math::RotMat; + + #[must_use] + #[cxx_name = "ToRotMat"] + /// Converts the angle to a RocketSim rotation matrix + fn to_rotmat(self: &Angle) -> RotMat; + } + + #[derive(Clone, Copy, Debug, Default)] + struct Angle { + yaw: f32, + pitch: f32, + roll: f32, + } +} + +pub use inner_math::Angle; diff --git a/src/sim/arena.rs b/src/sim/arena.rs new file mode 100644 index 0000000..989b6bb --- /dev/null +++ b/src/sim/arena.rs @@ -0,0 +1,204 @@ +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub enum GameMode { + #[default] + Soccar, + Hoops, + Heatseeker, + Snowday, + TheVoid, +} + +unsafe impl cxx::ExternType for GameMode { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::GameMode"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge] +mod base { + unsafe extern "C++" { + include!("arenar.h"); + + #[rust_name = "Arena"] + type Arenar; + + #[namespace = "RocketSim"] + type CarState = crate::sim::CarState; + #[namespace = "RocketSim"] + type BallState = crate::sim::BallState; + #[cxx_name = "EBoostPadState"] + type BoostPadState = crate::sim::BoostPadState; + #[namespace = "RocketSim"] + type CarConfig = crate::sim::CarConfig; + #[namespace = "RocketSim"] + type CarControls = crate::sim::CarControls; + #[namespace = "RocketSim"] + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + #[namespace = "RocketSim"] + type Team = crate::sim::Team; + #[namespace = "RocketSim"] + type MutatorConfig = crate::sim::MutatorConfig; + #[namespace = "RocketSim"] + type GameMode = crate::sim::GameMode; + + #[must_use] + #[doc(hidden)] + #[rust_name = "rsc"] + fn SetCar(self: Pin<&mut Arena>, car_id: u32, car: CarState) -> bool; + + #[must_use] + #[doc(hidden)] + #[rust_name = "rscc"] + fn SetCarControls(self: Pin<&mut Arena>, car_id: u32, car_controls: CarControls) -> bool; + + #[doc(hidden)] + #[rust_name = "rmvc"] + fn RemoveCar(self: Pin<&mut Arena>, car_id: u32) -> bool; + + #[doc(hidden)] + #[rust_name = "rtrk"] + fn ResetToRandomKickoff(self: Pin<&mut Arena>, seed: i32); + + #[doc(hidden)] + #[rust_name = "dc"] + fn DemolishCar(self: Pin<&mut Arena>, car_id: u32) -> bool; + + #[doc(hidden)] + #[rust_name = "rspc"] + fn RespawnCar(self: Pin<&mut Arena>, car_id: u32, seed: i32, boost_amount: f32) -> bool; + + #[doc(hidden)] + #[rust_name = "ibpgi"] + fn IsBallProbablyGoingIn(self: &Arena, max_time: f32, extra_margin: f32) -> bool; + + /// Returns all of the car ids" + #[must_use] + #[cxx_name = "GetCars"] + fn get_cars(self: &Arena) -> Vec; + + /// Returns the car state of the car with the given id + #[must_use] + #[cxx_name = "GetCar"] + fn get_car(self: Pin<&mut Arena>, car_id: u32) -> CarState; + + /// Adds a car to the arena with the given team and car config + #[must_use] + #[cxx_name = "AddCar"] + fn add_car(self: Pin<&mut Arena>, team: Team, car_config: &CarConfig) -> u32; + + /// Returns the ball state + #[must_use] + #[cxx_name = "GetBall"] + fn get_ball(self: Pin<&mut Arena>) -> BallState; + /// Sets the ball state + + #[cxx_name = "SetBall"] + fn set_ball(self: Pin<&mut Arena>, ball: BallState); + + /// Returns the position of the pad with the given index + #[must_use] + #[cxx_name = "GetPadPos"] + fn get_pad_pos(self: &Arena, index: usize) -> Vec3; + /// Sets the state of the pad with the given index + + #[cxx_name = "SetPadState"] + fn set_pad_state(self: Pin<&mut Arena>, index: usize, pad: BoostPadState); + + /// Returns the state of the pad with the given index + #[must_use] + #[cxx_name = "GetPadState"] + fn get_pad_state(self: &Arena, index: usize) -> BoostPadState; + + /// Returns the car config of the car with the given id + #[must_use] + #[cxx_name = "GetCarConfig"] + fn get_car_config(self: &Arena, id: u32) -> CarConfig; + + /// Returns the team of the car with the given id + #[must_use] + #[cxx_name = "GetCarTeam"] + fn get_car_team(self: &Arena, id: u32) -> Team; + /// Sets the goal scored callback + + #[cxx_name = "SetGoalScoreCallback"] + fn set_goal_scored_callback( + self: Pin<&mut Arena>, + callback: fn(arena: Pin<&mut Arena>, car_team: Team, user_data: usize), + user_data: usize, + ); + /// Sets the car bump callback + + #[cxx_name = "SetCarBumpCallback"] + fn set_car_bump_callback( + self: Pin<&mut Arena>, + callback: fn(arena: Pin<&mut Arena>, bumper: u32, victim: u32, is_demo: bool, user_data: usize), + user_data: usize, + ); + + /// Returns the mutator config + #[must_use] + #[cxx_name = "GetMutatorConfig"] + fn get_mutator_config(self: &Arena) -> MutatorConfig; + + /// Sets the mutator config + #[cxx_name = "SetMutatorConfig"] + fn set_mutator_config(self: Pin<&mut Arena>, config: MutatorConfig); + + /// Deep clone the arena, optionally copying the callbacks + /// + /// If `copy_callbacks` is true, the callbacks will be copied, + /// otherwise the new arena will have no callbacks + #[must_use] + #[cxx_name = "Clone"] + fn clone(self: &Arena, copy_callbacks: bool) -> UniquePtr; + + /// Returns the number of cars in the arena + #[cxx_name = "NumCars"] + fn num_cars(self: &Arena) -> usize; + + /// Returns the radius of the ball + #[cxx_name = "GetBallRadius"] + fn get_ball_radius(self: &Arena) -> f32; + + /// Returns the number of pads in the arena + #[cxx_name = "NumPads"] + fn num_pads(self: &Arena) -> usize; + + /// Returns if the pad with the given index is big (gives 100 boost instead of 12) + #[cxx_name = "GetPadIsBig"] + fn get_pad_is_big(self: &Arena, index: usize) -> bool; + + /// Resets the tick count + #[cxx_name = "ResetTickCount"] + fn reset_tick_count(self: Pin<&mut Arena>); + + /// Returns the tick count + #[cxx_name = "GetTickCount"] + fn get_tick_count(self: &Arena) -> u64; + + /// Returns the tick rate (i.e. `0.008333` aka `1 / 120`) + #[cxx_name = "GetTickRate"] + fn get_tick_rate(self: &Arena) -> f32; + + /// Returns the game mode + #[cxx_name = "GetGameMode"] + fn get_game_mode(self: &Arena) -> GameMode; + + /// Steps the simulation by the given number of ticks + #[cxx_name = "Step"] + fn step(self: Pin<&mut Arena>, num_ticks: u32); + + /// Returns if the ball is within a goal + #[cxx_name = "IsBallScored"] + fn is_ball_scored(self: &Arena) -> bool; + } + + impl UniquePtr {} +} + +unsafe impl Send for Arena {} + +pub use base::Arena; diff --git a/src/sim/arena_config.rs b/src/sim/arena_config.rs new file mode 100644 index 0000000..c55018b --- /dev/null +++ b/src/sim/arena_config.rs @@ -0,0 +1,39 @@ +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub enum ArenaMemWeightMode { + #[default] + Heavy, + Light, +} + +unsafe impl cxx::ExternType for ArenaMemWeightMode { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::ArenaMemWeightMode"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/Arena/ArenaConfig/ArenaConfig.h"); + + type ArenaConfig; + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + type ArenaMemWeightMode = crate::sim::ArenaMemWeightMode; + } + + #[derive(Clone, Copy, Debug)] + struct ArenaConfig { + mem_weight_mode: ArenaMemWeightMode, + min_pos: Vec3, + max_pos: Vec3, + max_aabb_len: f32, + no_ball_rot: bool, + use_custom_broadphase: bool, + max_objects: u32, + } +} + +pub use base::ArenaConfig; diff --git a/src/sim/ball_hit_info.rs b/src/sim/ball_hit_info.rs new file mode 100644 index 0000000..aa1f830 --- /dev/null +++ b/src/sim/ball_hit_info.rs @@ -0,0 +1,22 @@ +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/BallHitInfo/BallHitInfo.h"); + + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + type BallHitInfo; + } + + #[derive(Clone, Copy, Debug, Default)] + struct BallHitInfo { + is_valid: bool, + relative_pos_on_ball: Vec3, + ball_pos: Vec3, + extra_hit_vel: Vec3, + tick_count_when_hit: u64, + tick_count_when_extra_impulse_applied: u64, + } +} + +pub use base::BallHitInfo; diff --git a/src/sim/ball_state.rs b/src/sim/ball_state.rs new file mode 100644 index 0000000..9b0fcf9 --- /dev/null +++ b/src/sim/ball_state.rs @@ -0,0 +1,41 @@ +#[derive(Clone, Copy, Debug)] +pub struct HeatseekerInfo { + /// Which net the ball should seek towards; + /// When 0, no net + pub y_target_dir: f32, + pub cur_target_speed: f32, + pub time_since_hit: f32, +} + +unsafe impl cxx::ExternType for HeatseekerInfo { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::BallState::HeatseekerInfo"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/Ball/Ball.h"); + + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + type RotMat = crate::math::RotMat; + type BallState; + #[namespace = "RocketSim::BallState"] + type HeatseekerInfo = crate::sim::HeatseekerInfo; + } + + #[derive(Clone, Copy, Debug)] + struct BallState { + pos: Vec3, + rot_mat: RotMat, + vel: Vec3, + ang_vel: Vec3, + update_counter: u64, + hs_info: HeatseekerInfo, + } +} + +pub use base::BallState; diff --git a/src/sim/boost_pad_state.rs b/src/sim/boost_pad_state.rs new file mode 100644 index 0000000..54fe51e --- /dev/null +++ b/src/sim/boost_pad_state.rs @@ -0,0 +1,18 @@ +#[cxx::bridge] +mod boostpadstate { + unsafe extern "C++" { + include!("arenar.h"); + + type EBoostPadState; + } + + #[derive(Clone, Copy, Debug, Default)] + struct EBoostPadState { + is_active: bool, + cooldown: f32, + cur_locked_car_id: u32, + prev_locked_car_id: u32, + } +} + +pub use boostpadstate::EBoostPadState as BoostPadState; diff --git a/src/sim/car_config.rs b/src/sim/car_config.rs new file mode 100644 index 0000000..56a1a71 --- /dev/null +++ b/src/sim/car_config.rs @@ -0,0 +1,30 @@ +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/Car/CarConfig/CarConfig.h"); + + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + + type WheelPairConfig; + type CarConfig; + } + + #[derive(Clone, Copy, Debug, Default)] + struct WheelPairConfig { + wheel_radius: f32, + suspension_rest_length: f32, + connection_point_offset: Vec3, + } + + #[derive(Clone, Copy, Debug, Default)] + struct CarConfig { + hitbox_size: Vec3, + hitbox_pos_offset: Vec3, + front_wheels: WheelPairConfig, + back_wheels: WheelPairConfig, + dodge_deadzone: f32, + } +} + +pub use base::{CarConfig, WheelPairConfig}; diff --git a/src/sim/car_controls.rs b/src/sim/car_controls.rs new file mode 100644 index 0000000..9475645 --- /dev/null +++ b/src/sim/car_controls.rs @@ -0,0 +1,22 @@ +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/CarControls.h"); + + type CarControls; + } + + #[derive(Clone, Copy, Debug, Default)] + struct CarControls { + pub throttle: f32, + pub steer: f32, + pub pitch: f32, + pub yaw: f32, + pub roll: f32, + pub boost: bool, + pub jump: bool, + pub handbrake: bool, + } +} + +pub use base::CarControls; diff --git a/src/sim/car_state.rs b/src/sim/car_state.rs new file mode 100644 index 0000000..8609107 --- /dev/null +++ b/src/sim/car_state.rs @@ -0,0 +1,93 @@ +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub enum Team { + #[default] + Blue, + Orange, +} + +unsafe impl cxx::ExternType for Team { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::Team"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/Car/Car.h"); + + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + type RotMat = crate::math::RotMat; + type CarControls = crate::sim::CarControls; + type BallHitInfo = crate::sim::BallHitInfo; + + type CarState; + + #[cxx_name = "HasFlipOrJump"] + /// Returns if the car has flipped or jumped + fn has_flip_or_jump(self: &CarState) -> bool; + + #[cxx_name = "HasFlipReset"] + /// Returns if the car has a flip reset + fn has_flip_reset(self: &CarState) -> bool; + + #[cxx_name = "GotFlipReset"] + /// Returns if the car got a flip reset + fn got_flip_reset(self: &CarState) -> bool; + } + + #[derive(Clone, Copy, Debug)] + struct WorldContact { + has_contact: bool, + contact_normal: Vec3, + } + + #[derive(Clone, Copy, Debug)] + struct CarContact { + other_car_id: u32, + cooldown_timer: f32, + } + + #[derive(Clone, Copy, Debug)] + struct CarState { + pos: Vec3, + rot_mat: RotMat, + vel: Vec3, + ang_vel: Vec3, + update_counter: u64, + is_on_ground: bool, + wheels_with_contact: [bool; 4], + has_jumped: bool, + has_double_jumped: bool, + has_flipped: bool, + flip_rel_torque: Vec3, + jump_time: f32, + flip_time: f32, + is_flipping: bool, + is_jumping: bool, + air_time: f32, + air_time_since_jump: f32, + boost: f32, + time_spent_boosting: f32, + is_supersonic: bool, + supersonic_time: f32, + handbrake_val: f32, + is_auto_flipping: bool, + auto_flip_timer: f32, + auto_flip_torque_scale: f32, + world_contact: WorldContact, + car_contact: CarContact, + is_demoed: bool, + demo_respawn_timer: f32, + ball_hit_info: BallHitInfo, + last_controls: CarControls, + } + + impl UniquePtr {} + impl CxxVector {} +} + +pub use base::{CarContact, CarState, WorldContact}; diff --git a/src/sim/mod.rs b/src/sim/mod.rs new file mode 100644 index 0000000..8872297 --- /dev/null +++ b/src/sim/mod.rs @@ -0,0 +1,19 @@ +mod arena; +mod arena_config; +mod ball_hit_info; +mod ball_state; +mod boost_pad_state; +mod car_config; +mod car_controls; +mod car_state; +mod mutator_config; + +pub use arena::{Arena, GameMode}; +pub use arena_config::{ArenaConfig, ArenaMemWeightMode}; +pub use ball_hit_info::BallHitInfo; +pub use ball_state::{BallState, HeatseekerInfo}; +pub use boost_pad_state::BoostPadState; +pub use car_config::{CarConfig, WheelPairConfig}; +pub use car_controls::CarControls; +pub use car_state::{CarContact, CarState, Team, WorldContact}; +pub use mutator_config::{DemoMode, MutatorConfig}; diff --git a/src/sim/mutator_config.rs b/src/sim/mutator_config.rs new file mode 100644 index 0000000..7ee7515 --- /dev/null +++ b/src/sim/mutator_config.rs @@ -0,0 +1,59 @@ +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)] +pub enum DemoMode { + #[default] + Normal = 0, + OnContact = 1, + Disabled = 2, +} + +unsafe impl cxx::ExternType for DemoMode { + #[allow(unused_attributes)] + #[doc(hidden)] + type Id = cxx::type_id!("RocketSim::DemoMode"); + type Kind = cxx::kind::Trivial; +} + +#[cxx::bridge(namespace = "RocketSim")] +mod base { + unsafe extern "C++" { + include!("Sim/MutatorConfig/MutatorConfig.h"); + + #[rust_name = "Vec3"] + type Vec = crate::math::Vec3; + type DemoMode = crate::sim::DemoMode; + + type MutatorConfig; + } + + #[derive(Clone, Copy, Debug)] + struct MutatorConfig { + gravity: Vec3, + car_mass: f32, + car_world_friction: f32, + car_world_restitution: f32, + ball_mass: f32, + ball_max_speed: f32, + ball_drag: f32, + ball_world_friction: f32, + ball_world_restitution: f32, + jump_accel: f32, + jump_immediate_force: f32, + boost_accel: f32, + boost_used_per_second: f32, + respawn_delay: f32, + bump_cooldown_time: f32, + boost_pad_cooldown_big: f32, + boost_pad_cooldown_small: f32, + car_spawn_boost_amount: f32, + ball_hit_extra_force_scale: f32, + bump_force_scale: f32, + ball_radius: f32, + unlimited_flips: bool, + unlimited_double_jumps: bool, + demo_mode: DemoMode, + enable_team_demos: bool, + } +} + +pub use base::MutatorConfig;