kitgit

tirbofish/dropbear · commit

f5d11565e2f0616fe8774d0292fa19b119bb24de

worked on the NativeEngine, i think im getting somewhere (hopefully). all i remember is that eucalyptus-core is now a cdylib and a rlib, and a world is passed as a pointer. thats all i remember lol.

Unverified

tk <4tkbytes@pm.me> · 2025-10-08 15:23

view full diff

diff --git a/.gitignore b/.gitignore
index 1f67367..ba1ab3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,4 +51,8 @@ Cargo.lock
 **/*.rs.bk
 *.pdb
 
-.idea/
\ No newline at end of file
+.idea/
+
+src/*.dll
+src/*.dylib
+src/*.so
\ No newline at end of file
diff --git a/.run/generateJniHeaders.run.xml b/.run/generateJniHeaders.run.xml
new file mode 100644
index 0000000..acc9512
--- /dev/null
+++ b/.run/generateJniHeaders.run.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="generateJniHeaders" type="GradleRunConfiguration" factoryName="Gradle">
+    <ExternalSystemSettings>
+      <option name="executionName" />
+      <option name="externalProjectPath" value="$PROJECT_DIR$" />
+      <option name="externalSystemIdString" value="GRADLE" />
+      <option name="scriptParameters" value="" />
+      <option name="taskDescriptions">
+        <list />
+      </option>
+      <option name="taskNames">
+        <list>
+          <option value="generateJniHeaders" />
+        </list>
+      </option>
+      <option name="vmOptions" />
+    </ExternalSystemSettings>
+    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
+    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
+    <DebugAllEnabled>false</DebugAllEnabled>
+    <RunAsTest>false</RunAsTest>
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/README.md b/README.md
index 90af976..8835271 100644
--- a/README.md
+++ b/README.md
@@ -26,18 +26,17 @@ With Unix systems (macOS not tested), you will have to download a couple depende
 
 <!-- If you have a macOS system, please create a PR and add your own implementation. I know you need to use brew, but I don't know what dependencies to install.  -->
 
-### Dependencies
 
 ```bash
 # ubuntu
 sudo apt install libudev-dev pkg-config libssl-dev clang cmake meson assimp-utils
 
 # i use arch btw
-sudo pacman -Syu base-devel systemd pkgconf openssl clang cmake meson assimp
+sudo pacman -Syu base-devel systemd pkgconf openssl clang cmake meson assimp jdk21-openjdk
 
 ```
 
-### Engine Build
+Then run this to build the project
 
 ```bash
 git clone git@github.com:4tkbytes/dropbear
@@ -45,6 +44,7 @@ cd dropbear
 
 # this will build all the projects in the workspace
 cargo build
+./gradlew build
 ```
 
 [//]: # (# ensure submodules are checked-out)
@@ -64,7 +64,14 @@ If you do not want to build it locally, you are able to download the latest acti
 Despite the dropbear-engine (and other components) being made in Rust, the editor has chosen the scripting language of choice to be `Kotlin`
 because of previous experience and that Kotlin is more multiplatform than Swift. 
 
-Java is possible (as the JVM runs the script), however it is not officially supported and Kotlin is recommended. . 
+The dropbear engine uses Kotlin Multiplatform, which allows the cooked up product to be compatible with all platforms 
+KMP can support, which includes mobile, WASM and desktop. Because the editor is only available on desktop, the JVM is 
+used to evaluate the scripts as it allows for hot-reloading (not made yet). 
+
+It is recommended to use IntelliJ IDEA with the Rust plugin to help contribute to the engine. If you are a normal joe, 
+then just use the standard IntelliJ IDEA. 
+
+## Documentation
 
 API documentation and articles are available at (todo)
 
@@ -77,11 +84,11 @@ API documentation and articles are available at (todo)
 
 <sup>1</sup> Will never be implemented; not intended for that platform.
 
-<sup>2</sup>  Made some progress on implementing, but currently a WIP.
+<sup>2</sup> Made some progress on implementing, but currently a WIP.
 
 ## Contributions
 
-Yeah yeah, go ahead and contribute. Make sure it works, and its not spam, and any tests pass.
+Yeah, yeah, go ahead and contribute. Make sure it works, and its not spam, and any tests pass.
 
 # Licensing
 
diff --git a/build.gradle.kts b/build.gradle.kts
index 065e082..3144994 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -29,12 +29,11 @@ kotlin {
         compilations.getByName("main") {
             cinterops {
                 val dropbear by creating {
-                    defFile(project.file("src/nativeInterop/cinterop/dropbear.def"))
-                    includeDirs.headerFilterOnly(project.file("src/nativeInterop/cinterop"))
+                    defFile(project.file("src/dropbear.def"))
+                    includeDirs.headerFilterOnly(project.file("headers"))
                 }
             }
         }
-
         binaries {
             sharedLib {
                 baseName = "dropbear"
@@ -43,6 +42,11 @@ kotlin {
     }
 
     sourceSets {
+        commonMain {
+            dependencies {
+                implementation("co.touchlab:kermit:2.0.4")
+            }
+        }
         nativeMain {
             dependencies {
                 implementation(libs.kotlinxSerializationJson)
diff --git a/eucalyptus-core/Cargo.toml b/eucalyptus-core/Cargo.toml
index 18ed28c..e7f2f9d 100644
--- a/eucalyptus-core/Cargo.toml
+++ b/eucalyptus-core/Cargo.toml
@@ -6,6 +6,9 @@ license-file.workspace = true
 repository.workspace = true
 readme = "README.md"
 
+[lib]
+crate-type = ["rlib", "cdylib"]
+
 [dependencies]
 anyhow.workspace = true
 bincode.workspace = true
diff --git a/eucalyptus-core/README.md b/eucalyptus-core/README.md
index 735d9b7..e00bcec 100644
--- a/eucalyptus-core/README.md
+++ b/eucalyptus-core/README.md
@@ -2,4 +2,6 @@
 
 The core libraries of the eucalyptus editor. Great for embedding into `redback-runtime` and `eucalyptus-editor` as one big change instead of a bunch of features.
 
-This is a library, so if tools are wished to be made, this is the perfect library for you.
\ No newline at end of file
+This is a library, so if tools are wished to be made, this is the perfect library for you.
+
+it also produces a shared library for Kotlin/Native and the JVM :)
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting.rs b/eucalyptus-core/src/scripting.rs
index 8fd251d..970b0aa 100644
--- a/eucalyptus-core/src/scripting.rs
+++ b/eucalyptus-core/src/scripting.rs
@@ -12,83 +12,12 @@ use crate::ptr::SafePointer;
 
 pub const TEMPLATE_SCRIPT: &str = include_str!("../../resources/scripting/kotlin/Template.kt");
 
-static CONTEXT: LazyLock<DropbearScriptingAPIContext> = LazyLock::new(|| DropbearScriptingAPIContext::new());
-
-pub struct DropbearScriptingAPIContext {
-    pub current_entity: Option<Entity>,
-    // fyi: im pretty sure this is safe because I can just null with [`Option::None`]
-    current_world: Option<SafePointer<World>>,
-    pub current_input: Option<InputState>,
-    pub persistent_data: HashMap<String, Value>,
-    pub frame_data: HashMap<String, Value>,
-}
-
-impl Default for DropbearScriptingAPIContext {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl DropbearScriptingAPIContext {
-    pub fn new() -> Self {
-        Self {
-            current_entity: None,
-            current_world: None,
-            current_input: None,
-            persistent_data: HashMap::new(),
-            frame_data: HashMap::new(),
-        }
-    }
-
-    pub fn set_context(&mut self, entity: Entity, world: &mut World, input: &InputState) {
-        self.current_entity = Some(entity);
-        self.current_world = Some(SafePointer::new(world));
-        self.current_input = Some(input.clone());
-    }
-
-    pub fn clear_context(&mut self) {
-        self.current_entity = None;
-        self.current_world = None;
-        self.current_input = None;
-        self.frame_data.clear();
-    }
-
-    pub fn get_current_entity(&self) -> Option<Entity> {
-        self.current_entity
-    }
-
-    pub fn get_input(&self) -> Option<&InputState> {
-        self.current_input.as_ref()
-    }
-
-    pub fn set_persistent_data(&mut self, key: String, value: Value) {
-        self.persistent_data.insert(key, value);
-    }
-
-    pub fn get_persistent_data(&self, key: &str) -> Option<&Value> {
-        self.persistent_data.get(key)
-    }
-
-    pub fn set_frame_data(&mut self, key: String, value: Value) {
-        self.frame_data.insert(key, value);
-    }
-
-    pub fn get_frame_data(&self, key: &str) -> Option<&Value> {
-        self.frame_data.get(key)
-    }
-
-    pub fn cleanup_entity_data(&mut self, entity: Entity) {
-        let entity_prefix = format!("entity_{:?}_", entity);
-        self.persistent_data
-            .retain(|k, _| !k.starts_with(&entity_prefix));
-    }
-}
-
 pub struct ScriptManager;
 
 impl ScriptManager {
     pub fn new() -> anyhow::Result<Self> {
-        Err(anyhow::anyhow!("it aint ready yet bozo"))
+        Ok(Self)
+        // Err(anyhow::anyhow!("it aint ready yet bozo"))
     }
 
     pub fn load_script(
@@ -101,7 +30,7 @@ impl ScriptManager {
     pub fn init_entity_script(
         &mut self,
         entity_id: hecs::Entity,
-        tags: &Vec<String>,
+        tags: Vec<String>,
         world: &mut World,
         input_state: &InputState,
     ) -> anyhow::Result<()> {
@@ -112,7 +41,7 @@ impl ScriptManager {
     pub fn update_entity_script(
         &mut self,
         entity_id: hecs::Entity,
-        tags: &Vec<String>,
+        tags: Vec<String>,
         world: &mut World,
         input_state: &InputState,
         dt: f32,
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
index ee354f8..4997a86 100644
--- a/eucalyptus-core/src/scripting/jni.rs
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -1 +1,2 @@
-//! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate
\ No newline at end of file
+//! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate
+
diff --git a/eucalyptus-core/src/scripting/kmp.rs b/eucalyptus-core/src/scripting/kmp.rs
index 655b546..943f2af 100644
--- a/eucalyptus-core/src/scripting/kmp.rs
+++ b/eucalyptus-core/src/scripting/kmp.rs
@@ -1 +1,28 @@
-//! Deals with Kotlin/Native library loading for different platforms.
\ No newline at end of file
+//! Deals with Kotlin/Native library loading for different platforms.
+
+use std::ffi::c_char;
+use hecs::World;
+use dropbear_engine::entity::AdoptedEntity;
+
+#[unsafe(no_mangle)]
+pub extern "C" fn dropbear_get_entity(label: *const c_char, world_ptr: *const World) -> usize {
+    if world_ptr.is_null() {
+        log::debug!("World pointer is null");
+        return 0;
+    }
+
+    unsafe {
+        let world = &*world_ptr;
+
+        for (id, entity) in world.query::<&AdoptedEntity>().iter() {
+            if let Ok(label) = std::ffi::CStr::from_ptr(label).to_str()
+                && entity.model.label == label
+            {
+                log::debug!("Found entity with label: {}", label);
+                return id.id() as usize;
+            }
+        }
+        log::warn!("Entity with label: {} not found", std::ffi::CStr::from_ptr(label).to_str().unwrap());
+        0
+    }
+}
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index 3714202..8e8301a 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -48,7 +48,7 @@ use dropbear_engine::model::{ModelId};
 
 pub struct Editor {
     scene_command: SceneCommand,
-    pub world: World,
+    pub world: Box<World>,
     dock_state: DockState<EditorTab>,
     texture_id: Option<egui::TextureId>,
     size: Extent3d,
@@ -149,7 +149,7 @@ impl Editor {
             is_viewport_focused: false,
             // is_cursor_locked: false,
             window: None,
-            world: World::new(),
+            world: Box::new(World::new()),
             show_new_project: false,
             project_name: String::new(),
             project_path: Arc::new(Mutex::new(None)),
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index ef19513..492ebf6 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -52,7 +52,7 @@ impl Scene for Editor {
         if let Some(mut receiver) = self.world_receiver.take() {
             self.show_project_loading_window(&graphics.shared.get_egui_context());
             if let Ok(loaded_world) = receiver.try_recv() {
-                self.world = loaded_world;
+                self.world = Box::new(loaded_world);
                 self.is_world_loaded.mark_project_loaded();
                 
                 if let Some(dock_state_shared) = &self.dock_state_shared &&
@@ -121,7 +121,7 @@ impl Scene for Editor {
             for (entity_id, script_name) in script_entities {
                 if let Err(e) = self.script_manager.update_entity_script(
                     entity_id,
-                    &script_name,
+                    script_name.clone(),
                     &mut self.world,
                     &self.input_state,
                     dt,
@@ -175,46 +175,6 @@ impl Scene for Editor {
             }
         }
 
-        // let camera_follow_data: Vec<(Entity, String, glam::Vec3)> = {
-        //     self.world
-        //         .query::<(&Camera, &CameraComponent)>()
-        //         .iter()
-        //         .filter_map(|(entity_id, (_, _))| {
-        //             follow_target.map(|target| {
-        //                 (
-        //                     entity_id,
-        //                     target.follow_target.clone(),
-        //                     target.offset.as_vec3()
-        //                 )
-        //             })
-        //         })
-        //         .collect()
-        // };
-
-
-        // for (camera_entity, target_label, offset) in camera_follow_data {
-        //     let target_position = {
-        //         self.world
-        //             .query::<(&AdoptedEntity, &Transform)>()
-        //             .iter()
-        //             .find_map(|(_, (adopted, transform))| {
-        //                 if adopted.model.label == target_label {
-        //                     Some(transform.position)
-        //                 } else {
-        //                     None
-        //                 }
-        //             })
-        //     };
-        // 
-        // 
-        //     if let Some(pos) = target_position 
-        //         && let Ok(mut query) = self.world.query_one::<&mut Camera>(camera_entity)
-        //         && let Some(camera) = query.get() {
-        //             camera.eye = pos + offset.as_dvec3();
-        //             camera.target = pos;
-        //         }
-        // }
-
         {
             for (_entity_id, (camera, component)) in self.world
                 .query::<(&mut Camera, &mut CameraComponent)>()
diff --git a/eucalyptus-editor/src/signal.rs b/eucalyptus-editor/src/signal.rs
index 8963c1f..198810b 100644
--- a/eucalyptus-editor/src/signal.rs
+++ b/eucalyptus-editor/src/signal.rs
@@ -24,6 +24,7 @@ impl SignalController for Editor {
 
         match &self.signal {
             Signal::None => {
+                // returns absolutely nothing because no signal is set. 
                 Ok::<(), anyhow::Error>(())
             }
             Signal::Copy(_) => {Ok(())}
@@ -92,210 +93,6 @@ impl SignalController for Editor {
                 self.signal = Signal::None;
                 Ok(())
             }
-            // Signal::ScriptAction(action) => match action {
-            //     ScriptAction::AttachScript {
-            //         script_path,
-            //         script_name,
-            //     } => {
-            //         if let Some(selected_entity) = self.selected_entity {
-            //             match scripting::move_script_to_src(script_path) {
-            //                 Ok(moved_path) => {
-            //                     let new_script = ScriptComponent {
-            //                         name: script_name.clone(),
-            //                         path: moved_path.clone(),
-            //                     };
-            // 
-            //                     let replaced = {
-            //                         if let Ok(mut sc) = self.world.get::<&mut ScriptComponent>(selected_entity) {
-            //                             sc.name = new_script.name.clone();
-            //                             sc.path = new_script.path.clone();
-            //                             true
-            //                         } else {
-            //                             false
-            //                         }
-            //                     };
-            // 
-            //                     if !replaced {
-            //                         match scripting::attach_script_to_entity(
-            //                             &mut self.world,
-            //                             selected_entity,
-            //                             new_script.clone(),
-            //                         ) {
-            //                             Ok(_) => {
-            //                             }
-            //                             Err(e) => {
-            //                                 self.signal = Signal::None;
-            //                                 fatal!("Failed to attach script to entity {:?}: {}",
-            //                                     selected_entity,
-            //                                     e);
-            //                                 return Err(anyhow::anyhow!(e));
-            //                             }
-            //                         }
-            //                     }
-            // 
-            //                     {
-            //                         if let Err(e) = scripting::convert_entity_to_group(
-            //                             &mut self.world,
-            //                             selected_entity,
-            //                         ) {
-            //                             log::warn!("convert_entity_to_group failed (non-fatal): {}", e);
-            //                         }
-            //                     }
-            // 
-            //                     success!(
-            //                         "{} script '{}' at {} to entity {:?}",
-            //                         if replaced { "Reattached" } else { "Attached" },
-            //                         script_name,
-            //                         moved_path.display(),
-            //                         selected_entity
-            //                     );
-            //                 }
-            //                 Err(e) => {
-            //                     fatal!("Move failed: {}", e);
-            //                 }
-            //             }
-            //         } else {
-            //             fatal!("AttachScript requested but no entity is selected");
-            //         }
-            // 
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            //     ScriptAction::CreateAndAttachScript {
-            //         script_path,
-            //         script_name,
-            //     } => {
-            //         if let Some(selected_entity) = self.selected_entity {
-            //             let new_script = ScriptComponent {
-            //                 name: script_name.clone(),
-            //                 path: script_path.clone(),
-            //             };
-            // 
-            //             let replaced = {
-            //                 if let Ok(mut sc) = self.world.get::<&mut ScriptComponent>(selected_entity) {
-            //                     sc.name = new_script.name.clone();
-            //                     sc.path = new_script.path.clone();
-            //                     true
-            //                 } else {
-            //                     false
-            //                 }
-            //             };
-            // 
-            //             if !replaced {
-            //                 match scripting::attach_script_to_entity(
-            //                     &mut self.world,
-            //                     selected_entity,
-            //                     new_script.clone(),
-            //                 ) {
-            //                     Ok(_) => {
-            //                     }
-            //                     Err(e) => {
-            //                         self.signal = Signal::None;
-            //                         fatal!("Failed to attach new script: {}", e);
-            //                         return Err(anyhow::anyhow!(e));
-            //                     }
-            //                 }
-            //             }
-            // 
-            //             {
-            //                 if let Err(e) = scripting::convert_entity_to_group(
-            //                     &mut self.world,
-            //                     selected_entity,
-            //                 ) {
-            //                     log::warn!("convert_entity_to_group failed (non-fatal): {}", e);
-            //                 }
-            //             }
-            // 
-            //             success!(
-            //                 "{} new script '{}' at {} to entity {:?}",
-            //                 if replaced { "Replaced" } else { "Attached" },
-            //                 script_name,
-            //                 script_path.display(),
-            //                 selected_entity
-            //             );
-            //         } else {
-            //             warn_without_console!("No selected entity to attach new script");
-            //             log::warn!("CreateAndAttachScript requested but no entity is selected");
-            //         }
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            //     ScriptAction::RemoveScript => {
-            //         if let Some(selected_entity) = self.selected_entity {
-            //             let mut success = false;
-            //             let mut comp = ScriptComponent::default();
-            //             {
-            //                 if let Ok(script) = self.world
-            //                     .remove_one::<ScriptComponent>(selected_entity)
-            //                 {
-            //                     success!("Removed script from entity {:?}", selected_entity);
-            //                     success = true;
-            //                     comp = script.clone();
-            //                 } else {
-            //                     warn!("No script component found on entity {:?}", selected_entity);
-            //                 }
-            //                 match self.world.insert_one(selected_entity, ScriptComponent::default()) {
-            //                     Ok(_) => {
-            //                         log::debug!("Inserted default script component");
-            //                     }
-            //                     Err(e) => {
-            //                         log::warn!("No such entity is available. Additional info: {}", e);
-            //                     }
-            //                 }
-            //             }
-            // 
-            //             if success {
-            //                 if let Err(e) = scripting::convert_entity_to_group(
-            //                     &mut self.world,
-            //                     selected_entity,
-            //                 ) {
-            //                     log::warn!("convert_entity_to_group failed (non-fatal): {}", e);
-            //                 }
-            //                 log::debug!("Pushing remove component to undo stack");
-            //                 UndoableAction::push_to_undo(
-            //                     &mut self.undo_stack,
-            //                     UndoableAction::RemoveComponent(
-            //                         selected_entity,
-            //                         Box::new(ComponentType::Script(comp)),
-            //                     ),
-            //                 );
-            //             }
-            //         } else {
-            //             warn!("No entity selected to remove script from");
-            //         }
-            // 
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            //     ScriptAction::EditScript => {
-            //         if let Some(selected_entity) = self.selected_entity {
-            //             let script_opt = {
-            //                 if let Ok(mut q) = self.world.query_one::<&ScriptComponent>(selected_entity) {
-            //                     q.get().cloned()
-            //                 } else {
-            //                     None
-            //                 }
-            //             };
-            // 
-            //             if let Some(script) = script_opt {
-            //                 match open::that(script.path.clone()) {
-            //                     Ok(()) => {
-            //                         success!("Opened {}", script.name)
-            //                     }
-            //                     Err(e) => {
-            //                         warn!("Error while opening {}: {}", script.name, e);
-            //                     }
-            //                 }
-            //             } else {
-            //                 warn!("No script component found on entity {:?}", selected_entity);
-            //             }
-            //         } else {
-            //             warn!("No entity selected to edit script");
-            //         }
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            // },
             Signal::Play => {
                 if matches!(self.editor_state, EditorState::Playing) {
                     fatal!("Unable to play: already in playing mode");
@@ -339,7 +136,7 @@ impl SignalController for Editor {
                             Ok(_) => {
                                 if let Err(e) = self.script_manager.init_entity_script(
                                     entity_id,
-                                    &script.tags,
+                                    script.tags.clone(),
                                     &mut self.world,
                                     &self.input_state,
                                 ) {
@@ -392,28 +189,6 @@ impl SignalController for Editor {
                 self.signal = Signal::None;
                 Ok(())
             }
-            // Signal::CameraAction(action) => match action {
-            //     CameraAction::SetPlayerTarget { .. } => {
-            //         log::warn!("Deprecated: CameraAction::SetPlayerTarget");
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            //     CameraAction::ClearPlayerTarget => {
-            //         log::warn!("Deprecated: CameraAction::ClearPlayerTarget");
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            //     CameraAction::SetCurrentPositionAsOffset(_) => {
-            //         // if let Ok((camera, target)) = self.world.query_one_mut::<(&Camera, &mut CameraFollowTarget)>(*entity) {
-            //         //     target.offset = camera.target
-            //         // } else {
-            //         //     warn!("Unable to query camera to set current camera position to offset");
-            //         // }
-            //         log::warn!("Deprecated: CameraAction::SetCurrentPositionAsOffset");
-            //         self.signal = Signal::None;
-            //         Ok(())
-            //     }
-            // },
             Signal::AddComponent(entity, e_type) => {
                 match e_type {
                     EntityType::Entity => {
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/headers/dropbear.h b/headers/dropbear.h
new file mode 100644
index 0000000..75565a4
--- /dev/null
+++ b/headers/dropbear.h
@@ -0,0 +1,23 @@
+#ifndef DROPBEAR_H
+#define DROPBEAR_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct World World; // opaque pointer
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// ===========================================
+
+// returns 0 on success, non-zero on failure
+int dropbear_get_entity(const char* label, const World* world_ptr, uint64_t* out_entity);
+
+// ===========================================
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+#endif // DROPBEAR_H
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
index 9a6929c..502bc9d 100644
--- a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
@@ -1,24 +1,21 @@
 package com.dropbear
 
 import com.dropbear.ffi.NativeEngine
+import getProjectScriptMetadata
 
 class DropbearEngine(val native: NativeEngine) {
-    /**
-     * Fetches an entity based on its label in the editor.
-     *
-     * If there is no entity under that label, it will return a
-     * `null` in the form of a [Result]
-     */
+    private val globalScripts = mutableListOf<System>()
+
+    fun init() {
+        val scriptRegistration = getProjectScriptMetadata()
+    }
+
     fun getEntity(label: String): Result<EntityRef> {
         val entityId = native.getEntity(label)
         return (when (entityId) {
             null -> {
                 Result.failure(Exception("JNI returned null"))
             }
-            -1L -> {
-                // -1L means that query couldn't find entity with such a label
-                Result.failure(Exception("Entity with id $label not found"))
-            }
             else -> {
                 Result.success(EntityRef(EntityId(entityId)))
             }
diff --git a/src/commonMain/kotlin/com/dropbear/EntityId.kt b/src/commonMain/kotlin/com/dropbear/EntityId.kt
index 8733db0..b043b58 100644
--- a/src/commonMain/kotlin/com/dropbear/EntityId.kt
+++ b/src/commonMain/kotlin/com/dropbear/EntityId.kt
@@ -1,3 +1,3 @@
 package com.dropbear
 
-data class EntityId(val id: Long)
+data class EntityId(val id: ULong)
diff --git a/src/commonMain/kotlin/com/dropbear/EntityRef.kt b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
index 022be70..b49852f 100644
--- a/src/commonMain/kotlin/com/dropbear/EntityRef.kt
+++ b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
@@ -17,7 +17,7 @@ import com.dropbear.math.Vector3D
  * To edit any values part of the entity, take a look at the functions provided
  * by [EntityRef], which will require a reference to [DropbearEngine] to push the commands.
  */
-class EntityRef(val id: EntityId = EntityId(-1)) {
+class EntityRef(val id: EntityId = EntityId(0u)) {
     /**
      * Sets the position of the entity by a Vector
      */
diff --git a/src/commonMain/kotlin/com/dropbear/Example.kt b/src/commonMain/kotlin/com/dropbear/Example.kt
index e6a9d7d..6443e59 100644
--- a/src/commonMain/kotlin/com/dropbear/Example.kt
+++ b/src/commonMain/kotlin/com/dropbear/Example.kt
@@ -9,7 +9,15 @@ fun playerMovement(engine: DropbearEngine, entityId: EntityId, deltaTime: Double
 }
 
 class Player: System {
-    override fun update(engine: DropbearEngine, entityId: EntityId, deltaTime: Float) {
+    override fun load(engine: DropbearEngine) {
+        TODO("Not yet implemented")
+    }
+
+    override fun update(engine: DropbearEngine, deltaTime: Float) {
+        TODO("Not yet implemented")
+    }
+
+    override fun destroy(engine: DropbearEngine) {
         TODO("Not yet implemented")
     }
 }
@@ -23,4 +31,6 @@ class Metadata : ProjectScriptingMetadata {
             ),
         )
     }
-}
\ No newline at end of file
+}
+
+fun getProjectScriptMetadata(): ProjectScriptingMetadata = Metadata()
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/System.kt b/src/commonMain/kotlin/com/dropbear/System.kt
index 31eff7b..48bd854 100644
--- a/src/commonMain/kotlin/com/dropbear/System.kt
+++ b/src/commonMain/kotlin/com/dropbear/System.kt
@@ -1,5 +1,7 @@
 package com.dropbear
 
-fun interface System {
-    fun update(engine: DropbearEngine, current_entity: EntityId, deltaTime: Float)
+interface System {
+    fun load(engine: DropbearEngine)
+    fun update(engine: DropbearEngine, deltaTime: Float)
+    fun destroy(engine: DropbearEngine)
 }
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
index af3dfe2..28c8d95 100644
--- a/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
+++ b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
@@ -3,5 +3,6 @@ package com.dropbear.ffi
 import com.dropbear.EntityRef
 
 expect class NativeEngine {
-    fun getEntity(label: String): Long?
+    fun init(handle: ULong)
+    fun getEntity(label: String): ULong?
 }
\ No newline at end of file
diff --git a/src/dropbear.def b/src/dropbear.def
new file mode 100644
index 0000000..5a38050
--- /dev/null
+++ b/src/dropbear.def
@@ -0,0 +1,4 @@
+package: com.dropbear.ffi.generated
+headers: dropbear.h
+headerFilter: dropbear.h
+libraryPaths: src
\ No newline at end of file
diff --git a/src/jvmMain/java/com/dropbear/ffi/JNINative.java b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
index 0f8090b..2a2e28c 100644
--- a/src/jvmMain/java/com/dropbear/ffi/JNINative.java
+++ b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
@@ -1,5 +1,5 @@
 package com.dropbear.ffi;
 
 public class JNINative {
-    public native long getEntity(String label);
+    public native long getEntity(long handle, String label);
 }
diff --git a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
index fb3dba0..47486aa 100644
--- a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
+++ b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -1,9 +1,23 @@
 package com.dropbear.ffi
 
 actual class NativeEngine {
+    private var worldHandle: ULong = 0u
     private val jni = JNINative()
 
-    actual fun getEntity(label: String): Long? {
-        return jni.getEntity(label)
+    actual fun getEntity(label: String): ULong? {
+        val result = jni.getEntity(worldHandle.toLong(), label)
+        return if (result < 0) {
+            null
+        } else {
+            result.toULong()
+        }
+    }
+
+    actual fun init(handle: ULong) {
+        this.worldHandle = handle
+        if (this.worldHandle == 0uL) {
+            println("NativeEngine: Error - Invalid world handle received!")
+            return
+        }
     }
 }
\ No newline at end of file
diff --git a/src/nativeInterop/cinterop/dropbear.def b/src/nativeInterop/cinterop/dropbear.def
deleted file mode 100644
index a4b5340..0000000
--- a/src/nativeInterop/cinterop/dropbear.def
+++ /dev/null
@@ -1,7 +0,0 @@
-headers = dropbear.h
-headerFilter = dropbear.h
-package = com.dropbear.ffi.native
-
----
-
-long long dropbear_get_entity(const char* param);
diff --git a/src/nativeInterop/cinterop/dropbear.h b/src/nativeInterop/cinterop/dropbear.h
deleted file mode 100644
index 53eafe2..0000000
--- a/src/nativeInterop/cinterop/dropbear.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef DROPBEAR_H
-#define DROPBEAR_H
-
-#include <stdint.h>
-#include <stdbool.h>
-
-long long dropbear_get_entity(const char* param);
-
-#endif // DROPBEAR_H
\ No newline at end of file
diff --git a/src/nativeMain/kotlin/com/dropbear/ffi/Exports.kt b/src/nativeMain/kotlin/com/dropbear/ffi/Exports.kt
new file mode 100644
index 0000000..ff89e75
--- /dev/null
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/Exports.kt
@@ -0,0 +1,25 @@
+@file:OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class)
+
+package com.dropbear.ffi
+
+import co.touchlab.kermit.Logger
+import com.dropbear.DropbearEngine
+import kotlinx.cinterop.ExperimentalForeignApi
+import kotlin.experimental.ExperimentalNativeApi
+
+@CName("dropbear_entry")
+fun entry(worldHandle: ULong) {
+    Logger.i { "Starting kotlin scripting guest" }
+    val nativeEngine = NativeEngine()
+    nativeEngine.init(worldHandle)
+}
+
+@CName("dropbear_load")
+fun loadScriptByTag(tag: String?) {
+
+}
+
+@CName("dropbear_update")
+fun updateScriptByTag(tag: String?, deltaTime: Double) {
+
+}
diff --git a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
index fa4bea8..d2d366d 100644
--- a/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
@@ -1,13 +1,26 @@
-@file:OptIn(ExperimentalForeignApi::class)
+@file:OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class)
 @file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
 
 package com.dropbear.ffi
 
+import co.touchlab.kermit.Logger
 import kotlinx.cinterop.*
+import kotlin.experimental.ExperimentalNativeApi
 
 actual class NativeEngine {
-    actual fun getEntity(label: String): Long? {
-        return com.dropbear.ffi.native.dropbear_get_entity(label)
-//        return 0L
+    private var worldHandle: ULong = 0u
+
+    actual fun init(handle: ULong) {
+        this.worldHandle = handle
+        if (this.worldHandle == 0uL) {
+            Logger.i("NativeEngine: Error - Invalid world handle received!")
+            return
+        } else {
+            Logger.i("NativeEngine: Initialized with world handle: ${this.worldHandle}")
+        }
+    }
+
+    actual fun getEntity(label: String): ULong? {
+        TODO("Not yet implemented")
     }
 }
\ No newline at end of file