use boxdd_sys::ffi;
use std::any::Any;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Weak};
pub(crate) type CustomFilterCb = dyn Fn(&crate::world::CallbackWorld, crate::types::ShapeId, crate::types::ShapeId) -> bool
+ Send
+ Sync
+ 'static;
pub(crate) type PreSolveCb = dyn Fn(
&crate::world::CallbackWorld,
crate::types::ShapeId,
crate::types::ShapeId,
crate::types::Vec2,
crate::types::Vec2,
) -> bool
+ Send
+ Sync
+ 'static;
pub(crate) struct CustomFilterCtx {
pub(crate) core: Weak<WorldCore>,
pub(crate) cb: Box<CustomFilterCb>,
}
pub(crate) struct PreSolveCtx {
pub(crate) core: Weak<WorldCore>,
pub(crate) cb: Box<PreSolveCb>,
}
pub(crate) struct WorldCore {
pub(crate) id: ffi::b2WorldId,
pub(crate) custom_filter: Mutex<Option<Box<CustomFilterCtx>>>,
pub(crate) pre_solve: Mutex<Option<Box<PreSolveCtx>>>,
pub(crate) callback_panicked: AtomicBool,
pub(crate) callback_panic: Mutex<Option<Box<dyn Any + Send + 'static>>>,
pub(crate) deferred_destroys: Mutex<Vec<DeferredDestroy>>,
pub(crate) user_data: Mutex<crate::core::user_data::UserDataStore>,
pub(crate) borrowed_event_buffers: AtomicUsize,
#[cfg(feature = "serialize")]
pub(crate) registries: Mutex<crate::core::serialize_registry::Registries>,
pub(crate) owned_bodies: AtomicUsize,
pub(crate) owned_shapes: AtomicUsize,
pub(crate) owned_joints: AtomicUsize,
pub(crate) owned_chains: AtomicUsize,
}
unsafe impl Send for WorldCore {}
unsafe impl Sync for WorldCore {}
#[derive(Clone, Debug)]
pub(crate) enum DeferredDestroy {
Body(ffi::b2BodyId),
Shape {
id: ffi::b2ShapeId,
update_body_mass: bool,
},
Joint {
id: ffi::b2JointId,
wake_bodies: bool,
},
Chain(ffi::b2ChainId),
}
impl WorldCore {
pub(crate) fn new(id: ffi::b2WorldId) -> Arc<Self> {
Arc::new(Self {
id,
custom_filter: Mutex::new(None),
pre_solve: Mutex::new(None),
callback_panicked: AtomicBool::new(false),
callback_panic: Mutex::new(None),
deferred_destroys: Mutex::new(Vec::new()),
user_data: Mutex::new(crate::core::user_data::UserDataStore::default()),
borrowed_event_buffers: AtomicUsize::new(0),
#[cfg(feature = "serialize")]
registries: Mutex::new(crate::core::serialize_registry::Registries::default()),
owned_bodies: AtomicUsize::new(0),
owned_shapes: AtomicUsize::new(0),
owned_joints: AtomicUsize::new(0),
owned_chains: AtomicUsize::new(0),
})
}
pub(crate) fn owned_counts(&self) -> (usize, usize, usize, usize) {
(
self.owned_bodies.load(Ordering::Relaxed),
self.owned_shapes.load(Ordering::Relaxed),
self.owned_joints.load(Ordering::Relaxed),
self.owned_chains.load(Ordering::Relaxed),
)
}
pub(crate) fn defer_destroy(&self, d: DeferredDestroy) {
self.deferred_destroys
.lock()
.expect("deferred_destroys mutex poisoned")
.push(d);
}
pub(crate) fn events_buffers_are_borrowed(&self) -> bool {
self.borrowed_event_buffers.load(Ordering::Relaxed) > 0
}
pub(crate) fn borrow_event_buffers(self: &Arc<Self>) -> BorrowedEventBuffersGuard {
self.borrowed_event_buffers.fetch_add(1, Ordering::Relaxed);
BorrowedEventBuffersGuard {
core: Arc::clone(self),
}
}
pub(crate) fn process_deferred_destroys(&self) {
crate::core::callback_state::assert_not_in_callback();
let mut pending = self
.deferred_destroys
.lock()
.expect("deferred_destroys mutex poisoned");
if pending.is_empty() {
return;
}
let items = core::mem::take(&mut *pending);
drop(pending);
for item in items {
match item {
DeferredDestroy::Body(id) => {
if unsafe { ffi::b2Body_IsValid(id) } {
#[cfg(feature = "serialize")]
{
let mut r = self.registries.lock().expect("registries mutex poisoned");
r.remove_shape_flags_for_body(id);
r.remove_chains_for_body(id);
r.remove_body(id);
}
unsafe { ffi::b2DestroyBody(id) };
}
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.bodies
.remove(&crate::core::user_data::IdKey::from(id));
drop(old);
}
DeferredDestroy::Shape {
id,
update_body_mass,
} => {
if unsafe { ffi::b2Shape_IsValid(id) } {
unsafe { ffi::b2DestroyShape(id, update_body_mass) };
#[cfg(feature = "serialize")]
{
self.registries
.lock()
.expect("registries mutex poisoned")
.remove_shape_flags(id);
}
}
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.shapes
.remove(&crate::core::user_data::IdKey::from(id));
drop(old);
}
DeferredDestroy::Joint { id, wake_bodies } => {
if unsafe { ffi::b2Joint_IsValid(id) } {
unsafe { ffi::b2DestroyJoint(id, wake_bodies) };
}
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.joints
.remove(&crate::core::user_data::IdKey::from(id));
drop(old);
}
DeferredDestroy::Chain(id) => {
if unsafe { ffi::b2Chain_IsValid(id) } {
unsafe { ffi::b2DestroyChain(id) };
#[cfg(feature = "serialize")]
{
self.registries
.lock()
.expect("registries mutex poisoned")
.remove_chain(id);
}
}
}
}
}
}
pub(crate) fn clear_world_user_data(&self) -> bool {
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.world
.take();
let had = old.is_some();
drop(old);
had
}
pub(crate) fn clear_body_user_data(&self, id: ffi::b2BodyId) -> bool {
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.bodies
.remove(&crate::core::user_data::IdKey::from(id));
let had = old.is_some();
drop(old);
had
}
pub(crate) fn clear_shape_user_data(&self, id: ffi::b2ShapeId) -> bool {
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.shapes
.remove(&crate::core::user_data::IdKey::from(id));
let had = old.is_some();
drop(old);
had
}
pub(crate) fn clear_joint_user_data(&self, id: ffi::b2JointId) -> bool {
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.joints
.remove(&crate::core::user_data::IdKey::from(id));
let had = old.is_some();
drop(old);
had
}
pub(crate) fn set_world_user_data<T: 'static>(&self, value: T) -> *mut core::ffi::c_void {
let new = crate::core::user_data::ErasedUserData::new(value);
let ptr = new.as_ptr();
let old = {
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
s.world.replace(new)
};
drop(old);
ptr
}
pub(crate) fn set_body_user_data<T: 'static>(
&self,
id: ffi::b2BodyId,
value: T,
) -> *mut core::ffi::c_void {
let key = crate::core::user_data::IdKey::from(id);
let new = crate::core::user_data::ErasedUserData::new(value);
let ptr = new.as_ptr();
let old = {
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
s.bodies.insert(key, new)
};
drop(old);
ptr
}
pub(crate) fn set_shape_user_data<T: 'static>(
&self,
id: ffi::b2ShapeId,
value: T,
) -> *mut core::ffi::c_void {
let key = crate::core::user_data::IdKey::from(id);
let new = crate::core::user_data::ErasedUserData::new(value);
let ptr = new.as_ptr();
let old = {
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
s.shapes.insert(key, new)
};
drop(old);
ptr
}
pub(crate) fn set_joint_user_data<T: 'static>(
&self,
id: ffi::b2JointId,
value: T,
) -> *mut core::ffi::c_void {
let key = crate::core::user_data::IdKey::from(id);
let new = crate::core::user_data::ErasedUserData::new(value);
let ptr = new.as_ptr();
let old = {
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
s.joints.insert(key, new)
};
drop(old);
ptr
}
pub(crate) fn try_with_world_user_data<T: 'static, R>(
&self,
f: impl FnOnce(&T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.world.as_ref() else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_ref(f).expect("type checked")))
}
pub(crate) fn try_with_body_user_data<T: 'static, R>(
&self,
id: ffi::b2BodyId,
f: impl FnOnce(&T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.bodies.get(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_ref(f).expect("type checked")))
}
pub(crate) fn try_with_shape_user_data<T: 'static, R>(
&self,
id: ffi::b2ShapeId,
f: impl FnOnce(&T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.shapes.get(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_ref(f).expect("type checked")))
}
pub(crate) fn try_with_joint_user_data<T: 'static, R>(
&self,
id: ffi::b2JointId,
f: impl FnOnce(&T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.joints.get(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_ref(f).expect("type checked")))
}
pub(crate) fn try_with_body_user_data_mut<T: 'static, R>(
&self,
id: ffi::b2BodyId,
f: impl FnOnce(&mut T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.bodies.get_mut(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_mut(f).expect("type checked")))
}
pub(crate) fn try_with_shape_user_data_mut<T: 'static, R>(
&self,
id: ffi::b2ShapeId,
f: impl FnOnce(&mut T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.shapes.get_mut(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_mut(f).expect("type checked")))
}
pub(crate) fn try_with_joint_user_data_mut<T: 'static, R>(
&self,
id: ffi::b2JointId,
f: impl FnOnce(&mut T) -> R,
) -> crate::error::ApiResult<Option<R>> {
let key = crate::core::user_data::IdKey::from(id);
let mut s = self.user_data.lock().expect("user_data mutex poisoned");
let Some(e) = s.joints.get_mut(&key) else {
return Ok(None);
};
if !e.matches::<T>() {
return Err(crate::error::ApiError::UserDataTypeMismatch);
}
Ok(Some(e.with_mut(f).expect("type checked")))
}
pub(crate) fn take_world_user_data<T: 'static>(&self) -> crate::error::ApiResult<Option<T>> {
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.world
.take();
let Some(old) = old else { return Ok(None) };
match old.try_into_value::<T>() {
Ok(v) => Ok(Some(v)),
Err(old) => {
self.user_data
.lock()
.expect("user_data mutex poisoned")
.world = Some(old);
Err(crate::error::ApiError::UserDataTypeMismatch)
}
}
}
pub(crate) fn take_body_user_data<T: 'static>(
&self,
id: ffi::b2BodyId,
) -> crate::error::ApiResult<Option<T>> {
let key = crate::core::user_data::IdKey::from(id);
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.bodies
.remove(&key);
let Some(old) = old else { return Ok(None) };
match old.try_into_value::<T>() {
Ok(v) => Ok(Some(v)),
Err(old) => {
self.user_data
.lock()
.expect("user_data mutex poisoned")
.bodies
.insert(key, old);
Err(crate::error::ApiError::UserDataTypeMismatch)
}
}
}
pub(crate) fn take_shape_user_data<T: 'static>(
&self,
id: ffi::b2ShapeId,
) -> crate::error::ApiResult<Option<T>> {
let key = crate::core::user_data::IdKey::from(id);
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.shapes
.remove(&key);
let Some(old) = old else { return Ok(None) };
match old.try_into_value::<T>() {
Ok(v) => Ok(Some(v)),
Err(old) => {
self.user_data
.lock()
.expect("user_data mutex poisoned")
.shapes
.insert(key, old);
Err(crate::error::ApiError::UserDataTypeMismatch)
}
}
}
pub(crate) fn take_joint_user_data<T: 'static>(
&self,
id: ffi::b2JointId,
) -> crate::error::ApiResult<Option<T>> {
let key = crate::core::user_data::IdKey::from(id);
let old = self
.user_data
.lock()
.expect("user_data mutex poisoned")
.joints
.remove(&key);
let Some(old) = old else { return Ok(None) };
match old.try_into_value::<T>() {
Ok(v) => Ok(Some(v)),
Err(old) => {
self.user_data
.lock()
.expect("user_data mutex poisoned")
.joints
.insert(key, old);
Err(crate::error::ApiError::UserDataTypeMismatch)
}
}
}
#[cfg(feature = "serialize")]
pub(crate) fn record_body(&self, id: ffi::b2BodyId) {
self.registries
.lock()
.expect("registries mutex poisoned")
.record_body(id);
}
#[cfg(feature = "serialize")]
pub(crate) fn record_chain(
&self,
id: crate::types::ChainId,
meta: crate::core::serialize_registry::ChainCreateMeta,
) {
self.registries
.lock()
.expect("registries mutex poisoned")
.record_chain(id, meta);
}
#[cfg(feature = "serialize")]
pub(crate) fn record_shape_flags(&self, sid: ffi::b2ShapeId, def: &ffi::b2ShapeDef) {
self.registries
.lock()
.expect("registries mutex poisoned")
.record_shape_flags(sid, def);
}
#[cfg(feature = "serialize")]
pub(crate) fn remove_chain(&self, id: crate::types::ChainId) {
self.registries
.lock()
.expect("registries mutex poisoned")
.remove_chain(id);
}
#[cfg(feature = "serialize")]
pub(crate) fn remove_shape_flags(&self, sid: ffi::b2ShapeId) {
self.registries
.lock()
.expect("registries mutex poisoned")
.remove_shape_flags(sid);
}
#[cfg(feature = "serialize")]
pub(crate) fn cleanup_before_destroy_body(&self, id: ffi::b2BodyId) {
crate::core::callback_state::assert_not_in_callback();
let mut r = self.registries.lock().expect("registries mutex poisoned");
r.remove_shape_flags_for_body(id);
r.remove_chains_for_body(id);
r.remove_body(id);
}
}
pub(crate) struct BorrowedEventBuffersGuard {
core: Arc<WorldCore>,
}
impl Drop for BorrowedEventBuffersGuard {
fn drop(&mut self) {
let prev = self
.core
.borrowed_event_buffers
.fetch_sub(1, Ordering::Relaxed);
debug_assert!(prev > 0, "borrowed_event_buffers counter underflow");
}
}
impl Drop for WorldCore {
fn drop(&mut self) {
self.clear_world_user_data();
let _guard = crate::core::box2d_lock::lock();
unsafe { ffi::b2DestroyWorld(self.id) };
}
}