use super::*;
use crate::interfaces::ValveEngine;
use crate::server::Module;
use crate::test_support::edicts::{set_change_accessor, set_shared_change_info};
use crate::test_support::entities::{
MockEntity, base_entity_fields, set_datamap, set_local_bounds, set_networking, set_solid_type,
};
use crate::test_support::sdk_core::change_tracking_engine;
use crate::test_support::server::{export, mock_server};
use sdk_raw::edicts::FL_EDICT_CHANGED;
use sdk_raw::test_support::edicts::mock_edict;
use sdk_raw::test_support::entities::{data_map, field};
use std::ffi::c_int;
use std::mem::zeroed;
use std::ptr::null_mut;
const MOCK_COLLISION_OFFSET: usize = 96;
const MOCK_SOLID_FLAGS_FIELD: usize = 6;
const MOCK_SOLID_FLAGS_OFFSET: usize = MOCK_COLLISION_OFFSET + MOCK_SOLID_FLAGS_FIELD;
#[test]
fn solid_flags_need_the_embedded_collision_property() {
let mut mock = MockEntity::new(5);
let mut solid_flags = field(
c"m_usSolidFlags",
sys::_fieldtypes_FIELD_SHORT,
MOCK_SOLID_FLAGS_OFFSET,
);
solid_flags.fieldSizeInBytes = size_of::<u16>() as c_int;
let mut fields = Vec::from(base_entity_fields());
fields.push(solid_flags);
let maps = data_map(c"CBaseEntity", fields, null_mut());
assert_eq!(
find_solid_flags_field(
unsafe { sdk_raw::entities::datamap::DataMaps::new(maps) }
),
None
);
set_datamap(solid_maps());
assert_eq!(
find_solid_flags_field(unsafe { sdk_raw::entities::datamap::DataMaps::new(solid_maps()) }),
Some(MOCK_SOLID_FLAGS_OFFSET)
);
assert_eq!(mock.entity().solid_flags(), Ok(SolidFlags::empty()));
}
fn solid_maps() -> *mut sys::datamap_t {
let mut solid_flags = field(
c"m_usSolidFlags",
sys::_fieldtypes_FIELD_SHORT,
MOCK_SOLID_FLAGS_FIELD,
);
solid_flags.fieldSizeInBytes = size_of::<u16>() as c_int;
let mut collision = field(
c"m_Collision",
sys::_fieldtypes_FIELD_EMBEDDED,
MOCK_COLLISION_OFFSET,
);
collision.fieldSize = 1;
collision.td = data_map(c"CCollisionProperty", vec![solid_flags], null_mut());
let mut fields = Vec::from(base_entity_fields());
fields.push(collision);
data_map(
c"CTFPlayer",
vec![],
data_map(c"CBaseEntity", fields, null_mut()),
)
}
fn store_flags(mock: &mut MockEntity, flags: u16) {
unsafe {
mock.as_ptr()
.byte_add(MOCK_SOLID_FLAGS_OFFSET)
.cast::<u16>()
.write(flags)
};
}
fn stored_flags(mock: &mut MockEntity) -> u16 {
unsafe {
mock.as_ptr()
.byte_add(MOCK_SOLID_FLAGS_OFFSET)
.cast::<u16>()
.read()
}
}
fn assert_set_alone_and_networked(
flag: u16,
set: fn(Entity<'_>, Server<'_>, bool) -> Result<bool, SolidFlagsError>,
) {
let mut mock = MockEntity::new(5);
set_datamap(solid_maps());
let trigger = FSOLID_NOT_SOLID | FSOLID_TRIGGER | 0x8000;
store_flags(&mut mock, trigger);
assert_eq!(
mock.entity().solid_flags(),
Ok(SolidFlags::NOT_SOLID | SolidFlags::TRIGGER | SolidFlags::from_bits_retain(0x8000))
);
let mut shared = Box::new(unsafe { zeroed::<sys::CSharedEdictChangeInfo>() });
let mut accessor = sys::IChangeInfoAccessor {
m_iChangeInfo: 0,
m_iChangeInfoSerialNumber: 0,
};
let mut edict = mock_edict(5, false);
shared.m_iSerialNumber = 7;
set_change_accessor(&raw mut accessor);
set_shared_change_info(&raw mut *shared);
set_networking(null_mut(), &raw mut edict);
export(
Module::Engine,
ValveEngine::VERSION,
change_tracking_engine().as_ptr(),
);
let scope = ();
let server = mock_server(&scope);
assert_eq!(set(mock.entity(), server, true), Ok(false));
assert_eq!(stored_flags(&mut mock), trigger | flag);
assert_ne!(edict._base.m_fStateFlags & FL_EDICT_CHANGED, 0);
assert_eq!(shared.m_ChangeInfos[0].m_nChangeOffsets, 1);
assert_eq!(
shared.m_ChangeInfos[0].m_ChangeOffsets[0],
u16::try_from(MOCK_SOLID_FLAGS_OFFSET).unwrap()
);
assert_eq!(set(mock.entity(), server, true), Ok(true));
assert_eq!(shared.m_ChangeInfos[0].m_nChangeOffsets, 1);
assert_eq!(set(mock.entity(), server, false), Ok(true));
assert_eq!(stored_flags(&mut mock), trigger);
set_change_accessor(null_mut());
set_shared_change_info(null_mut());
set_networking(null_mut(), null_mut());
}
#[test]
fn the_custom_ray_test_is_set_alone_and_networked() {
assert_set_alone_and_networked(FSOLID_CUSTOMRAYTEST, |entity, server, enabled| {
entity.set_custom_ray_test(server, enabled)
});
}
#[test]
fn debris_touches_are_set_alone_and_networked() {
assert_set_alone_and_networked(FSOLID_TRIGGER_TOUCH_DEBRIS, |entity, server, enabled| {
entity.set_trigger_touch_debris(server, enabled)
});
}
#[test]
fn solid_types_are_read_through_the_collideable() {
let mut mock = MockEntity::new(5);
assert_eq!(mock.entity().solid_type(), Some(SolidType::BoundingBox));
set_solid_type(sys::SolidType_t_SOLID_VPHYSICS);
assert_eq!(mock.entity().solid_type(), Some(SolidType::Vphysics));
set_solid_type(sys::SolidType_t_SOLID_LAST);
assert_eq!(mock.entity().solid_type(), None);
}
#[test]
fn solid_types_convert_to_and_from_their_values() {
let solids = (0..=u8::MAX)
.filter_map(SolidType::from_raw)
.collect::<Vec<_>>();
assert_eq!(
solids,
[
SolidType::None,
SolidType::Bsp,
SolidType::BoundingBox,
SolidType::OrientedBox,
SolidType::OrientedBoxYaw,
SolidType::Custom,
SolidType::Vphysics,
]
);
for (value, solid) in solids.into_iter().enumerate() {
assert_eq!(usize::from(solid.to_raw()), value);
}
}
#[test]
fn local_bounds_are_read_through_the_collideable() {
let mut mock = MockEntity::new(5);
assert_eq!(
mock.entity().local_bounds(),
Some((Vector::new(-1.0, -1.0, -1.0), Vector::new(1.0, 1.0, 1.0)))
);
set_local_bounds(
sys::Vector {
x: -24.0,
y: -24.0,
z: 0.0,
},
sys::Vector {
x: 24.0,
y: 24.0,
z: 82.0,
},
);
assert_eq!(
mock.entity().local_bounds(),
Some((
Vector::new(-24.0, -24.0, 0.0),
Vector::new(24.0, 24.0, 82.0)
))
);
}