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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
define_api_id!(0xbb0f_0dfe_ff53_2a60, "applet-v5");
use crate::Align16U128;
use crate::FFIResult;
use crate::TransparentPad;
use bytemuck::CheckedBitPattern;
use bytemuck::NoUninit;
use num_enum::IntoPrimitive;
use num_enum::TryFromPrimitive;
pub use super::applet_v0::CursorMode;
pub use super::applet_v0::EventType;
pub use super::applet_v0::KeyEventType;
pub use super::applet_v0::KeyInput;
pub use super::applet_v0::MouseButton;
pub use super::applet_v0::MouseEventType;
pub use super::applet_v0::TouchEventType;
pub use super::applet_v0::VirtualKeyCode;
pub use super::applet_v0::WindowState;
pub use super::applet_v3::TimestepMode;
pub use super::applet_v4::CursorShape;
pub use super::applet_v4::PlayerIdRepr;
/// Axis input event axis enumeration.
#[repr(u32)]
#[derive(
Copy,
Clone,
Debug,
Hash,
Eq,
PartialEq,
IntoPrimitive,
TryFromPrimitive,
NoUninit,
CheckedBitPattern,
)]
#[non_exhaustive]
pub enum Axis {
LeftStickX = 0, // range -1 to 1
LeftStickY = 1,
RightStickX = 2,
RightStickY = 3,
DpadX = 4, // On Android the DPAD is often exposed as an axis. So let's reserve values for that, but maybe convert to key events in the future.
DpadY = 5,
LeftShoulder = 6, // range 0 to 1
RightShoulder = 7,
}
/// Describes a button of a gamepad controller.
#[repr(u32)]
#[derive(
Copy,
Clone,
Debug,
Hash,
Eq,
PartialEq,
IntoPrimitive,
TryFromPrimitive,
NoUninit,
CheckedBitPattern,
)]
#[non_exhaustive]
pub enum GamepadButton {
North = 0,
East = 1,
South = 2,
West = 3,
Select = 4,
Mode = 5,
Start = 6,
LeftBumper = 7,
RightBumper = 8,
LeftStick = 9,
RightStick = 10,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct MouseInput {
pub event_type: MouseEventType,
/// If event_type == `ButtonPress` or `ButtonRelease`, indicates which button was pressed.
pub button: MouseButton,
/// Position, wheel or relative movement on the X axis. For positions, values are in logical pixels.
pub x: f32,
/// Position, wheel or relative movement on the Y axis. For positions, values are in logical pixels.
pub y: f32,
/// Only for [`MouseEventType::RelativeMove`].
/// Rotation in radians, taking into account mouse sensitivity and optional invert y axis.
pub delta_angle: [f32; 2],
/// Ray origin (world space). Applicable for moves and button presses.
pub ray_origin: [f32; 3],
/// Ray direction (world space). Applicable for moves and button presses.
pub ray_dir: [f32; 3],
}
crate::impl_checked_bit_pattern_for_transparent_pad!(MouseInput);
impl MouseInput {
pub fn to_mouse_input_v0(self) -> super::applet_v0::MouseInput {
super::applet_v0::MouseInput {
event_type: self.event_type,
button: self.button,
x: self.x,
y: self.y,
ray_origin: self.ray_origin,
ray_dir: self.ray_dir,
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct TouchInput {
/// Represents a touch event
pub event_type: TouchEventType,
/// Unique identifier of a finger.
pub id: u32,
/// Position or relative movement on the X axis. For positions, values are in logical pixels.
pub x: f32,
/// Position or relative movement on the Y axis. For positions, values are in logical pixels.
pub y: f32,
/// Only for [`TouchEventType::RelativeMove`].
/// Rotation in radians, taking into account mouse sensitivity and optional invert y axis.
pub delta_angle: [f32; 2],
/// Ray origin (world space). Applicable for moves and button presses.
pub ray_origin: [f32; 3],
/// Ray direction (world space). Applicable for moves and button presses.
pub ray_dir: [f32; 3],
}
crate::impl_checked_bit_pattern_for_transparent_pad!(TouchInput);
impl TouchInput {
pub fn to_touch_input_v0(self) -> super::applet_v0::TouchInput {
super::applet_v0::TouchInput {
event_type: self.event_type,
id: self.id,
x: self.x,
y: self.y,
ray_origin: self.ray_origin,
ray_dir: self.ray_dir,
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, NoUninit, CheckedBitPattern)]
pub struct AxisInput {
/// The axis that was moved.
pub axis: Axis,
/// The new value of the axis.
pub value: f32,
}
crate::impl_checked_bit_pattern_for_transparent_pad!(AxisInput);
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, NoUninit, CheckedBitPattern)]
pub struct GamepadButtonInput {
/// Which gamepad button was pressed or released
pub button: GamepadButton,
/// True = pressed, false = released
pub is_pressed: bool,
pub _pad: [u8; 3],
}
crate::impl_checked_bit_pattern_for_transparent_pad!(GamepadButtonInput);
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, NoUninit, CheckedBitPattern)]
pub struct RawMidiInput {
// TODO: Having padding problems with u64 in unions
pub timestamp_lo: u32,
pub timestamp_hi: u32,
pub device_id: u32,
pub len: u32,
pub data: [u8; 8],
}
impl RawMidiInput {
pub fn timestamp(&self) -> u64 {
u64::from(self.timestamp_lo) | (u64::from(self.timestamp_hi) << 32)
}
}
crate::impl_checked_bit_pattern_for_transparent_pad!(RawMidiInput);
#[repr(u32)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, NoUninit, CheckedBitPattern)]
#[non_exhaustive]
pub enum EventType2 {
Key = 0,
Mouse = 7,
Touch = 8,
Axis = 9,
GamepadButton = 10,
RawMidi = 11,
}
#[repr(C)]
#[derive(Copy, Clone, NoUninit, CheckedBitPattern)]
pub struct TaggedEvent {
pub ty: EventType2,
data: EventUnion,
}
impl TaggedEvent {
pub fn new_key(key: KeyInput) -> Self {
Self {
ty: EventType2::Key,
data: EventUnion {
key: TransparentPad::new(key),
},
}
}
pub fn new_mouse(mouse: MouseInput) -> Self {
Self {
ty: EventType2::Mouse,
data: EventUnion {
mouse: TransparentPad::new(mouse),
},
}
}
pub fn new_touch(touch: TouchInput) -> Self {
Self {
ty: EventType2::Touch,
data: EventUnion {
touch: TransparentPad::new(touch),
},
}
}
pub fn new_axis(axis: AxisInput) -> Self {
Self {
ty: EventType2::Axis,
data: EventUnion {
axis: TransparentPad::new(axis),
},
}
}
pub fn new_gamepad_button(gamepad_button: GamepadButtonInput) -> Self {
Self {
ty: EventType2::GamepadButton,
data: EventUnion {
gamepad_button: TransparentPad::new(gamepad_button),
},
}
}
pub fn new_raw_midi(raw_midi: RawMidiInput) -> Self {
Self {
ty: EventType2::RawMidi,
data: EventUnion {
raw_midi: TransparentPad::new(raw_midi),
},
}
}
pub fn key(&self) -> Option<KeyInput> {
if self.ty == EventType2::Key {
Some(*self.data.as_key())
} else {
None
}
}
pub fn mouse_v0(&self) -> Option<super::applet_v0::MouseInput> {
self.mouse().map(MouseInput::to_mouse_input_v0)
}
pub fn mouse(&self) -> Option<MouseInput> {
if self.ty == EventType2::Mouse {
Some(*self.data.as_mouse())
} else {
None
}
}
pub fn touch_v0(&self) -> Option<super::applet_v0::TouchInput> {
self.touch().map(TouchInput::to_touch_input_v0)
}
pub fn touch(&self) -> Option<TouchInput> {
if self.ty == EventType2::Touch {
Some(*self.data.as_touch())
} else {
None
}
}
pub fn axis(&self) -> Option<AxisInput> {
if self.ty == EventType2::Axis {
Some(*self.data.as_axis())
} else {
None
}
}
pub fn gamepad_button(&self) -> Option<GamepadButtonInput> {
if self.ty == EventType2::GamepadButton {
Some(*self.data.as_gamepad_button())
} else {
None
}
}
pub fn raw_midi(&self) -> Option<RawMidiInput> {
if self.ty == EventType2::RawMidi {
Some(*self.data.as_raw_midi())
} else {
None
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
#[ark_api_macros::ffi_union(size = 48, checked_accessors)]
pub union EventUnion {
pub key: KeyInput,
pub mouse: MouseInput,
pub touch: TouchInput,
pub axis: AxisInput,
pub gamepad_button: GamepadButtonInput,
pub raw_midi: RawMidiInput,
}
impl core::fmt::Debug for EventUnion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let bytes = bytemuck::cast_ref::<Self, [u8; 48]>(self);
write!(f, "EventUnion {{ <opaque>: {bytes:?} }}")
}
}
pub type DetachPlayerHandle = u64;
pub type SessionId = Align16U128;
#[repr(u32)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, NoUninit, CheckedBitPattern)]
#[non_exhaustive]
pub enum DetachPlayerTargetType {
World = 0,
Server = 1,
Player = 2,
}
impl TryFrom<u32> for DetachPlayerTargetType {
type Error = String;
fn try_from(target: u32) -> Result<Self, Self::Error> {
match target {
0 => Ok(Self::World),
1 => Ok(Self::Server),
2 => Ok(Self::Player),
x => Err(format!("{x} is not a valid detach player target")),
}
}
}
/// Default value when a game session is played locally.
pub const LOCAL_SESSION: SessionId = Align16U128(0);
#[ark_api_macros::ark_bindgen(imports = "ark-applet-v5")]
mod applet {
use super::*;
extern "C" {
/// Get the module update and physics simulation frequency in Hz. Default is 60 Hz.
#[deprecated(note = "This will be removed in future API versions, always returns 60.0")]
#[deprecated_infallible]
pub fn simulation_rate() -> f64;
/// Set the module update and physics simulation frequency in Hz. Default is 60 Hz.
#[deprecated(note = "This is ignored and will be removed in future API versions")]
#[deprecated_infallible]
pub fn set_simulation_rate(simulation_rate: f64);
/// Call first to know how many events to expect from `events_get`.
#[deprecated_infallible]
pub fn events_count(player_id: PlayerIdRepr) -> u64;
/// Writes all events into `out_events` for a specific player (0 == local).
///
/// Use `events_count` to know how many events to expect.
#[deprecated_infallible]
pub fn events_get(player_id: PlayerIdRepr, out_events: &mut [TaggedEvent]);
/// Retrieves module launch argument string.
///
/// This is an arbitrary string argument that can be passed in to the module from an Ark
/// module link.
///
/// note: This should really been called `launch_argument_string` (without the `get_`) prefix, should fix for a future breaking version
#[deprecated_infallible]
pub fn get_launch_argument_string() -> String;
/// In multiplayer mode, broadcasts a message to all the players for the specified duration
/// (in ms).
pub fn broadcast_message(msg: &str, duration: f32);
/// Request the virtual keyboard to be shown or not for the specified player
///
/// - show is expected to be a bool, so either 0 or 1
pub fn show_virtual_keyboard(player_id: PlayerIdRepr, show: u32);
/// Starts detaching a player to another instance of the applet, with the given arguments.
///
/// Async operation: returns a handle that must be polled with `detach_player_is_ready`,
/// then confirmed with `detach_player_confirm`.
#[deprecated(note = "Use updated detach_players_to_applet2 instead")]
pub fn detach_player_to_applet(player_id: PlayerIdRepr, args: &str) -> DetachPlayerHandle;
/// Starts detaching a list of players to another instance of the applet, with the given target, bubble and arguments.
/// The available targets are described by `DetachPlayerTarget`. The bubble string will be
/// provided to the matchmaker along with the arguments. A bubble can be seen as a filter for the matchmaker.
/// For example if I'm looking for a specific world and provide a bubble string the matchmaker will only look at servers
/// that are tagged with the same bubble string and matchmake within those.
/// Async operation: returns a handle that must be polled with `detach_player_is_ready`,
/// then confirmed with `detach_player_confirm`.
pub fn detach_player_to_applet2(
player_ids: &[PlayerIdRepr],
target: u32,
args: &str,
bubble: &str,
) -> FFIResult<DetachPlayerHandle>;
/// Queries if a request of detaching the players is ready for consumption.
///
/// If it is not ready, it must be polled again in a subsequent simulation tick.
/// If it is ready, then the applet's code can perform last minute serializations, and the
/// handle can be consumed with `detach_player_confirm`.
///
/// # Errors
///
/// Can return an error if something went wrong during the detachment process. In that
/// case, the handle is invalidated and the caller may decide to retry or not.
pub fn detach_player_is_ready(handle: DetachPlayerHandle) -> FFIResult<bool>;
/// Confirms that the players can be teleported to another applet instance represented by the
/// handle.
///
/// Prerequisite: this implies that a call to `detach_player_is_ready` with this handle has
/// returned true. If that's not the case, this function may panic.
///
/// Calling this will invalidate the handle, so subsequent calls to `detach_player_confirm`
/// or `detach_player_is_ready` will fail.
pub fn detach_player_confirm(handle: DetachPlayerHandle);
/// When a detach player requests causes a hot-reload, indicate that the hot-reload is done
/// and that the applet is in a ready state for further use.
///
/// This will panic if there was not hot-reload happening. As such it should only be called
/// after the hot-reload entrypoint has been called *and* the applet is done performing the
/// hot-reloading steps. It should be called only once per hot-reload request, and it will
/// panic if it's called multiple times for the same hot-reload request.
pub fn notify_finished_hot_reload(is_success: u32);
/// Returns a session ID that links all applet runs that have been detached from the same
/// root.
pub fn get_session_id() -> SessionId;
}
}
pub use applet::*;