use super::players::{ENTITY_OFFSET, SLOT_ENTITY_GET_POSITION};
use super::server::ServerComponent;
use super::types::{UID, Vector3};
use super::vtable::call_vtable;
pub const VEHICLES_COMPONENT_UID: UID = 0x3f1f_62ee_9e22_ab19;
#[cfg(not(target_env = "msvc"))]
const SLOT_CREATE_VEHICLE: usize = 19;
#[cfg(target_env = "msvc")]
const SLOT_CREATE_VEHICLE: usize = 18;
#[cfg(not(target_env = "msvc"))]
const SLOT_VEHICLE_SET_COLOUR: usize = 11;
#[cfg(target_env = "msvc")]
const SLOT_VEHICLE_SET_COLOUR: usize = 10;
#[cfg(not(target_env = "msvc"))]
const SLOT_VEHICLE_SET_HEALTH: usize = 13;
#[cfg(target_env = "msvc")]
const SLOT_VEHICLE_SET_HEALTH: usize = 12;
#[cfg(not(target_env = "msvc"))]
const SLOT_VEHICLE_GET_HEALTH: usize = 14;
#[cfg(target_env = "msvc")]
const SLOT_VEHICLE_GET_HEALTH: usize = 13;
#[cfg(not(target_env = "msvc"))]
const SLOT_VEHICLE_GET_MODEL: usize = 57;
#[cfg(target_env = "msvc")]
const SLOT_VEHICLE_GET_MODEL: usize = 56;
#[cfg(not(target_env = "msvc"))]
const SLOT_VEHICLE_DISPATCHER: usize = 21;
#[cfg(target_env = "msvc")]
const SLOT_VEHICLE_DISPATCHER: usize = 19;
#[cfg(not(target_env = "msvc"))]
const VEHICLE_POOL_OFFSET: isize = 44;
#[cfg(target_env = "msvc")]
const VEHICLE_POOL_OFFSET: isize = 64;
#[repr(C)]
pub struct IVehiclesComponent {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct IVehicle {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct IVehicleDispatcher {
_opaque: [u8; 0],
}
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
pub struct VehicleHandlerVTable {
pub on_vehicle_stream_in: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_stream_out: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_death: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_player_enter_vehicle:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
pub on_player_exit_vehicle: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
pub on_vehicle_damage_status_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_paint_job:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_mod:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_respray:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
pub on_enter_exit_mod_shop: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, bool, i32),
pub on_vehicle_spawn: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle),
pub on_unoccupied_vehicle_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
pub on_trailer_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
pub on_vehicle_siren_state_change:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
}
#[cfg(target_env = "msvc")]
#[repr(C)]
pub struct VehicleHandlerVTable {
pub on_vehicle_stream_in:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_stream_out:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_death: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_player_enter_vehicle:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
pub on_player_exit_vehicle:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
pub on_vehicle_damage_status_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_paint_job:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_mod:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_respray:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
pub on_enter_exit_mod_shop:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, bool, i32),
pub on_vehicle_spawn: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle),
pub on_unoccupied_vehicle_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
pub on_trailer_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
pub on_vehicle_siren_state_change:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
}
#[repr(C)]
pub struct VehicleHandler {
vtable: *const VehicleHandlerVTable,
}
unsafe impl Send for VehicleHandler {}
unsafe impl Sync for VehicleHandler {}
impl VehicleHandler {
#[must_use]
pub fn new(vtable: *const VehicleHandlerVTable) -> Self {
Self { vtable }
}
}
#[must_use]
pub unsafe fn vehicle_event_dispatcher(
component: *mut IVehiclesComponent,
) -> *mut IVehicleDispatcher {
call_vtable!(
component.cast::<u8>(),
0,
SLOT_VEHICLE_DISPATCHER,
() -> *mut IVehicleDispatcher,
(),
std::ptr::null_mut()
)
}
pub unsafe fn add_vehicle_handler(
dispatcher: *mut IVehicleDispatcher,
handler: *mut VehicleHandler,
) -> bool {
call_vtable!(
dispatcher.cast::<u8>(),
0,
0,
(*mut VehicleHandler, i8) -> bool,
(handler, 0),
false
)
}
#[must_use]
pub unsafe fn vehicle_by_id(component: *mut IVehiclesComponent, id: i32) -> *mut IVehicle {
call_vtable!(
component.cast::<u8>(),
VEHICLE_POOL_OFFSET,
super::players::SLOT_POOL_GET_PUB,
(i32) -> *mut IVehicle,
(id),
std::ptr::null_mut()
)
}
#[must_use]
pub unsafe fn vehicle_id(vehicle: *mut IVehicle) -> i32 {
call_vtable!(
vehicle.cast::<u8>(),
ENTITY_OFFSET,
super::players::SLOT_ENTITY_GET_ID,
() -> i32,
(),
-1
)
}
#[must_use]
pub unsafe fn as_vehicles_component(component: *mut ServerComponent) -> *mut IVehiclesComponent {
component.cast::<IVehiclesComponent>()
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub unsafe fn create_vehicle(
component: *mut IVehiclesComponent,
model: i32,
position: Vector3,
z_angle: f32,
colour1: i32,
colour2: i32,
respawn_delay_secs: i64,
siren: bool,
) -> *mut IVehicle {
call_vtable!(
component.cast::<u8>(),
0,
SLOT_CREATE_VEHICLE,
(bool, i32, Vector3, f32, i32, i32, i64, bool) -> *mut IVehicle,
(false, model, position, z_angle, colour1, colour2, respawn_delay_secs, siren),
std::ptr::null_mut()
)
}
unsafe fn vehicle_f32(vehicle: *mut IVehicle, slot: usize) -> f32 {
call_vtable!(vehicle.cast::<u8>(), 0, slot, () -> f32, (), 0.0)
}
#[must_use]
pub unsafe fn vehicle_model(vehicle: *mut IVehicle) -> i32 {
call_vtable!(vehicle.cast::<u8>(), 0, SLOT_VEHICLE_GET_MODEL, () -> i32, (), 0)
}
#[must_use]
pub unsafe fn vehicle_health(vehicle: *mut IVehicle) -> f32 {
unsafe { vehicle_f32(vehicle, SLOT_VEHICLE_GET_HEALTH) }
}
pub unsafe fn vehicle_set_health(vehicle: *mut IVehicle, health: f32) {
call_vtable!(
vehicle.cast::<u8>(),
0,
SLOT_VEHICLE_SET_HEALTH,
(f32) -> (),
(health),
()
)
}
pub unsafe fn vehicle_set_colour(vehicle: *mut IVehicle, colour1: i32, colour2: i32) {
call_vtable!(
vehicle.cast::<u8>(),
0,
SLOT_VEHICLE_SET_COLOUR,
(i32, i32) -> (),
(colour1, colour2),
()
)
}
#[must_use]
pub unsafe fn vehicle_position(vehicle: *mut IVehicle) -> Vector3 {
let zero = Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
};
call_vtable!(
vehicle.cast::<u8>(),
ENTITY_OFFSET,
SLOT_ENTITY_GET_POSITION,
() -> Vector3,
(),
zero
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uid_matches_the_header() {
assert_eq!(VEHICLES_COMPONENT_UID, 0x3f1f_62ee_9e22_ab19);
}
#[test]
fn slots_match_what_clang_reports() {
#[cfg(not(target_env = "msvc"))]
let expected = [19, 11, 13, 14, 57];
#[cfg(target_env = "msvc")]
let expected = [18, 10, 12, 13, 56];
assert_eq!(
[
SLOT_CREATE_VEHICLE,
SLOT_VEHICLE_SET_COLOUR,
SLOT_VEHICLE_SET_HEALTH,
SLOT_VEHICLE_GET_HEALTH,
SLOT_VEHICLE_GET_MODEL,
],
expected
);
}
#[test]
fn a_null_component_creates_nothing() {
let position = Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
};
let vehicle =
unsafe { create_vehicle(std::ptr::null_mut(), 411, position, 0.0, -1, -1, -1, false) };
assert!(vehicle.is_null());
}
}