use super::{ffi, AnimationRequest, Entity, EntityOwned, Physics, PlayerId, World};
use crate::{Ray3, Vec3};
use ark_api_ffi::TransparentPad;
pub use ffi::AnimationReplyType as AnimationReply;
use ffi::{
ComponentType, ForceMode, ForceType, Space, Value, {Message, SpatialQueryOptions},
};
use std::collections::{HashMap, HashSet, VecDeque};
type AnimationRequestId = u64;
use super::ffi::v4::SpatialQueryHit as SpatialQueryHitFfi;
pub struct Animation {
request_id: AnimationRequestId,
}
impl Animation {
pub fn enqueue_request_in_global_queue(request: &AnimationRequest) -> Self {
let request_id = EntityMessenger::get().global_queue().animate_value(request);
Self { request_id }
}
pub fn enqueue_request(request: &AnimationRequest, queue: &mut EntityMessageQueue) -> Self {
let request_id = queue.animate_value(request);
Self { request_id }
}
pub fn result(&self) -> MessageResponse<&ffi::AnimationReplyType> {
EntityMessenger::get().animation_reply(self.request_id)
}
}
#[derive(Copy, Clone)]
#[allow(missing_docs)]
pub enum SpatialQueryType {
Raycast {
direction: Vec3,
min_distance: f32,
max_distance: f32,
spherecast_radius: f32,
},
}
#[derive(Copy, Clone)]
pub struct SpatialQueryRequest {
query_type: SpatialQueryType,
position: Vec3,
max_hits: u32,
layer_mask: u64,
options: SpatialQueryOptions,
ignore_entity: Option<Entity>,
}
impl SpatialQueryRequest {
#[allow(deprecated)] pub fn as_message(&self, request_id: u64, hits_data_ptr: u32) -> Message {
use ffi::*;
match self.query_type {
SpatialQueryType::Raycast {
direction,
min_distance,
max_distance,
spherecast_radius,
} => Message {
msg_type: MessageType::SpatialQuerySpherecast,
msg_payload: MessagePayload {
spatial_query_spherecast: TransparentPad::new(SpatialQuerySpherecast {
common: SpatialQueryCommon2 {
id: request_id,
max_hits: self.max_hits,
hits_data_ptr,
layer_mask: self.layer_mask,
options: self.options,
position: self.position.into(),
_pad: Default::default(),
},
direction: direction.into(),
min_t: min_distance,
max_t: max_distance,
spherecast_radius,
_pad0: Default::default(),
_pad1: Default::default(),
}),
},
_pad: Default::default(),
},
}
}
}
#[derive(Copy, Clone)]
pub struct SpatialQueryRequestBuilder {
request: SpatialQueryRequest,
}
impl SpatialQueryRequestBuilder {
pub fn raycast(ray: Ray3, range: std::ops::Range<f32>) -> Self {
Self {
request: SpatialQueryRequest {
query_type: SpatialQueryType::Raycast {
direction: ray.dir,
min_distance: range.start,
max_distance: range.end,
spherecast_radius: 0.0,
},
position: ray.origin,
max_hits: 1,
layer_mask: !0u64,
options: SpatialQueryOptions::empty(),
ignore_entity: None,
},
}
}
pub fn spherecast(ray: Ray3, range: std::ops::Range<f32>, radius: f32) -> Self {
Self {
request: SpatialQueryRequest {
query_type: SpatialQueryType::Raycast {
direction: ray.dir,
min_distance: range.start,
max_distance: range.end,
spherecast_radius: radius,
},
position: ray.origin,
max_hits: 1,
layer_mask: !0u64,
options: SpatialQueryOptions::PHYSX_QUERY,
ignore_entity: None,
},
}
}
pub fn with_max_hits(&mut self, max_hits: usize) -> &mut Self {
self.request.max_hits = max_hits.min(u32::MAX as usize) as u32;
self
}
pub fn with_layer_mask(&mut self, layer_mask: super::EntityLayerMask) -> &mut Self {
self.request.layer_mask = layer_mask.value();
self
}
pub fn with_ignore_entity(&mut self, entity: Entity) -> &mut Self {
self.request.ignore_entity = Some(entity);
self
}
pub fn with_options(&mut self, options: SpatialQueryOptions) -> &mut Self {
self.request.options |= options;
self
}
pub fn build(&self) -> SpatialQueryRequest {
self.request
}
}
impl std::ops::Deref for SpatialQueryRequestBuilder {
type Target = SpatialQueryRequest;
fn deref(&self) -> &Self::Target {
&self.request
}
}
type SpatialQueryRequestId = u64;
pub struct SpatialQuery {
request_id: SpatialQueryRequestId,
}
impl SpatialQuery {
pub fn enqueue_request_in_global_queue(request: &SpatialQueryRequest) -> Self {
let request_id = EntityMessenger::get()
.global_queue()
.spatial_query_request(request);
Self { request_id }
}
pub fn enqueue_request(request: &SpatialQueryRequest, queue: &mut EntityMessageQueue) -> Self {
let request_id = queue.spatial_query_request(request);
Self { request_id }
}
pub fn result(&self) -> MessageResponse<&[SpatialQueryHit]> {
EntityMessenger::get().spatial_query_result(self.request_id)
}
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct SpatialQueryHit {
pub entity: Entity,
pub point: Vec3,
pub distance: f32,
pub normal: Vec3,
}
static_assertions::assert_eq_size!(SpatialQueryHit, SpatialQueryHitFfi);
pub type AnimationReplyFn<'message> = dyn FnMut(&ffi::AnimationReplyType) + 'message;
pub type SpatialQueryResultFn<'message> = dyn FnMut(&[SpatialQueryHit]) + 'message;
pub type CollisionEventFn<'message> = dyn FnMut(&[ffi::OnCollision]) + 'message;
pub type TriggerEventFn<'message> = dyn FnMut(&[ffi::OnTrigger]) + 'message;
#[derive(Default)]
pub struct EntityMessageDispatcher<'message> {
spatial_queries: VecDeque<(SpatialQuery, Box<SpatialQueryResultFn<'message>>)>,
animations: VecDeque<(Animation, Box<AnimationReplyFn<'message>>)>,
collisions: Vec<(Entity, Box<CollisionEventFn<'message>>)>,
triggers: Vec<(Entity, Box<TriggerEventFn<'message>>)>,
}
impl<'message> EntityMessageDispatcher<'message> {
pub fn remove_all_listeners(&mut self) {
self.spatial_queries.clear();
self.animations.clear();
self.collisions.clear();
self.triggers.clear();
}
fn notify_destroyed(&mut self, entity: Entity) {
self.collisions.retain(|(e, _func)| *e != entity);
self.triggers.retain(|(e, _func)| *e != entity);
}
pub fn spatial_query_in_global_queue<T>(&mut self, request: &SpatialQueryRequest, f: T)
where
T: FnMut(&[SpatialQueryHit]) + 'message,
{
self.spatial_queries.push_back((
SpatialQuery::enqueue_request_in_global_queue(request),
Box::new(f),
));
}
pub fn spatial_query<T>(
&mut self,
queue: &mut EntityMessageQueue,
request: &SpatialQueryRequest,
f: T,
) where
T: FnMut(&[SpatialQueryHit]) + 'message,
{
self.spatial_queries
.push_back((SpatialQuery::enqueue_request(request, queue), Box::new(f)));
}
pub fn animation_in_global_queue<T>(&mut self, request: &AnimationRequest, f: T)
where
T: FnMut(&ffi::AnimationReplyType) + 'message,
{
self.animations.push_back((
Animation::enqueue_request_in_global_queue(request),
Box::new(f),
));
}
pub fn animation<T>(&mut self, queue: &mut EntityMessageQueue, request: &AnimationRequest, f: T)
where
T: FnMut(&ffi::AnimationReplyType) + 'message,
{
self.animations
.push_back((Animation::enqueue_request(request, queue), Box::new(f)));
}
fn spatial_query_result(&mut self) -> bool {
for (idx, (req, func)) in self.spatial_queries.iter_mut().enumerate() {
if let MessageResponse::Ok(query_hit) =
EntityMessenger::get().spatial_query_result(req.request_id)
{
func(query_hit);
let _ = self.spatial_queries.swap_remove_front(idx);
return true;
}
}
false
}
fn animation_reply(&mut self) -> bool {
for (idx, (req, func)) in self.animations.iter_mut().enumerate() {
if let MessageResponse::Ok(result) =
EntityMessenger::get().animation_reply(req.request_id)
{
func(result);
let _ = self.animations.swap_remove_front(idx);
return true;
}
}
false
}
pub fn on_collision<T>(&mut self, entity: Entity, f: T)
where
T: FnMut(&[ffi::OnCollision]) + 'message,
{
EntityMessenger::get().listen_collisions(entity);
self.collisions.push((entity, Box::new(f)));
}
pub fn on_trigger<T>(&mut self, entity: Entity, f: T)
where
T: FnMut(&[ffi::OnTrigger]) + 'message,
{
EntityMessenger::get().listen_triggers(entity);
self.triggers.push((entity, Box::new(f)));
}
pub fn update(&mut self) {
puffin::profile_function!();
self.cleanup();
while self.animation_reply() {}
while self.spatial_query_result() {}
for (entity, func) in &mut self.collisions {
if let Some(collisions) = EntityMessenger::get().collisions(*entity) {
if !collisions.is_empty() {
func(&collisions);
}
}
}
for (entity, func) in &mut self.triggers {
if let Some(triggers) = EntityMessenger::get().triggers(*entity) {
if !triggers.is_empty() {
func(&triggers);
}
}
}
}
fn cleanup(&mut self) {
let world = World::instance();
for e in world.entities_destroyed_this_and_last_frame() {
self.notify_destroyed(*e);
}
}
pub fn global() -> &'static mut Self {
unsafe {
static mut MESSENGER: Option<EntityMessageDispatcher<'static>> = None;
if MESSENGER.is_none() {
MESSENGER = Some(Self::default());
}
MESSENGER.as_mut().unwrap()
}
}
}
struct EntityMessageQueueState {
message_request_count: u32, message_request_range: u32,
listen_to_entities: HashMap<Entity, u32>, spatial_query_requests: HashMap<SpatialQueryRequestId, Vec<SpatialQueryHitFfi>>, collision_requests: HashSet<Entity>,
trigger_requests: HashSet<Entity>,
animation_requests: HashSet<AnimationRequestId>,
global_entity: Entity,
outbox: HashMap<Entity, Vec<Message>>,
}
impl EntityMessageQueueState {
fn new(message_request_range: u32, global_entity: Entity) -> Self {
Self {
message_request_count: 0,
message_request_range,
listen_to_entities: HashMap::new(),
spatial_query_requests: HashMap::new(),
animation_requests: HashSet::new(),
collision_requests: HashSet::new(),
trigger_requests: HashSet::new(),
global_entity,
outbox: HashMap::new(),
}
}
}
pub struct EntityMessageQueue {
state: EntityMessageQueueState,
}
impl EntityMessageQueue {
fn new(message_request_range: u32, global_entity: Entity) -> Self {
Self {
state: EntityMessageQueueState::new(message_request_range, global_entity),
}
}
fn notify_destroyed(&mut self, entity: Entity) {
if self.state.listen_to_entities.contains_key(&entity) {
self.state.listen_to_entities.remove(&entity);
self.state.collision_requests.remove(&entity);
self.state.trigger_requests.remove(&entity);
}
}
fn push_message(&mut self, entity: Entity, msg: &Message) {
self.state
.outbox
.entry(entity)
.or_insert_with(Vec::new)
.push(*msg);
}
fn add_listener_to_entity(&mut self, entity: Entity) {
*self.state.listen_to_entities.entry(entity).or_insert(0) += 1;
}
pub fn set_value(
&mut self,
entity: Entity,
component_type: ComponentType,
param_id: u32,
value: &Value,
) {
use ffi::*;
self.push_message(
entity,
&Message {
msg_type: MessageType::SetValue,
msg_payload: MessagePayload {
set_value: TransparentPad::new(SetValue {
component_type,
param_id,
value: *value,
_pad: Default::default(),
}),
},
_pad: Default::default(),
},
);
}
fn animate_value_send(&mut self, request: &AnimationRequest, request_id: u64) {
use ffi::*;
self.push_message(
request.id,
&Message {
msg_type: if request.enqueue {
MessageType::AnimateValueEnqueue
} else {
MessageType::AnimateValue
},
msg_payload: MessagePayload {
animate_value: TransparentPad::new(AnimateValue {
id: request_id,
param: SetValue {
component_type: request.component,
param_id: request.param,
value: request.target,
_pad: Default::default(),
},
options: request.options,
_pad: Default::default(),
}),
},
_pad: Default::default(),
},
);
}
pub fn animate_value_no_reply(&mut self, request: &AnimationRequest) {
use ffi::*;
self.animate_value_send(request, ANIMATION_REQUEST_ID_NONE);
}
fn alloc_request_id(&mut self) -> u64 {
let request_id = u64::from(self.state.message_request_count)
| (u64::from(self.state.message_request_range) << 32);
self.state.message_request_count += 1;
request_id
}
#[allow(clippy::too_many_arguments)]
pub fn animate_value(&mut self, request: &AnimationRequest) -> AnimationRequestId {
let request_id = self.alloc_request_id();
self.animate_value_send(request, request_id);
let _ = self.state.animation_requests.insert(request_id);
self.add_listener_to_entity(request.id);
request_id
}
pub fn stop_animating_value(
&mut self,
entity: Entity,
component_type: ComponentType,
param_id: u32,
value: &Value,
) {
use ffi::*;
self.push_message(
entity,
&Message {
msg_type: MessageType::StopAnimatingValue,
msg_payload: MessagePayload {
stop_animating_value: TransparentPad::new(SetValue {
component_type,
param_id,
value: *value,
_pad: Default::default(),
}),
},
_pad: Default::default(),
},
);
}
pub fn set_active(&mut self, entity: Entity, component_type: ComponentType) {
use ffi::*;
self.push_message(
entity,
&Message {
msg_type: MessageType::SetActive,
msg_payload: MessagePayload {
set_active: TransparentPad::new(SetActive { component_type }),
},
_pad: Default::default(),
},
);
}
pub fn set_active_for_player(
&mut self,
entity: Entity,
component_type: ComponentType,
player_id: PlayerId,
) {
use ffi::*;
self.push_message(
entity,
&Message {
msg_type: MessageType::SetActiveForPlayer,
msg_payload: MessagePayload {
set_active_for_player: TransparentPad::new(SetActiveForPlayer {
component_type,
player_id,
}),
},
_pad: Default::default(),
},
);
}
#[allow(clippy::too_many_arguments)]
pub fn physics_force_at_request(
&mut self,
entity: Entity,
force_type: ForceType,
force_mode: ForceMode,
force_space: Space,
pos_space: Space,
vector: Vec3,
pos: Vec3,
) {
fn is_finite(v: Vec3) -> bool {
v.x.is_finite() && v.y.is_finite() && v.z.is_finite()
}
assert!(is_finite(vector));
assert!(is_finite(pos));
use ffi::*;
self.push_message(
entity,
&Message {
msg_type: MessageType::PhysicsForceRequest2,
msg_payload: MessagePayload {
physics_force_request2: TransparentPad::new(PhysicsForceRequest2 {
force_type,
force_mode,
force_space,
pos_space,
vector: vector.into(),
pos: pos.into(),
}),
},
_pad: Default::default(),
},
);
}
pub fn spatial_query_request(
&mut self,
request: &SpatialQueryRequest,
) -> SpatialQueryRequestId {
let request_id = self.alloc_request_id();
let entity = request
.ignore_entity
.unwrap_or_else(|| self.get_global_entity());
let mut hits: Vec<SpatialQueryHitFfi> =
vec![SpatialQueryHitFfi::invalid(); request.max_hits as usize];
self.push_message(
entity,
&request.as_message(request_id, hits.as_mut_ptr() as u32),
);
let _ = self.state.spatial_query_requests.insert(request_id, hits);
self.add_listener_to_entity(entity);
request_id
}
fn get_global_entity(&self) -> Entity {
self.state.global_entity
}
pub fn listen_collisions(&mut self, entity: Entity) {
let _ = self.state.collision_requests.insert(entity);
}
pub fn listen_triggers(&mut self, entity: Entity) {
let _ = self.state.trigger_requests.insert(entity);
}
}
impl Drop for EntityMessageQueue {
fn drop(&mut self) {
let mut state = EntityMessageQueueState::new(
self.state.message_request_range,
self.state.global_entity,
);
std::mem::swap(&mut state, &mut self.state);
EntityMessenger::get().submit_queue_state(state);
}
}
pub fn send_messages(outbox: &HashMap<Entity, Vec<Message>>) {
for (entity, messages) in outbox {
World::send_messages(*entity, messages);
}
}
#[allow(missing_docs)]
pub enum MessageResponse<T> {
NonExistent,
Pending,
Empty,
Ok(T),
}
pub struct EntityMessenger {
message_queue_range: u32,
listen_to_entities: HashMap<Entity, u32>, spatial_query_result_handlers: HashMap<SpatialQueryRequestId, (bool, Vec<SpatialQueryHitFfi>)>,
collision_event_queues: HashMap<Entity, Vec<ffi::OnCollision>>,
trigger_event_queues: HashMap<Entity, Vec<ffi::OnTrigger>>,
animation_replies: HashMap<AnimationRequestId, Option<ffi::AnimationReplyType>>,
global_entity: EntityOwned,
global_queue: EntityMessageQueue,
allow_sending_messages_to_dead_entities: bool,
}
impl EntityMessenger {
fn new() -> Self {
let entity = EntityOwned::create_empty("entity_messenger");
let entity_handle = entity.as_entity();
Self {
message_queue_range: 1,
listen_to_entities: HashMap::new(),
spatial_query_result_handlers: HashMap::new(),
animation_replies: HashMap::new(),
collision_event_queues: Default::default(),
trigger_event_queues: Default::default(),
global_entity: entity,
global_queue: EntityMessageQueue::new(0, entity_handle),
allow_sending_messages_to_dead_entities: false,
}
}
fn notify_destroyed(&mut self, entity: Entity) {
if self.listen_to_entities.contains_key(&entity) {
self.listen_to_entities.remove(&entity);
self.collision_event_queues.remove(&entity);
self.trigger_event_queues.remove(&entity);
}
self.global_queue.notify_destroyed(entity);
}
fn add_listener_to_entity(&mut self, entity: Entity, count: u32) {
*self.listen_to_entities.entry(entity).or_insert(0) += count;
}
fn remove_listener_from_entity(&mut self, entity: Entity) {
if let Some(count) = self.listen_to_entities.get_mut(&entity) {
if *count > 0 {
*count -= 1;
if *count == 0 {
let _ = self.listen_to_entities.remove(&entity);
}
}
}
}
pub fn spatial_query_result(
&self,
id: SpatialQueryRequestId,
) -> MessageResponse<&[SpatialQueryHit]> {
if let Some((received, hits)) = self.spatial_query_result_handlers.get(&id) {
if *received {
let slice = unsafe {
static_assertions::assert_eq_size!(SpatialQueryHit, SpatialQueryHitFfi);
let ptr = hits.as_slice().as_ptr().cast::<SpatialQueryHit>();
std::slice::from_raw_parts(ptr, hits.len())
};
MessageResponse::Ok(slice)
} else {
MessageResponse::Pending
}
} else {
MessageResponse::NonExistent
}
}
pub fn animation_reply(
&self,
id: AnimationRequestId,
) -> MessageResponse<&ffi::AnimationReplyType> {
if let Some(reply) = self.animation_replies.get(&id) {
if let Some(reply) = reply {
MessageResponse::Ok(reply)
} else {
MessageResponse::Pending
}
} else {
MessageResponse::NonExistent
}
}
fn setup_collision_events_mask(physics_entity: Entity, ty: &'static str) {
if !physics_entity.is_valid() {
log::warn!(
"Cannot listen for {} on an invalid entity (was it destroyed?)",
ty
);
return;
}
if !physics_entity.has::<Physics>() {
log::warn!("Cannot listen for {} on a non-physics entity", ty);
return;
}
let mask = physics_entity.physics().collision_events_mask().get();
if mask == 0u64 {
physics_entity.physics().collision_events_mask().set(!0u64);
}
}
pub fn listen_collisions(&mut self, entity: Entity) {
Self::setup_collision_events_mask(entity, "collisions");
if !self.collision_event_queues.contains_key(&entity) {
self.add_listener_to_entity(entity, 1);
}
let _ = self
.collision_event_queues
.entry(entity)
.or_insert_with(Vec::new);
}
pub fn listen_triggers(&mut self, entity: Entity) {
Self::setup_collision_events_mask(entity, "triggers");
if !self.trigger_event_queues.contains_key(&entity) {
self.add_listener_to_entity(entity, 1);
}
let _ = self
.trigger_event_queues
.entry(entity)
.or_insert_with(Vec::new);
}
pub fn collisions(&mut self, entity: Entity) -> Option<Vec<ffi::OnCollision>> {
self.listen_collisions(entity);
self.collision_event_queues.get(&entity).cloned()
}
pub fn triggers(&mut self, entity: Entity) -> Option<Vec<ffi::OnTrigger>> {
self.listen_triggers(entity);
self.trigger_event_queues.get(&entity).cloned()
}
pub fn global_queue(&mut self) -> &mut EntityMessageQueue {
&mut self.global_queue
}
pub fn send_messages(&mut self) {
puffin::profile_function!();
self.global_queue = self.new_queue();
}
pub fn submit_queue(&mut self, queue_to_submit: &mut EntityMessageQueue) {
*queue_to_submit = self.new_queue();
}
pub fn new_queue(&mut self) -> EntityMessageQueue {
let queue =
EntityMessageQueue::new(self.message_queue_range, self.global_entity.as_entity());
self.message_queue_range += 1;
queue
}
fn submit_queue_state(&mut self, queue: EntityMessageQueueState) {
for (entity, count) in queue.listen_to_entities {
self.add_listener_to_entity(entity, count);
}
for entity in queue.collision_requests {
self.listen_collisions(entity);
}
for entity in queue.trigger_requests {
self.listen_triggers(entity);
}
for (request_id, hits) in queue.spatial_query_requests {
let _ = self
.spatial_query_result_handlers
.insert(request_id, (false, hits));
}
for request_id in queue.animation_requests {
let _ = self.animation_replies.insert(request_id, None);
}
let mut outbox = queue.outbox;
if !self.allow_sending_messages_to_dead_entities {
let world = World::instance();
for e in world.entities_destroyed_this_and_last_frame() {
outbox.remove(e);
}
}
send_messages(&outbox);
}
fn receive_messages(&self) -> Vec<(Entity, Message)> {
puffin::profile_function!();
self.listen_to_entities
.keys()
.flat_map(|entity| {
let messages = World::retrieve_messages(*entity);
std::iter::repeat(*entity).zip(messages.into_iter())
})
.collect()
}
pub fn set_allow_sending_messages_to_dead_entities(&mut self, allow: bool) {
self.allow_sending_messages_to_dead_entities = allow;
}
fn cleanup(&mut self) {
let world = World::instance();
for e in world.entities_destroyed_this_and_last_frame() {
self.notify_destroyed(*e);
}
}
pub fn process_messages(&mut self) -> usize {
puffin::profile_function!();
use ffi::*;
self.cleanup();
let mut listeners_to_remove = vec![];
self.send_messages();
let incoming = self.receive_messages();
let processed_count = incoming.len();
for queue in self.collision_event_queues.values_mut() {
queue.clear();
}
for queue in self.trigger_event_queues.values_mut() {
queue.clear();
}
self.spatial_query_result_handlers
.retain(|_request_id, (received, _entities)| !*received);
self.animation_replies
.retain(|_request_id, reply| reply.is_none());
for (entity, message) in incoming {
match message {
Message {
msg_type: MessageType::SpatialQueryHits,
msg_payload: payload,
..
} => {
let (id, num_hits) = unsafe {
(
payload.spatial_query_hits.id,
payload.spatial_query_hits.num_hits,
)
};
if let Some((received, result_data)) =
self.spatial_query_result_handlers.get_mut(&id)
{
*received = true;
result_data.resize(num_hits as usize, SpatialQueryHitFfi::invalid());
}
}
Message {
msg_type: MessageType::OnCollision,
msg_payload: payload,
..
} => {
let on_collision = unsafe { &payload.on_collision };
if let Some(queue) = self.collision_event_queues.get_mut(&entity) {
queue.push(*on_collision.as_ref());
} else {
let _ = self
.collision_event_queues
.insert(entity, vec![*on_collision.as_ref()]);
}
}
Message {
msg_type: MessageType::OnTrigger,
msg_payload: payload,
..
} => {
let on_trigger = unsafe { &payload.on_trigger };
if let Some(queue) = self.trigger_event_queues.get_mut(&entity) {
queue.push(*on_trigger.as_ref());
} else {
let _ = self
.trigger_event_queues
.insert(entity, vec![*on_trigger.as_ref()]);
}
}
Message {
msg_type: MessageType::AnimationReply,
msg_payload: payload,
..
} => {
let id = unsafe { payload.animation_reply.id };
let reply_type = unsafe { payload.animation_reply.reply_type };
let _ = self.animation_replies.insert(id, Some(reply_type));
listeners_to_remove.push(entity);
}
_ => {
log::warn!("Unknown message type {:?}", message);
}
}
}
for remove in listeners_to_remove {
self.remove_listener_from_entity(remove);
}
processed_count
}
pub fn get() -> &'static mut Self {
unsafe {
static mut MESSENGER: Option<EntityMessenger> = None;
if MESSENGER.is_none() {
MESSENGER = Some(Self::new());
}
MESSENGER.as_mut().unwrap()
}
}
}