use crate::abi::{BuildRights, Header};
use super::{Player, World, WorldError};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PlayerPermissions {
CanEditBase,
CanEditWorld,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum BuildRefusal {
#[error("the player lacks {0:?}")]
NoPermission(PlayerPermissions),
#[error("outside the world's building area")]
CursorOutsideBuildingZone,
#[error("inside a strict no-build zone")]
CursorInsideStrictBuildInhibitorZone,
#[error("the server could not check: {0}")]
Unchecked(#[from] WorldError),
}
impl World {
pub fn can_build(self, player: &Player, a: [f64; 3], b: [f64; 3]) -> Result<(), BuildRefusal> {
if !a.iter().chain(&b).all(|v| v.is_finite()) {
return Err(BuildRefusal::Unchecked(WorldError::Refused));
}
let mut check = BuildRights {
header: Header::of::<BuildRights>(),
player: player.entity,
refusal: BuildRights::ALLOWED,
min: [0, 1, 2].map(|i| a[i].min(b[i])),
max: [0, 1, 2].map(|i| a[i].max(b[i])),
};
let status = (self.0.host.api().build_rights)(self.0.host.handle(), &raw mut check);
WorldError::from_status(status)?;
BuildRefusal::from_code(check.refusal).map_or(Ok(()), Err)
}
pub fn persists(self, a: [f64; 3], b: [f64; 3]) -> Result<bool, WorldError> {
if !a.iter().chain(&b).all(|v| v.is_finite()) {
return Err(WorldError::Refused);
}
let min = [0, 1, 2].map(|i| a[i].min(b[i]));
let max = [0, 1, 2].map(|i| a[i].max(b[i]));
let mut kept = 0u8;
let status = (self.0.host.api().world_persists)(
self.0.host.handle(),
&raw const min,
&raw const max,
&raw mut kept,
);
WorldError::from_status(status)?;
Ok(kept != 0)
}
}
impl BuildRefusal {
pub(crate) const fn from_code(code: u32) -> Option<Self> {
Some(match code {
BuildRights::ALLOWED => return None,
BuildRights::NO_CAN_EDIT_WORLD => Self::NoPermission(PlayerPermissions::CanEditWorld),
BuildRights::NO_CAN_EDIT_BASE => Self::NoPermission(PlayerPermissions::CanEditBase),
BuildRights::OUTSIDE_BUILDING_ZONE => Self::CursorOutsideBuildingZone,
BuildRights::INSIDE_STRICT_BUILD_INHIBITOR_ZONE => {
Self::CursorInsideStrictBuildInhibitorZone
}
_ => Self::Unchecked(WorldError::Refused),
})
}
}