Skip to main content

uika_runtime/
world.rs

1// World-level gameplay template function wrappers (raw handle versions).
2// Type-safe wrappers live in uika-bindings/src/manual/world_ext.rs.
3
4use uika_ffi::{UClassHandle, UObjectHandle};
5
6use crate::api::api;
7use crate::error::{check_ffi, UikaError, UikaResult};
8
9/// Spawn an actor in the world.
10///
11/// - `world`: the UWorld handle
12/// - `class`: the UClass of the actor to spawn
13/// - `transform_buf`: raw bytes of an FTransform struct
14/// - `owner`: optional owning actor (null handle for none)
15pub fn spawn_actor_raw(
16    world: UObjectHandle,
17    class: UClassHandle,
18    transform_buf: &[u8],
19    owner: UObjectHandle,
20) -> UikaResult<UObjectHandle> {
21    let result = unsafe {
22        ((*api().world).spawn_actor)(
23            world,
24            class,
25            transform_buf.as_ptr(),
26            transform_buf.len() as u32,
27            owner,
28        )
29    };
30    if result.0.is_null() {
31        Err(UikaError::InvalidOperation("spawn_actor returned null".into()))
32    } else {
33        Ok(result)
34    }
35}
36
37/// Find a UObject by class and path (already loaded objects only).
38pub fn find_object_raw(class: UClassHandle, path: &str) -> UikaResult<UObjectHandle> {
39    let result = unsafe {
40        ((*api().world).find_object)(class, path.as_ptr(), path.len() as u32)
41    };
42    if result.0.is_null() {
43        Err(UikaError::InvalidOperation(format!("find_object: not found: {path}")))
44    } else {
45        Ok(result)
46    }
47}
48
49/// Load a UObject by class and path (triggers load if not already loaded).
50pub fn load_object_raw(class: UClassHandle, path: &str) -> UikaResult<UObjectHandle> {
51    let result = unsafe {
52        ((*api().world).load_object)(class, path.as_ptr(), path.len() as u32)
53    };
54    if result.0.is_null() {
55        Err(UikaError::InvalidOperation(format!("load_object: failed to load: {path}")))
56    } else {
57        Ok(result)
58    }
59}
60
61/// Create a new UObject of the given class.
62///
63/// - `outer`: the outer object (null handle = transient package)
64/// - `class`: the UClass to instantiate
65pub fn new_object_raw(outer: UObjectHandle, class: UClassHandle) -> UikaResult<UObjectHandle> {
66    let result = unsafe { ((*api().world).new_object)(outer, class) };
67    if result.0.is_null() {
68        Err(UikaError::InvalidOperation("new_object returned null".into()))
69    } else {
70        Ok(result)
71    }
72}
73
74/// Spawn an actor with deferred construction (BeginPlay not yet called).
75///
76/// The returned actor can be configured before calling `finish_spawning_raw`.
77/// `collision_method` maps to `ESpawnActorCollisionHandlingMethod` (0..4).
78pub fn spawn_actor_deferred_raw(
79    world: UObjectHandle,
80    class: UClassHandle,
81    transform_buf: &[u8],
82    owner: UObjectHandle,
83    instigator: UObjectHandle,
84    collision_method: u8,
85) -> UikaResult<UObjectHandle> {
86    let result = unsafe {
87        ((*api().world).spawn_actor_deferred)(
88            world,
89            class,
90            transform_buf.as_ptr(),
91            transform_buf.len() as u32,
92            owner,
93            instigator,
94            collision_method,
95        )
96    };
97    if result.0.is_null() {
98        Err(UikaError::InvalidOperation("spawn_actor_deferred returned null".into()))
99    } else {
100        Ok(result)
101    }
102}
103
104/// Finish spawning a deferred actor (triggers BeginPlay).
105pub fn finish_spawning_raw(
106    actor: UObjectHandle,
107    transform_buf: &[u8],
108) -> UikaResult<()> {
109    check_ffi(unsafe {
110        ((*api().world).finish_spawning)(
111            actor,
112            transform_buf.as_ptr(),
113            transform_buf.len() as u32,
114        )
115    })
116}
117
118/// Get the UWorld from an actor handle.
119pub fn get_world_raw(actor: UObjectHandle) -> UikaResult<UObjectHandle> {
120    let result = unsafe { ((*api().world).get_world)(actor) };
121    if result.0.is_null() {
122        Err(UikaError::InvalidOperation("get_world returned null".into()))
123    } else {
124        Ok(result)
125    }
126}
127
128/// Get all actors of a given class in the world.
129pub fn get_all_actors_of_class_raw(
130    world: UObjectHandle,
131    class: UClassHandle,
132) -> UikaResult<Vec<UObjectHandle>> {
133    // First call with zero capacity to get the count.
134    let mut count: u32 = 0;
135    check_ffi(unsafe {
136        ((*api().world).get_all_actors_of_class)(
137            world,
138            class,
139            std::ptr::null_mut(),
140            0,
141            &mut count,
142        )
143    })?;
144
145    if count == 0 {
146        return Ok(Vec::new());
147    }
148
149    // Second call with allocated buffer.
150    let mut buf = vec![UObjectHandle(std::ptr::null_mut()); count as usize];
151    let mut actual_count: u32 = 0;
152    check_ffi(unsafe {
153        ((*api().world).get_all_actors_of_class)(
154            world,
155            class,
156            buf.as_mut_ptr(),
157            count,
158            &mut actual_count,
159        )
160    })?;
161
162    buf.truncate(actual_count as usize);
163    Ok(buf)
164}