use crate::{
global::{CallbackReturn, EntityId},
internal::{conversion::FromBindgen, executor::EXECUTOR, wit},
};
#[cfg(any(feature = "client", feature = "server"))]
use crate::internal::conversion::IntoBindgen;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Source {
Runtime,
#[cfg(feature = "client")]
Server,
#[cfg(feature = "server")]
Client {
user_id: String,
},
Local(EntityId),
}
impl Source {
pub fn runtime(self) -> bool {
matches!(self, Source::Runtime)
}
#[cfg(feature = "server")]
pub fn client_user_id(self) -> Option<String> {
if let Source::Client { user_id } = self {
Some(user_id)
} else {
None
}
}
#[cfg(feature = "server")]
pub fn client_entity_id(self) -> Option<EntityId> {
let Some(user_id) = self.client_user_id() else { return None; };
let Some(player_id) = crate::player::get_by_user_id(&user_id) else { return None; };
Some(player_id)
}
pub fn local(self) -> Option<EntityId> {
match self {
Source::Local(id) => Some(id),
_ => None,
}
}
}
impl FromBindgen for wit::guest::Source {
type Item = Source;
fn from_bindgen(self) -> Self::Item {
match self {
wit::guest::Source::Runtime => Source::Runtime,
#[cfg(feature = "client")]
wit::guest::Source::Server => Source::Server,
#[cfg(feature = "server")]
wit::guest::Source::Client(user_id) => Source::Client { user_id },
wit::guest::Source::Local(entity_id) => Source::Local(entity_id.from_bindgen()),
#[cfg(not(feature = "client"))]
wit::guest::Source::Server => unreachable!(),
#[cfg(not(feature = "server"))]
wit::guest::Source::Client(_user_id) => unreachable!(),
}
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Target {
LocalBroadcast,
Local(EntityId),
#[cfg(feature = "client")]
ServerUnreliable,
#[cfg(feature = "client")]
ServerReliable,
#[cfg(feature = "server")]
ClientBroadcastUnreliable,
#[cfg(feature = "server")]
ClientBroadcastReliable,
#[cfg(feature = "server")]
ClientTargetedUnreliable(
String,
),
#[cfg(feature = "server")]
ClientTargetedReliable(
String,
),
}
#[cfg(feature = "client")]
impl IntoBindgen for Target {
type Item = wit::client_message::Target;
fn into_bindgen(self) -> Self::Item {
match self {
Target::ServerUnreliable => Self::Item::ServerUnreliable,
Target::ServerReliable => Self::Item::ServerReliable,
Target::LocalBroadcast => Self::Item::LocalBroadcast,
Target::Local(id) => Self::Item::Local(id.into_bindgen()),
#[cfg(feature = "server")]
_ => unreachable!(),
}
}
}
#[cfg(feature = "server")]
impl<'a> IntoBindgen for &'a Target {
type Item = wit::server_message::Target<'a>;
fn into_bindgen(self) -> Self::Item {
match self {
Target::ClientBroadcastUnreliable => Self::Item::ClientBroadcastUnreliable,
Target::ClientBroadcastReliable => Self::Item::ClientBroadcastReliable,
Target::ClientTargetedUnreliable(user_id) => {
Self::Item::ClientTargetedUnreliable(user_id.as_str())
}
Target::ClientTargetedReliable(user_id) => {
Self::Item::ClientTargetedReliable(user_id.as_str())
}
Target::LocalBroadcast => Self::Item::LocalBroadcast,
Target::Local(id) => Self::Item::Local(id.into_bindgen()),
#[cfg(feature = "client")]
_ => unreachable!(),
}
}
}
pub fn send<T: Message>(target: Target, data: &T) {
#[cfg(all(feature = "client", not(feature = "server")))]
wit::client_message::send(
target.into_bindgen(),
T::id(),
&data.serialize_message().unwrap(),
);
#[cfg(all(feature = "server", not(feature = "client")))]
wit::server_message::send(
target.into_bindgen(),
T::id(),
&data.serialize_message().unwrap(),
);
#[cfg(any(
all(not(feature = "server"), not(feature = "client")),
all(feature = "server", feature = "client")
))]
let _ = (target, data);
}
pub struct Listener(String, u128);
impl Listener {
pub fn stop(self) {
EXECUTOR.unregister_callback(&self.0, self.1);
}
}
#[allow(clippy::collapsible_else_if)]
pub fn subscribe<R: CallbackReturn, T: Message>(
mut callback: impl FnMut(Source, T) -> R + 'static,
) -> Listener {
let id = T::id();
wit::message::subscribe(id);
Listener(
id.to_string(),
EXECUTOR.register_callback(
id.to_string(),
Box::new(move |source, data| {
callback(source.clone().from_bindgen(), T::deserialize_message(data)?)
.into_result()?;
Ok(())
}),
),
)
}
pub trait ModuleMessage: Message {
fn send(&self, target: Target) {
self::send(target, self)
}
fn send_local_broadcast(&self) {
self.send(Target::LocalBroadcast)
}
fn send_local(&self, module_id: EntityId) {
self.send(Target::Local(module_id))
}
#[cfg(feature = "client")]
fn send_server_unreliable(&self) {
self.send(Target::ServerUnreliable)
}
#[cfg(feature = "client")]
fn send_server_reliable(&self) {
self.send(Target::ServerReliable)
}
#[cfg(feature = "server")]
fn send_client_broadcast_unreliable(&self) {
self.send(Target::ClientBroadcastUnreliable)
}
#[cfg(feature = "server")]
fn send_client_broadcast_reliable(&self) {
self.send(Target::ClientBroadcastReliable)
}
#[cfg(feature = "server")]
fn send_client_targeted_unreliable(&self, user_id: String) {
self.send(Target::ClientTargetedUnreliable(user_id))
}
#[cfg(feature = "server")]
fn send_client_targeted_reliable(&self, user_id: String) {
self.send(Target::ClientTargetedReliable(user_id))
}
fn subscribe<R: CallbackReturn>(callback: impl FnMut(Source, Self) -> R + 'static) -> Listener {
self::subscribe(callback)
}
}
pub trait RuntimeMessage: Message {
fn subscribe<R: CallbackReturn>(mut callback: impl FnMut(Self) -> R + 'static) -> Listener {
self::subscribe(move |_source, msg| callback(msg))
}
}
mod serde {
pub use ambient_project_rt::message_serde::*;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::global::EntityId;
impl MessageSerde for EntityId {
fn serialize_message_part(&self, output: &mut Vec<u8>) -> Result<(), MessageSerdeError> {
output.write_u64::<BigEndian>(self.id0)?;
output.write_u64::<BigEndian>(self.id1)?;
Ok(())
}
fn deserialize_message_part(
input: &mut dyn std::io::Read,
) -> Result<Self, MessageSerdeError> {
let (id0, id1) = (
input.read_u64::<BigEndian>()?,
input.read_u64::<BigEndian>()?,
);
Ok(Self { id0, id1 })
}
}
}
pub use serde::*;