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;