1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
define_api_id!(0xbb0f_0e2f_ff53_2a51, "behavior-v0");
use crate::ErrorCode;
use bytemuck::Pod;
use bytemuck::Zeroable;
pub type ActorId = u32;
/// Sentinel value indicating that a message is a system message, with open interpretation of what
/// this entails, by the behavior module.
/// Keep in sync with the same value in the `behavior_controller_v0` API.
pub const CONTROLLER_SENTINEL_ACTOR_ID: ActorId = !0u32;
/// Sentinel value indicating that a system message has no instance tied to it.
/// Keep in sync with the same value in the `behavior_controller_v0` API.
pub const INCOMING_MESSAGE_NO_INSTANCE_SENTINEL: u64 = 0xffff_ffff_ffff;
pub type Guid = u128;
/// A unique id local to a behavior module representing a behavior type
pub type LocalBehaviorTypeId = u16;
/// A unique id of a couple comprising a local behavior type id within a behavior module as well as an instance id
pub type ForeignBehaviorInstanceId = u64;
pub const INVALID_GUID_COMPONENT: u64 = !0u64;
/// An outgoing message's address, targeting either an entire actor or a specific behavior
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Pod, Zeroable)]
pub struct OutgoingMessageAddr {
pub to_actor_id: ActorId,
_pad: u32,
behavior_guid_hi: u64,
behavior_guid_lo: u64,
}
impl OutgoingMessageAddr {
/// Constructs a new `OutgoingMessageAddr` targeting an actor
pub fn new(to_actor_id: ActorId) -> Self {
Self {
to_actor_id,
_pad: 0,
behavior_guid_hi: INVALID_GUID_COMPONENT,
behavior_guid_lo: INVALID_GUID_COMPONENT,
}
}
/// Constructs a new `OutgoingMessageAddr` targeting a specific behavior on an actor.
///
/// # Errors
///
/// Returns an [`ErrorCode::InvalidArguments`] if the lower or higher `u64` component
/// of the provided guid is an [`INVALID_GUID_COMPONENT`]
pub fn new_to_behavior(to_actor_id: ActorId, behavior_guid: Guid) -> Result<Self, ErrorCode> {
let behavior_guid_hi = (behavior_guid >> 64) as u64;
let behavior_guid_lo = behavior_guid as u64;
if behavior_guid_hi != INVALID_GUID_COMPONENT || behavior_guid_lo != INVALID_GUID_COMPONENT
{
Ok(Self {
to_actor_id,
_pad: 0,
behavior_guid_hi,
behavior_guid_lo,
})
} else {
Err(ErrorCode::InvalidArguments)
}
}
/// A new message targeted at the controller module itself.
pub fn new_to_controller() -> Self {
Self {
to_actor_id: CONTROLLER_SENTINEL_ACTOR_ID,
_pad: 0,
behavior_guid_hi: INVALID_GUID_COMPONENT,
behavior_guid_lo: INVALID_GUID_COMPONENT,
}
}
/// Retrieve the address' behavior guid if it was set
pub fn guid(&self) -> Option<Guid> {
if self.behavior_guid_hi == INVALID_GUID_COMPONENT
&& self.behavior_guid_lo == INVALID_GUID_COMPONENT
{
None
} else {
Some(((self.behavior_guid_hi as Guid) << 64) | self.behavior_guid_lo as Guid)
}
}
}
/// An outgoing message that can be sent from a behavior instance to the controller module
///
/// Note: it is up to the user to ensure that the serialized message data this structure points to is still
/// alive when being used.
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Pod, Zeroable)]
pub struct OutgoingMessage {
pub addr: OutgoingMessageAddr,
serialized_message_ptr: u32,
serialized_message_len: u32,
}
impl OutgoingMessage {
/// Construct a new `OutgoingMessage` given the provided address and serialized message data
pub fn new(addr: OutgoingMessageAddr, serialized_message: &[u8]) -> Self {
Self {
addr,
serialized_message_ptr: serialized_message.as_ptr() as _,
serialized_message_len: serialized_message.len() as _,
}
}
/// Retrieve a pointer to the serialized message slice
///
/// Note: there is no check to ensure the data is still alive
pub fn msg_ptr(&self) -> u32 {
self.serialized_message_ptr
}
/// Retrieve the length of the serialized message slice
pub fn msg_len(&self) -> u32 {
self.serialized_message_len
}
}
/// An incoming message sent from the controller module
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Pod, Zeroable)]
pub struct IncomingMessage {
pub instance_id: ForeignBehaviorInstanceId,
pub actor_id: ActorId,
pub serialized_message_ptr: u32,
pub serialized_message_len: u32,
pub _pad: u32,
}
/// Describes a behavior local to a behavior module
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LocalBehaviorRegistration {
pub type_id: LocalBehaviorTypeId,
pub name: String,
}
/// Contains all registration info for an external behavior module
///
/// During behavior module registration, behavior modules must return `RegistrationInfo` as a
/// serialized vector of JSON bytes. Ark will validate the returned registration info and store
/// it internally.
///
/// A controller module can retrieve the registration info from Ark and use it to, for example,
/// create behavior instances of a specific behavior type.
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LocalModuleRegistration {
pub behavior_infos: Vec<LocalBehaviorRegistration>,
}
#[ark_api_macros::ark_bindgen(imports = "ark-behavior-v0")]
mod behavior {
use super::ActorId;
use super::OutgoingMessage;
use crate::pod_helpers::Align16U128;
use crate::FFIResult;
extern "C" {
/// Retrieve a serialized representation of aspect data of the given actor and aspect guid if it exists
///
/// The serialization format has to be defined in a protocol that is agreed upon with the owning controller module
///
/// # Errors
///
/// Returns [`crate::ErrorCode::NotFound`] if no aspect data can be found for the provided arguments
pub fn aspect_get(
aspect_guid_hi: u64,
aspect_guid_lo: u64,
actor_id: ActorId,
) -> FFIResult<Vec<u8>>;
/// Batch send [`OutgoingMessage`]s back to the controller module
///
/// It is important to understand that this doesn't send the message directly to a
/// behavior instance for processing. It is sent to the controller module who then decides
/// what to do with it.
///
/// For performance reasons it is recommended to always batch as many outgoing messages together
/// as that will reduce calls over FFI, which are expensive
#[with_memory]
pub fn send_outgoing_messages(outgoing_messages: &[OutgoingMessage]) -> FFIResult<()>;
/// Retrieve a list of all actors with the given aspect GUID
///
/// The returned byte stream is encoded as follows:
///
/// |--- u32 ---| |--- [`ActorId`] ---| |--- [`ActorId`] ---| |--- repeat..
///
/// The first u32 in the byte stream indicates the total amount of [`ActorId`]s
///
/// # Errors
///
/// Returns [`crate::ErrorCode::NotFound`] on an unrecognized aspect guid
pub fn actors_with_aspect(aspect_guid_hi: u64, aspect_guid_lo: u64) -> FFIResult<Vec<u8>>;
/// A single 128-bit random value, unique to this run.
///
/// Initialize your random number generators that need to be unpredictable from this.
/// Not meant for cryptographic operations.
pub fn random_seed_value() -> Align16U128;
}
}
pub use behavior::safe as safe_v0;
#[cfg(not(target_arch = "wasm32"))]
pub use behavior::HostShim as HostShim_v0;