1use super::component_api::ComponentInterface;
26use super::players::{ENTITY_OFFSET, SLOT_ENTITY_GET_POSITION};
27use super::server::ServerComponent;
28use super::types::{UID, Vector3};
29use super::vtable::{call_vtable, opaque, slots, virtual_fns};
30
31pub const VEHICLES_COMPONENT_UID: UID = 0x3f1f_62ee_9e22_ab19;
33
34slots! {
35 SLOT_CREATE_VEHICLE: usize = 19, 18;
36 SLOT_VEHICLE_SET_COLOUR: usize = 11, 10;
37 SLOT_VEHICLE_SET_HEALTH: usize = 13, 12;
38 SLOT_VEHICLE_GET_HEALTH: usize = 14, 13;
39 SLOT_VEHICLE_GET_MODEL: usize = 57, 56;
40 SLOT_VEHICLE_DISPATCHER: usize = 21, 19;
42 VEHICLE_POOL_OFFSET: isize = 44, 64;
48}
49
50opaque! {
51 pub IVehiclesComponent;
53 pub IVehicle;
55 pub IVehicleDispatcher;
57}
58
59#[cfg(not(target_env = "msvc"))]
66#[repr(C)]
67pub struct VehicleHandlerVTable {
68 pub on_vehicle_stream_in: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
69 pub on_vehicle_stream_out: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
70 pub on_vehicle_death: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
71 pub on_player_enter_vehicle:
72 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
73 pub on_player_exit_vehicle: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
74 pub on_vehicle_damage_status_update:
75 unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
76 pub on_vehicle_paint_job:
77 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
78 pub on_vehicle_mod:
79 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
80 pub on_vehicle_respray:
81 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
82 pub on_enter_exit_mod_shop: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, bool, i32),
83 pub on_vehicle_spawn: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle),
84 pub on_unoccupied_vehicle_update:
85 unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
86 pub on_trailer_update:
87 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
88 pub on_vehicle_siren_state_change:
89 unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
90}
91
92#[cfg(target_env = "msvc")]
94#[repr(C)]
95pub struct VehicleHandlerVTable {
96 pub on_vehicle_stream_in:
97 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
98 pub on_vehicle_stream_out:
99 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
100 pub on_vehicle_death: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
101 pub on_player_enter_vehicle:
102 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
103 pub on_player_exit_vehicle:
104 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
105 pub on_vehicle_damage_status_update:
106 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
107 pub on_vehicle_paint_job:
108 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
109 pub on_vehicle_mod:
110 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
111 pub on_vehicle_respray:
112 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
113 pub on_enter_exit_mod_shop:
114 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, bool, i32),
115 pub on_vehicle_spawn: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle),
116 pub on_unoccupied_vehicle_update:
117 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
118 pub on_trailer_update:
119 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
120 pub on_vehicle_siren_state_change:
121 unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
122}
123
124#[repr(C)]
128pub struct VehicleHandler {
129 vtable: *const VehicleHandlerVTable,
130}
131
132unsafe impl Send for VehicleHandler {}
134unsafe impl Sync for VehicleHandler {}
135
136impl VehicleHandler {
137 #[must_use]
139 pub fn new(vtable: *const VehicleHandlerVTable) -> Self {
140 Self { vtable }
141 }
142}
143
144virtual_fns! {
145 #[must_use]
150 pub fn vehicle_event_dispatcher(component: IVehiclesComponent) -> *mut IVehicleDispatcher = [0, SLOT_VEHICLE_DISPATCHER] or std::ptr::null_mut();
151}
152
153pub unsafe fn add_vehicle_handler(
158 dispatcher: *mut IVehicleDispatcher,
159 handler: *mut VehicleHandler,
160) -> bool {
161 call_vtable!(
162 dispatcher.cast::<u8>(),
163 0,
164 0,
165 (*mut VehicleHandler, i8) -> bool,
166 (handler, 0),
167 false
168 )
169}
170
171virtual_fns! {
172 #[must_use]
177 pub fn vehicle_by_id(component: IVehiclesComponent, id: i32) -> *mut IVehicle = [VEHICLE_POOL_OFFSET, super::players::SLOT_POOL_GET] or std::ptr::null_mut();
178
179 #[must_use]
184 pub fn vehicle_id(vehicle: IVehicle) -> i32 = [ENTITY_OFFSET, super::players::SLOT_ENTITY_GET_ID] or -1;
185}
186
187#[must_use]
192#[deprecated(
193 since = "3.6.0",
194 note = "the UID and the cast can disagree; use `omp_query::<Component<IVehiclesComponent>>()` and `Component::as_ptr`"
195)]
196pub unsafe fn as_vehicles_component(component: *mut ServerComponent) -> *mut IVehiclesComponent {
197 component.cast::<IVehiclesComponent>()
198}
199
200#[must_use]
212#[allow(clippy::too_many_arguments)]
217pub unsafe fn create_vehicle(
218 component: *mut IVehiclesComponent,
219 model: i32,
220 position: Vector3,
221 z_angle: f32,
222 colour1: i32,
223 colour2: i32,
224 respawn_delay_secs: i64,
225 siren: bool,
226) -> *mut IVehicle {
227 call_vtable!(
228 component.cast::<u8>(),
229 0,
230 SLOT_CREATE_VEHICLE,
231 (bool, i32, Vector3, f32, i32, i32, i64, bool) -> *mut IVehicle,
232 (false, model, position, z_angle, colour1, colour2, respawn_delay_secs, siren),
233 std::ptr::null_mut()
234 )
235}
236
237virtual_fns! {
238 #[must_use]
243 pub fn vehicle_model(vehicle: IVehicle) -> i32 = [0, SLOT_VEHICLE_GET_MODEL] or 0;
244
245 #[must_use]
250 pub fn vehicle_health(vehicle: IVehicle) -> f32 = [0, SLOT_VEHICLE_GET_HEALTH] or 0.0;
251
252 pub fn vehicle_set_health(vehicle: IVehicle, health: f32) = [0, SLOT_VEHICLE_SET_HEALTH];
257
258 pub fn vehicle_set_colour(vehicle: IVehicle, colour1: i32, colour2: i32) = [0, SLOT_VEHICLE_SET_COLOUR];
263
264 #[must_use]
272 pub fn vehicle_position(vehicle: IVehicle) -> Vector3 = [ENTITY_OFFSET, SLOT_ENTITY_GET_POSITION] or Vector3::ZERO;
273}
274
275impl ComponentInterface for IVehiclesComponent {
278 const UID: UID = VEHICLES_COMPONENT_UID;
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn uid_matches_the_header() {
287 assert_eq!(VEHICLES_COMPONENT_UID, 0x3f1f_62ee_9e22_ab19);
289 }
290
291 #[test]
292 fn slots_match_what_clang_reports() {
293 #[cfg(not(target_env = "msvc"))]
297 let expected = [19, 11, 13, 14, 57];
298 #[cfg(target_env = "msvc")]
299 let expected = [18, 10, 12, 13, 56];
300 assert_eq!(
301 [
302 SLOT_CREATE_VEHICLE,
303 SLOT_VEHICLE_SET_COLOUR,
304 SLOT_VEHICLE_SET_HEALTH,
305 SLOT_VEHICLE_GET_HEALTH,
306 SLOT_VEHICLE_GET_MODEL,
307 ],
308 expected
309 );
310 }
311
312 #[test]
313 fn a_null_component_creates_nothing() {
314 let position = Vector3::ZERO;
315 let vehicle =
316 unsafe { create_vehicle(std::ptr::null_mut(), 411, position, 0.0, -1, -1, -1, false) };
317 assert!(vehicle.is_null());
318 }
319}