kitgit

tirbofish/dropbear · diff

d4725cc · tk

18 hours on this commit, and fuckass kotlin native doesnt work for shit. i hate this ugh

Unverified

diff --git a/build.gradle.kts b/build.gradle.kts
index cbfc7df..065e082 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,6 @@
 plugins {
-    kotlin("jvm") version "2.1.21"
+    alias(libs.plugins.kotlinMultiplatform)
+    alias(libs.plugins.kotlinxSerialization)
 }
 
 group = "com.dropbear"
@@ -9,21 +10,61 @@ repositories {
     mavenCentral()
 }
 
-dependencies {
-    testImplementation(kotlin("test"))
-    implementation(kotlin("test"))
-}
-
-tasks.test {
-    useJUnitPlatform()
-}
 kotlin {
-    jvmToolchain(21)
-}
+    jvm()
+
+    val hostOs = System.getProperty("os.name")
+    val isArm64 = System.getProperty("os.arch") == "aarch64"
+    val isMingwX64 = hostOs.startsWith("Windows")
+    val nativeTarget = when {
+        hostOs == "Mac OS X" && isArm64 -> macosArm64("nativeLib")
+        hostOs == "Mac OS X" && !isArm64 -> macosX64("nativeLib")
+        hostOs == "Linux" && isArm64 -> linuxArm64("nativeLib")
+        hostOs == "Linux" && !isArm64 -> linuxX64("nativeLib")
+        isMingwX64 -> mingwX64("nativeLib")
+        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
+    }
+
+    nativeTarget.apply {
+        compilations.getByName("main") {
+            cinterops {
+                val dropbear by creating {
+                    defFile(project.file("src/nativeInterop/cinterop/dropbear.def"))
+                    includeDirs.headerFilterOnly(project.file("src/nativeInterop/cinterop"))
+                }
+            }
+        }
 
-sourceSets {
-    main {
-        java.srcDirs("src/main/kotlin", "src/main/java")
+        binaries {
+            sharedLib {
+                baseName = "dropbear"
+            }
+        }
+    }
+
+    sourceSets {
+        nativeMain {
+            dependencies {
+                implementation(libs.kotlinxSerializationJson)
+            }
+        }
+
+        jvmMain {
+            kotlin.srcDirs("src/jvmMain/kotlin", "src/jvmMain/java")
+            dependencies {
+                
+            }
+        }
+    }
+
+    targets.all {
+        compilations.all {
+            compileTaskProvider.configure {
+                compilerOptions {
+                    freeCompilerArgs.add("-Xexpect-actual-classes")
+                }
+            }
+        }
     }
 }
 
@@ -31,14 +72,15 @@ tasks.register<JavaCompile>("generateJniHeaders") {
     val outputDir = layout.buildDirectory.dir("generated/jni-headers")
     options.headerOutputDirectory.set(outputDir.get().asFile)
 
+    destinationDirectory.set(layout.buildDirectory.dir("classes/java/jni"))
+
     classpath = files(
-        tasks.named("compileKotlin"),
-        tasks.named("compileJava")
+        tasks.named("compileKotlinJvm"),
     )
 
-    source = fileTree("src/main/java") {
+    source = fileTree("src/jvmMain/java") {
         include("**/*.java")
     }
 
-    dependsOn("compileJava", "compileKotlin")
-}
+    dependsOn("compileKotlinJvm")
+}
\ No newline at end of file
diff --git a/eucalyptus-core/Cargo.toml b/eucalyptus-core/Cargo.toml
index 202141c..18ed28c 100644
--- a/eucalyptus-core/Cargo.toml
+++ b/eucalyptus-core/Cargo.toml
@@ -26,8 +26,9 @@ winit.workspace = true
 tokio.workspace = true
 rayon.workspace = true
 jni.workspace = true
-lazy_static = "1.5.0"
 crossbeam-channel = "0.5.15"
+interprocess = "2.2"
+serde_json = "1.0.143"
 
 [features]
 editor = []
diff --git a/eucalyptus-core/build.rs b/eucalyptus-core/build.rs
index 8966f89..ec0208f 100644
--- a/eucalyptus-core/build.rs
+++ b/eucalyptus-core/build.rs
@@ -1,59 +1,56 @@
-use std::fs::{self, File};
-use std::io::Cursor;
-
 fn main() -> anyhow::Result<()> {
-    // todo: move this into the "setup" process
-    let repo_zip_url = "https://github.com/4tkbytes/dropbear/archive/refs/heads/main.zip";
-    let response = reqwest::blocking::get(repo_zip_url)
-        .map_err(|e| anyhow::anyhow!("Failed to download repo zip: {}", e))?
-        .bytes()
-        .map_err(|e| anyhow::anyhow!("Failed to read zip bytes: {}", e))?;
-
-    let reader = Cursor::new(response);
-    let mut zip = zip::ZipArchive::new(reader)
-        .map_err(|e| anyhow::anyhow!("Failed to read zip archive: {}", e))?;
-
-    let app_info = app_dirs2::AppInfo {
-        name: "Eucalyptus",
-        author: "4tkbytes",
-    };
-    let app_data_dir = app_dirs2::app_root(app_dirs2::AppDataType::UserData, &app_info)
-        .map_err(|e| anyhow::anyhow!("Could not determine app data directory: {}", e))?;
-
-    fs::create_dir_all(&app_data_dir)
-        .map_err(|e| anyhow::anyhow!("Failed to create app data directory: {}", e))?;
-
-    let resource_prefix = "dropbear-main/resources/";
-    let mut found_resource = false;
-    for i in 0..zip.len() {
-        let mut file = zip.by_index(i).unwrap();
-        let name = file.name();
-
-        if name.starts_with(resource_prefix) && !name.ends_with('/') {
-            found_resource = true;
-            let rel_path = &name[resource_prefix.len()..];
-            let rel_path = rel_path.strip_prefix('/').unwrap_or(rel_path);
-            let dest_path = app_data_dir.join(rel_path);
-
-            if let Some(parent) = dest_path.parent() {
-                fs::create_dir_all(parent)
-                    .map_err(|e| anyhow::anyhow!("Failed to create parent directory: {}", e))?;
-            }
-
-            println!("Copying {} to {:?}", name, dest_path);
-
-            let mut outfile = File::create(&dest_path)
-                .map_err(|e| anyhow::anyhow!("Failed to create file: {}", e))?;
-            std::io::copy(&mut file, &mut outfile)
-                .map_err(|e| anyhow::anyhow!("Failed to copy file: {}", e))?;
-        }
-    }
-
-    if !found_resource {
-        return Err(anyhow::anyhow!(
-            "No resources folder found in the github repository [4tkbytes/dropbear] :("
-        ));
-    }
+    // // todo: move this into the "setup" process
+    // let repo_zip_url = "https://github.com/4tkbytes/dropbear/archive/refs/heads/main.zip";
+    // let response = reqwest::blocking::get(repo_zip_url)
+    //     .map_err(|e| anyhow::anyhow!("Failed to download repo zip: {}", e))?
+    //     .bytes()
+    //     .map_err(|e| anyhow::anyhow!("Failed to read zip bytes: {}", e))?;
+    //
+    // let reader = Cursor::new(response);
+    // let mut zip = zip::ZipArchive::new(reader)
+    //     .map_err(|e| anyhow::anyhow!("Failed to read zip archive: {}", e))?;
+    //
+    // let app_info = app_dirs2::AppInfo {
+    //     name: "Eucalyptus",
+    //     author: "4tkbytes",
+    // };
+    // let app_data_dir = app_dirs2::app_root(app_dirs2::AppDataType::UserData, &app_info)
+    //     .map_err(|e| anyhow::anyhow!("Could not determine app data directory: {}", e))?;
+    //
+    // fs::create_dir_all(&app_data_dir)
+    //     .map_err(|e| anyhow::anyhow!("Failed to create app data directory: {}", e))?;
+    //
+    // let resource_prefix = "dropbear-main/resources/";
+    // let mut found_resource = false;
+    // for i in 0..zip.len() {
+    //     let mut file = zip.by_index(i).unwrap();
+    //     let name = file.name();
+    //
+    //     if name.starts_with(resource_prefix) && !name.ends_with('/') {
+    //         found_resource = true;
+    //         let rel_path = &name[resource_prefix.len()..];
+    //         let rel_path = rel_path.strip_prefix('/').unwrap_or(rel_path);
+    //         let dest_path = app_data_dir.join(rel_path);
+    //
+    //         if let Some(parent) = dest_path.parent() {
+    //             fs::create_dir_all(parent)
+    //                 .map_err(|e| anyhow::anyhow!("Failed to create parent directory: {}", e))?;
+    //         }
+    //
+    //         println!("Copying {} to {:?}", name, dest_path);
+    //
+    //         let mut outfile = File::create(&dest_path)
+    //             .map_err(|e| anyhow::anyhow!("Failed to create file: {}", e))?;
+    //         std::io::copy(&mut file, &mut outfile)
+    //             .map_err(|e| anyhow::anyhow!("Failed to copy file: {}", e))?;
+    //     }
+    // }
+    //
+    // if !found_resource {
+    //     return Err(anyhow::anyhow!(
+    //         "No resources folder found in the github repository [4tkbytes/dropbear] :("
+    //     ));
+    // }
 
     // fuck you windows :(
     #[cfg(target_os = "windows")]
diff --git a/eucalyptus-core/src/lib.rs b/eucalyptus-core/src/lib.rs
index 87e2061..c4907b1 100644
--- a/eucalyptus-core/src/lib.rs
+++ b/eucalyptus-core/src/lib.rs
@@ -5,4 +5,5 @@ pub mod logging;
 pub mod scripting;
 pub mod states;
 pub mod utils;
-pub mod spawn;
\ No newline at end of file
+pub mod spawn;
+mod ptr;
diff --git a/eucalyptus-core/src/ptr.rs b/eucalyptus-core/src/ptr.rs
new file mode 100644
index 0000000..c55c5c4
--- /dev/null
+++ b/eucalyptus-core/src/ptr.rs
@@ -0,0 +1,29 @@
+#![allow(dead_code)] // needed because some fields arent accessed
+use std::marker::PhantomData;
+
+/// A clonable Send/Sync pointer. Typically unsafe, but fuck it we ball.
+/// Anything to not deal with Mutex and RwLock amirite???
+#[derive(Clone)]
+pub struct SafePointer<T> {
+    ptr: *const T,
+    _marker: PhantomData<T>
+}
+
+unsafe impl<T> Send for SafePointer<T> where T: Send {}
+
+unsafe impl<T> Sync for SafePointer<T> where T: Sync {}
+
+impl<T> SafePointer<T> {
+    /// Creates a new safe pointer from an unsafe pointer
+    pub fn new(ptr: *const T) -> Self {
+        SafePointer {
+            ptr,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Accesses the [`SafePointer`] as an unsafe pointer
+    pub unsafe fn get(&self) -> *const T {
+        self.ptr
+    }
+}
diff --git a/eucalyptus-core/src/scripting.rs b/eucalyptus-core/src/scripting.rs
index 0475600..8fd251d 100644
--- a/eucalyptus-core/src/scripting.rs
+++ b/eucalyptus-core/src/scripting.rs
@@ -1,4 +1,5 @@
-mod java;
+mod kmp;
+mod jni;
 
 use crate::input::InputState;
 use crate::states::{EntityNode, PROJECT, SOURCE, ScriptComponent, Value};
@@ -6,15 +7,17 @@ use dropbear_engine::entity::{AdoptedEntity, Transform};
 use hecs::{Entity, World};
 use std::path::PathBuf;
 use std::{collections::HashMap, fs};
-use crate::scripting::java::JavaContext;
+use std::sync::LazyLock;
+use crate::ptr::SafePointer;
 
 pub const TEMPLATE_SCRIPT: &str = include_str!("../../resources/scripting/kotlin/Template.kt");
 
-#[derive(Clone)]
+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<*const World>,
+    current_world: Option<SafePointer<World>>,
     pub current_input: Option<InputState>,
     pub persistent_data: HashMap<String, Value>,
     pub frame_data: HashMap<String, Value>,
@@ -39,7 +42,7 @@ impl DropbearScriptingAPIContext {
 
     pub fn set_context(&mut self, entity: Entity, world: &mut World, input: &InputState) {
         self.current_entity = Some(entity);
-        self.current_world = Some(world as *mut World);
+        self.current_world = Some(SafePointer::new(world));
         self.current_input = Some(input.clone());
     }
 
@@ -81,70 +84,41 @@ impl DropbearScriptingAPIContext {
     }
 }
 
-/// A message from Kotlin that gets sent to Rust
-pub enum KotlinMessage {
-
-}
-
-/// A message from Rust that gets sent to Kotlin
-pub enum RustMessage {
-
-}
-
-pub struct ScriptManager {
-    script_context: DropbearScriptingAPIContext,
-    java: JavaContext,
-    from_kotlin: crossbeam_channel::Receiver<KotlinMessage>,
-    to_kotlin: crossbeam_channel::Sender<RustMessage>,
-}
+pub struct ScriptManager;
 
 impl ScriptManager {
     pub fn new() -> anyhow::Result<Self> {
-        // let lib_path: PathBuf = Self::look_for_potential_library()?;
-        // let library = unsafe { Library::new(lib_path.clone())? };
-
-        let result = Self {
-            java: JavaContext::new()?,
-            script_context: DropbearScriptingAPIContext::new(),
-        };
-
-        log::debug!("Initialised ScriptManager");
-        Ok(result)
+        Err(anyhow::anyhow!("it aint ready yet bozo"))
     }
 
     pub fn load_script(
         &mut self,
-        script_name: &String,
-        script_content: String,
-    ) -> anyhow::Result<String> {
+    ) -> anyhow::Result<()> {
         
-        log::debug!("Loaded library [{}]", script_name);
-        Ok(script_name.clone())
+        Ok(())
     }
 
     pub fn init_entity_script(
         &mut self,
         entity_id: hecs::Entity,
-        script_name: &str,
+        tags: &Vec<String>,
         world: &mut World,
         input_state: &InputState,
     ) -> anyhow::Result<()> {
-        log_once::debug_once!("init_entity_script: {} for {:?}", script_name, entity_id);
 
-        Ok(())
+        Err(anyhow::anyhow!("it aint ready yet bozo"))
     }
 
     pub fn update_entity_script(
         &mut self,
         entity_id: hecs::Entity,
-        script_name: &str,
+        tags: &Vec<String>,
         world: &mut World,
         input_state: &InputState,
         dt: f32,
     ) -> anyhow::Result<()> {
-        log_once::debug_once!("Update entity script name: {}", script_name);
 
-        Ok(())
+        Err(anyhow::anyhow!("it aint ready yet bozo"))
     }
 }
 
@@ -236,8 +210,7 @@ pub fn convert_entity_to_group(
 
             let script_node = if let Ok(script) = world.get::<&ScriptComponent>(entity_id) {
                 Some(EntityNode::Script {
-                    name: script.name.clone(),
-                    path: script.path.clone(),
+                    tags: script.tags.clone(),
                 })
             } else {
                 None
@@ -280,15 +253,15 @@ pub fn attach_script_to_entity(
     Ok(())
 }
 
-pub enum ScriptAction {
-    AttachScript {
-        script_path: PathBuf,
-        script_name: String,
-    },
-    CreateAndAttachScript {
-        script_path: PathBuf,
-        script_name: String,
-    },
-    RemoveScript,
-    EditScript,
-}
+// pub enum ScriptAction {
+//     AttachScript {
+//         script_path: PathBuf,
+//         script_name: String,
+//     },
+//     CreateAndAttachScript {
+//         script_path: PathBuf,
+//         script_name: String,
+//     },
+//     RemoveScript,
+//     EditScript,
+// }
diff --git a/eucalyptus-core/src/scripting/java.rs b/eucalyptus-core/src/scripting/java.rs
deleted file mode 100644
index 62b3c3c..0000000
--- a/eucalyptus-core/src/scripting/java.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-use jni::{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM};
-use jni::objects::{JObject, JString};
-
-/// A dropbear wrapper for Java Virtual Machine (JVM) based functions
-pub(crate) struct JavaContext {
-    jvm: JavaVM,
-}
-
-impl JavaContext {
-    /// Creates a new [`JavaContext`]
-    pub fn new() -> anyhow::Result<Self> {
-        let jvm_args = InitArgsBuilder::new()
-            .version(JNIVersion::V8)
-            .build()?;
-
-        let jvm = JavaVM::new(jvm_args)?;
-        log::info!("Initialised JVM");
-        Ok(Self {
-            jvm
-        })
-    }
-}
-
-// JNIEXPORT jstring JNICALL Java_com_dropbear_ffi_NativeEngine_Ping
-//    (JNIEnv *, jobject, jstring);
-#[unsafe(no_mangle)]
-#[allow(non_snake_case)]
-pub extern "system" fn Java_com_dropbear_ffi_NativeEngine_Ping<'local>(
-    mut env: JNIEnv<'local>,
-    _this: JObject<'local>,
-    input: JString<'local>,
-) -> JString<'local> {
-    let message: String = env.get_string(&input)
-        .expect("Failed to get input string")
-        .into();
-
-    let response = format!("Pong! You sent: {}", message);
-
-    let output = env.new_string(response)
-        .expect("Failed to create output string");
-
-    output.into()
-}
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting/jni.rs b/eucalyptus-core/src/scripting/jni.rs
new file mode 100644
index 0000000..ee354f8
--- /dev/null
+++ b/eucalyptus-core/src/scripting/jni.rs
@@ -0,0 +1 @@
+//! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate
\ No newline at end of file
diff --git a/eucalyptus-core/src/scripting/kmp.rs b/eucalyptus-core/src/scripting/kmp.rs
new file mode 100644
index 0000000..655b546
--- /dev/null
+++ b/eucalyptus-core/src/scripting/kmp.rs
@@ -0,0 +1 @@
+//! Deals with Kotlin/Native library loading for different platforms.
\ No newline at end of file
diff --git a/eucalyptus-core/src/states.rs b/eucalyptus-core/src/states.rs
index b2231cb..6b936e7 100644
--- a/eucalyptus-core/src/states.rs
+++ b/eucalyptus-core/src/states.rs
@@ -439,8 +439,7 @@ pub enum EntityNode {
         name: String,
     },
     Script {
-        name: String,
-        path: PathBuf,
+        tags: Vec<String>
     },
     Light {
         id: hecs::Entity,
@@ -460,8 +459,7 @@ pub enum EntityNode {
 
 #[derive(Default, Debug, Serialize, Deserialize, Clone)]
 pub struct ScriptComponent {
-    pub name: String,
-    pub path: PathBuf,
+    pub tags: Vec<String>
 }
 
 impl EntityNode {
@@ -484,8 +482,7 @@ impl EntityNode {
                     name: name.clone(),
                 },
                 EntityNode::Script {
-                    name: script.name.clone(),
-                    path: script.path.clone(),
+                    tags: script.tags.clone(),
                 },
             ];
 
@@ -892,8 +889,7 @@ impl SceneConfig {
 
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
                             // if let (Some(target_label), Some(offset)) = (
                             //     &camera_config.follow_target_entity_label,
@@ -924,8 +920,7 @@ impl SceneConfig {
                     } else {
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
                             world.spawn((adopted, transform, script, entity_config.properties.clone()))
                         } else {
@@ -977,20 +972,8 @@ impl SceneConfig {
 
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
-                            // if let (Some(target_label), Some(offset)) = (
-                            //     &camera_config.follow_target_entity_label,
-                            //     &camera_config.follow_offset,
-                            // ) {
-                            //     let follow_target = CameraFollowTarget {
-                            //         follow_target: target_label.clone(),
-                            //         offset: DVec3::from_array(*offset),
-                            //     };
-                            //     world.spawn((adopted, transform, script, entity_config.properties.clone(), camera, camera_component, follow_target))
-                            // } else {
-                            // }
                             world.spawn((adopted, transform, script, entity_config.properties.clone(), camera, camera_component))
                         } else {
                             world.spawn((adopted, transform, entity_config.properties.clone(), camera, camera_component))
@@ -999,8 +982,7 @@ impl SceneConfig {
                         // Entity without camera components
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
                             world.spawn((adopted, transform, script, entity_config.properties.clone()))
                         } else {
@@ -1090,8 +1072,7 @@ impl SceneConfig {
 
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
                             // if let (Some(target_label), Some(offset)) = (
                             //     &camera_config.follow_target_entity_label,
@@ -1123,8 +1104,7 @@ impl SceneConfig {
                         // Entity without camera components
                         if let Some(script_config) = &entity_config.script {
                             let script = ScriptComponent {
-                                name: script_config.name.clone(),
-                                path: script_config.path.clone(),
+                                tags: script_config.tags.clone(),
                             };
                             world.spawn((plane, transform, script, entity_config.properties.clone()))
                         } else {
diff --git a/eucalyptus-editor/build.rs b/eucalyptus-editor/build.rs
index 6f5a5fc..5d3a697 100644
--- a/eucalyptus-editor/build.rs
+++ b/eucalyptus-editor/build.rs
@@ -1,5 +1,3 @@
-use std::fs::{self, File};
-use std::io::Cursor;
 use std::process::Command;
 
 fn main() -> anyhow::Result<()> {
@@ -16,59 +14,6 @@ fn main() -> anyhow::Result<()> {
     println!("cargo:rerun-if-changed=.git/HEAD");
     println!("cargo:rerun-if-changed=.git/refs/heads");
 
-    // todo: move this into the "setup" process
-    let repo_zip_url = "https://github.com/4tkbytes/dropbear/archive/refs/heads/main.zip";
-    let response = reqwest::blocking::get(repo_zip_url)
-        .map_err(|e| anyhow::anyhow!("Failed to download repo zip: {}", e))?
-        .bytes()
-        .map_err(|e| anyhow::anyhow!("Failed to read zip bytes: {}", e))?;
-
-    let reader = Cursor::new(response);
-    let mut zip = zip::ZipArchive::new(reader)
-        .map_err(|e| anyhow::anyhow!("Failed to read zip archive: {}", e))?;
-
-    let app_info = app_dirs2::AppInfo {
-        name: "Eucalyptus",
-        author: "4tkbytes",
-    };
-    let app_data_dir = app_dirs2::app_root(app_dirs2::AppDataType::UserData, &app_info)
-        .map_err(|e| anyhow::anyhow!("Could not determine app data directory: {}", e))?;
-
-    fs::create_dir_all(&app_data_dir)
-        .map_err(|e| anyhow::anyhow!("Failed to create app data directory: {}", e))?;
-
-    let resource_prefix = "dropbear-main/resources/";
-    let mut found_resource = false;
-    for i in 0..zip.len() {
-        let mut file = zip.by_index(i).unwrap();
-        let name = file.name();
-
-        if name.starts_with(resource_prefix) && !name.ends_with('/') {
-            found_resource = true;
-            let rel_path = &name[resource_prefix.len()..];
-            let rel_path = rel_path.strip_prefix('/').unwrap_or(rel_path);
-            let dest_path = app_data_dir.join(rel_path);
-
-            if let Some(parent) = dest_path.parent() {
-                fs::create_dir_all(parent)
-                    .map_err(|e| anyhow::anyhow!("Failed to create parent directory: {}", e))?;
-            }
-
-            println!("Copying {} to {:?}", name, dest_path);
-
-            let mut outfile = File::create(&dest_path)
-                .map_err(|e| anyhow::anyhow!("Failed to create file: {}", e))?;
-            std::io::copy(&mut file, &mut outfile)
-                .map_err(|e| anyhow::anyhow!("Failed to copy file: {}", e))?;
-        }
-    }
-
-    if !found_resource {
-        return Err(anyhow::anyhow!(
-            "No resources folder found in the github repository [4tkbytes/dropbear] :("
-        ));
-    }
-
     // fuck you windows :(
     #[cfg(target_os = "windows")]
     {
diff --git a/eucalyptus-editor/src/debug.rs b/eucalyptus-editor/src/debug.rs
index 3b3dc4d..d33fe39 100644
--- a/eucalyptus-editor/src/debug.rs
+++ b/eucalyptus-editor/src/debug.rs
@@ -17,5 +17,7 @@ pub(crate) fn show_menu_bar(
             log::info!("Show Entities Loaded under Debug Menu is clicked");
             *signal = Signal::LogEntities;
         }
+
+        
     });
 }
diff --git a/eucalyptus-editor/src/editor/component.rs b/eucalyptus-editor/src/editor/component.rs
index d70c299..a3cfd2a 100644
--- a/eucalyptus-editor/src/editor/component.rs
+++ b/eucalyptus-editor/src/editor/component.rs
@@ -6,7 +6,6 @@ use dropbear_engine::attenuation::ATTENUATION_PRESETS;
 use dropbear_engine::entity::{AdoptedEntity, Transform};
 use dropbear_engine::lighting::{Light, LightComponent, LightType};
 use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui};
-use eucalyptus_core::scripting::{ScriptAction, TEMPLATE_SCRIPT};
 use eucalyptus_core::states::{ModelProperties, ScriptComponent, Value};
 use eucalyptus_core::warn;
 use glam::Vec3;
@@ -585,70 +584,30 @@ impl InspectableComponent for ScriptComponent {
         signal: &mut Signal,
         label: &mut String,
     ) {
-        let script_loc = self.path.to_str().unwrap_or("").to_string();
-
         ui.vertical(|ui| {
             CollapsingHeader::new("Scripting")
                 .default_open(true)
                 .show(ui, |ui| {
-                    ui.horizontal(|ui| {
-                        if ui.button("Browse").clicked()
-                            && let Some(script_file) = rfd::FileDialog::new()
-                                .add_filter("Typescript", &["ts"])
-                                .pick_file()
-                            {
-                                let script_name = script_file
-                                    .file_stem()
-                                    .unwrap_or_default()
-                                    .to_string_lossy()
-                                    .to_string();
-                                *signal = Signal::ScriptAction(ScriptAction::AttachScript {
-                                    script_path: script_file,
-                                    script_name,
+                    CollapsingHeader::new("Tags")
+                        .default_open(true)
+                        .show(ui, |ui| {
+                            let mut local_del: Option<usize> = None;
+                            for (i, tag) in self.tags.iter_mut().enumerate() {
+                                let current_width = ui.available_width();
+                                ui.horizontal(|ui| {
+                                    ui.add_sized([current_width*70.0/100.0, 20.0], TextEdit::singleline(tag));
+                                    if ui.button("🗑️").clicked() {
+                                        local_del = Some(i);
+                                    }
                                 });
                             }
-
-                        if ui.button("New").clicked()
-                            && let Some(script_path) = rfd::FileDialog::new()
-                                .add_filter("TypeScript", &["ts"])
-                                .set_file_name(format!("{}_script.ts", label))
-                                .save_file()
-                            {
-                                match std::fs::write(&script_path, TEMPLATE_SCRIPT) {
-                                    Ok(_) => {
-                                        let script_name = script_path
-                                            .file_stem()
-                                            .unwrap_or_default()
-                                            .to_string_lossy()
-                                            .to_string();
-                                        *signal = Signal::ScriptAction(ScriptAction::CreateAndAttachScript {
-                                            script_path,
-                                            script_name,
-                                        });
-                                    },
-                                    Err(e) => {
-                                        warn!("Failed to create script file: {}", e);
-                                    },
-                                }
+                            if let Some(i) = local_del {
+                                self.tags.remove(i);
                             }
-                    });
-
-                    ui.separator();
-
-                    ui.horizontal_wrapped(|ui| {
-                        ui.label("Script Location:");
-                        ui.label(script_loc);
-                    });
-
-                    if ui.button("Remove").clicked() {
-                        *signal = Signal::ScriptAction(ScriptAction::RemoveScript);
-                    }
-                    ui.separator();
-                    ui.horizontal(|ui| {
-                        if ui.button("Edit Script").clicked() {
-                            *signal = Signal::ScriptAction(ScriptAction::EditScript);
-                        }
-                    });
+                            if ui.button("➕ Add").clicked() {
+                                self.tags.push(String::new())
+                            }
+                        });
                 });
         });
     }
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index c523504..3714202 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -28,7 +28,7 @@ use eucalyptus_core::{camera::{
     CameraAction, CameraComponent, CameraType, DebugCamera,
 }, states::WorldLoadingStatus};
 use eucalyptus_core::input::InputState;
-use eucalyptus_core::scripting::{ScriptAction, ScriptManager};
+use eucalyptus_core::scripting::{ScriptManager};
 use eucalyptus_core::states::{
     CameraConfig, EditorTab, EntityNode, LightConfig, ModelProperties, PROJECT, SCENES,
     SceneEntity, ScriptComponent,
@@ -899,12 +899,12 @@ fn show_entity_tree(
                     }
                 });
             }
-            EntityNode::Script { name, path: _ } => {
+            EntityNode::Script { tags } => {
                 ui.horizontal(|ui| {
                     handle.ui(ui, |ui| {
                         ui.label("📜");
                     });
-                    ui.label(name.to_string());
+                    ui.label("Script");
                 });
             }
             EntityNode::Group {
@@ -1168,10 +1168,9 @@ pub enum Signal {
     Paste(SceneEntity),
     Delete,
     Undo,
-    ScriptAction(ScriptAction),
-    #[allow(dead_code)]
+    // ScriptAction(ScriptAction),
     // not actions required because follow target is set through scripting. 
-    CameraAction(CameraAction),
+    // CameraAction(CameraAction),
     Play,
     StopPlaying,
     AddComponent(hecs::Entity, EntityType),
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index 6c899c5..ef19513 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -106,18 +106,11 @@ impl Scene for Editor {
                     .iter()
                 {
                     log_once::debug_once!(
-                        "Script Entity -> id: {:?}, component: {:?}",
+                        "Script Entity -> id: {:?}, tags: {:?}",
                         entity_id,
-                        script
+                        script.tags
                     );
-                    script.name = script
-                        .path
-                        .file_name()
-                        .unwrap()
-                        .to_str()
-                        .unwrap()
-                        .to_string();
-                    script_entities.push((entity_id, script.name.clone()));
+                    script_entities.push((entity_id, script.tags.clone()));
                 }
             }
 
@@ -134,8 +127,7 @@ impl Scene for Editor {
                     dt,
                 ) {
                     log_once::warn_once!(
-                        "Failed to update script '{}' for entity {:?}: {}",
-                        script_name,
+                        "Failed to update script for entity {:?}: {}",
                         entity_id,
                         e
                     );
diff --git a/eucalyptus-editor/src/signal.rs b/eucalyptus-editor/src/signal.rs
index 135363b..8963c1f 100644
--- a/eucalyptus-editor/src/signal.rs
+++ b/eucalyptus-editor/src/signal.rs
@@ -8,7 +8,6 @@ use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use eucalyptus_core::states::{ModelProperties, ScriptComponent, Value};
 use eucalyptus_core::{fatal, info, scripting, success, success_without_console, warn, warn_without_console};
 use eucalyptus_core::camera::{CameraAction, CameraComponent, CameraType};
-use eucalyptus_core::scripting::ScriptAction;
 use eucalyptus_core::spawn::{push_pending_spawn, PendingSpawn};
 use crate::editor::{ComponentType, Editor, EditorState, EntityType, PendingSpawn2, Signal, UndoableAction};
 
@@ -93,210 +92,210 @@ 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::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");
@@ -331,43 +330,21 @@ impl SignalController for Editor {
 
                     for (entity_id, script) in script_entities {
                         log::debug!(
-                            "Initialising entity script [{}] from path: {}",
-                            script.name,
-                            script.path.display()
+                            "Initialising entity script for entity {:?} with tags: {:?}",
+                            entity_id,
+                            script.tags
                         );
 
-                        let bytes = match std::fs::read_to_string(&script.path) {
-                            Ok(val) => val,
-                            Err(e) => {
-                                self.signal = Signal::None;
-                                fatal!(
-                                    "Unable to read script {} to bytes because {}",
-                                    &script.path.display(),
-                                    e
-                                );
-                                return Err(anyhow::anyhow!(e));
-                            }
-                        };
-
-                        match self.script_manager.load_script(
-                            &script
-                                .path
-                                .file_name()
-                                .unwrap()
-                                .to_string_lossy()
-                                .to_string(),
-                            bytes,
-                        ) {
-                            Ok(script_name) => {
+                        match self.script_manager.load_script() {
+                            Ok(_) => {
                                 if let Err(e) = self.script_manager.init_entity_script(
                                     entity_id,
-                                    &script_name,
+                                    &script.tags,
                                     &mut self.world,
                                     &self.input_state,
                                 ) {
                                     log::warn!(
-                                        "Failed to initialise script '{}' for entity {:?}: {}",
-                                        script.name,
+                                        "Failed to initialise script for entity {:?}: {}",
                                         entity_id,
                                         e
                                     );
@@ -382,7 +359,7 @@ impl SignalController for Editor {
                             Err(e) => {
                                 // todo: proper error menu
                                 self.signal = Signal::StopPlaying;
-                                fatal!("Failed to load script '{}': {}", script.name, e);
+                                fatal!("Failed to load script for {:?} with tags {:?} because {}", entity_id, script.tags, e);
                             }
                         }
                     }
@@ -415,28 +392,28 @@ 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::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/gradle.properties b/gradle.properties
index 7fc6f1f..29e08e8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1 @@
-kotlin.code.style=official
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..3eb05ee
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,10 @@
+[versions]
+kotlin = "2.1.0"
+kotlinxSerialization = "1.8.0"
+
+[libraries]
+kotlinxSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
+
+[plugins]
+kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
+kotlinxSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 5354fc0..d12f0c0 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,4 +1,8 @@
-plugins {
-    id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
-}
-rootProject.name = "dropbear"
\ No newline at end of file
+rootProject.name = "dropbear"
+
+pluginManagement {
+    repositories {
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/README.md
diff --git a/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
new file mode 100644
index 0000000..9a6929c
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/DropbearEngine.kt
@@ -0,0 +1,27 @@
+package com.dropbear
+
+import com.dropbear.ffi.NativeEngine
+
+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]
+     */
+    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)))
+            }
+        })
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/EntityId.kt b/src/commonMain/kotlin/com/dropbear/EntityId.kt
new file mode 100644
index 0000000..8733db0
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/EntityId.kt
@@ -0,0 +1,3 @@
+package com.dropbear
+
+data class EntityId(val id: Long)
diff --git a/src/commonMain/kotlin/com/dropbear/EntityRef.kt b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
new file mode 100644
index 0000000..022be70
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/EntityRef.kt
@@ -0,0 +1,25 @@
+package com.dropbear
+
+import com.dropbear.math.Vector3D
+
+/**
+ * A class to hold a reference to an entity.
+ *
+ * The dropbear engine interface is made in Rust, which uses a
+ * borrow checker paradigm, which heavily utilises immutability
+ * unless explicitly provided with the `mut` keyword.
+ *
+ * Because of this, the primary source of the World (place to store
+ * entities) is stored in Rust, and passing an EntityRef (which contains
+ * an ID) follows Rust's ideologies of passing references instead of the entire entity,
+ * which provides immutability.
+ *
+ * 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)) {
+    /**
+     * Sets the position of the entity by a Vector
+     */
+    fun setPosition(position: Vector3D, engine: DropbearEngine) {}
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/Example.kt b/src/commonMain/kotlin/com/dropbear/Example.kt
new file mode 100644
index 0000000..e6a9d7d
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/Example.kt
@@ -0,0 +1,26 @@
+import com.dropbear.DropbearEngine
+import com.dropbear.EntityId
+import com.dropbear.System
+import com.dropbear.ProjectScriptingMetadata
+import com.dropbear.ScriptRegistration
+
+fun playerMovement(engine: DropbearEngine, entityId: EntityId, deltaTime: Double) {
+
+}
+
+class Player: System {
+    override fun update(engine: DropbearEngine, entityId: EntityId, deltaTime: Float) {
+        TODO("Not yet implemented")
+    }
+}
+
+class Metadata : ProjectScriptingMetadata {
+    override fun getScripts(): List<ScriptRegistration> {
+        return listOf (
+            ScriptRegistration(
+                tags = listOf("player", "movement"),
+                script = Player()
+            ),
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/ProjectScriptingMetadata.kt b/src/commonMain/kotlin/com/dropbear/ProjectScriptingMetadata.kt
new file mode 100644
index 0000000..3965904
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/ProjectScriptingMetadata.kt
@@ -0,0 +1,16 @@
+package com.dropbear
+
+/**
+ * A mandatory class defined in a eucalyptus project that "discovers" and attaches
+ * tags to files for the registry to use.
+ */
+interface ProjectScriptingMetadata {
+    /**
+     * A function that is placed in Manifest.kt, and allows you to attach the required
+     * tags to the correct files.
+     *
+     * The original method was to use Kotlin Symbol Processing, however Kotlin/Native
+     * doesn't support KSP.
+     */
+    fun getScripts(): List<ScriptRegistration>
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/Registry.kt b/src/commonMain/kotlin/com/dropbear/Registry.kt
new file mode 100644
index 0000000..533f268
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/Registry.kt
@@ -0,0 +1,17 @@
+package com.dropbear
+
+internal object Registry {
+    private val _entries = mutableListOf<ScriptEntry>()
+    val entries: List<ScriptEntry> get() = _entries.toList()
+
+    fun register(entry: ScriptEntry) {
+        _entries.add(entry)
+    }
+
+    fun findByTag(tag: String): List<ScriptEntry> {
+        return _entries.filter { tag in it.tags }
+    }
+
+    internal fun registerAll() {
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/RunnableScript.kt b/src/commonMain/kotlin/com/dropbear/RunnableScript.kt
new file mode 100644
index 0000000..b10521e
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/RunnableScript.kt
@@ -0,0 +1,20 @@
+package com.dropbear
+
+/**
+ * The basic interface that all classes implement for the class to be run.
+ */
+sealed class RunnableScript {
+    /**
+     * A function that is run once during the lifetime of the entity.
+     *
+     * It can be used to set initial properties such as health and more.
+     *
+     * ALl classes that implement RunnableScript need to implement the load function.
+     */
+    abstract fun load()
+
+    /**
+     * A function that is run every frame during the lifetime of the entity.
+     */
+    abstract fun update()
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/ScriptEntry.kt b/src/commonMain/kotlin/com/dropbear/ScriptEntry.kt
new file mode 100644
index 0000000..6448f56
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/ScriptEntry.kt
@@ -0,0 +1,7 @@
+package com.dropbear
+
+internal data class ScriptEntry(
+    val tags: List<String>,
+    val functionName: String,
+    val invoker: (DropbearEngine, EntityId, Double) -> Unit
+)
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/ScriptRegistration.kt b/src/commonMain/kotlin/com/dropbear/ScriptRegistration.kt
new file mode 100644
index 0000000..63b8e57
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/ScriptRegistration.kt
@@ -0,0 +1,6 @@
+package com.dropbear
+
+data class ScriptRegistration(
+    val tags: List<String>,
+    val script: System
+)
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/System.kt b/src/commonMain/kotlin/com/dropbear/System.kt
new file mode 100644
index 0000000..31eff7b
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/System.kt
@@ -0,0 +1,5 @@
+package com.dropbear
+
+fun interface System {
+    fun update(engine: DropbearEngine, current_entity: EntityId, deltaTime: Float)
+}
\ 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
new file mode 100644
index 0000000..af3dfe2
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/ffi/NativeEngine.kt
@@ -0,0 +1,7 @@
+package com.dropbear.ffi
+
+import com.dropbear.EntityRef
+
+expect class NativeEngine {
+    fun getEntity(label: String): Long?
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
new file mode 100644
index 0000000..97779eb
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/math/Quaternion.kt
@@ -0,0 +1,11 @@
+package com.dropbear.math
+
+typealias QuaternionD = Quaternion<Double>
+
+class Quaternion<T: Number>(var x: T, var y: T, var z: T, var w: T) {
+    companion object {
+        fun identity(): Quaternion<Double> {
+            return Quaternion(0.0, 0.0, 0.0, 1.0)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/math/Transform.kt b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
new file mode 100644
index 0000000..bfa039f
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
@@ -0,0 +1,9 @@
+package com.dropbear.math
+
+/**
+ * A class that keeps all the values of a Transform, which consists of a
+ * position ([Vector3D]), rotation([QuaternionD]) and a scale([Vector3D]).
+ */
+class Transform(var position: Vector3D, var rotation: QuaternionD, var scale: Vector3D) {
+
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/com/dropbear/math/Vector3.kt b/src/commonMain/kotlin/com/dropbear/math/Vector3.kt
new file mode 100644
index 0000000..822dd23
--- /dev/null
+++ b/src/commonMain/kotlin/com/dropbear/math/Vector3.kt
@@ -0,0 +1,29 @@
+package com.dropbear.math
+
+/**
+ * A class for holding a vector of `3` values of the same type.
+ */
+class Vector3<T: Number>(var x: T, var y: T, var z: T) {
+    companion object {
+        /**
+         * Creates a new [com.dropbear.math.Vector3] of type `T` with one value.
+         */
+        fun <T: Number> uniform(value: T): Vector3<T> {
+            return Vector3(value, value, value)
+        }
+
+        /**
+         * Creates a [com.dropbear.math.Vector3] of type [Double] filled with only zeroes
+         */
+        fun zero(): Vector3<Double> {
+            return Vector3(0.0, 0.0, 0.0)
+        }
+    }
+
+    fun asDoubleVector(): Vector3<Double> {
+        return Vector3(this.x.toDouble(), this.y.toDouble(), this.z.toDouble())
+    }
+
+}
+
+typealias Vector3D = Vector3<Double>
\ 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
new file mode 100644
index 0000000..0f8090b
--- /dev/null
+++ b/src/jvmMain/java/com/dropbear/ffi/JNINative.java
@@ -0,0 +1,5 @@
+package com.dropbear.ffi;
+
+public class JNINative {
+    public native long getEntity(String label);
+}
diff --git a/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
new file mode 100644
index 0000000..fb3dba0
--- /dev/null
+++ b/src/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -0,0 +1,9 @@
+package com.dropbear.ffi
+
+actual class NativeEngine {
+    private val jni = JNINative()
+
+    actual fun getEntity(label: String): Long? {
+        return jni.getEntity(label)
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/DropbearEngine.kt b/src/main/kotlin/com/dropbear/DropbearEngine.kt
deleted file mode 100644
index 44722c3..0000000
--- a/src/main/kotlin/com/dropbear/DropbearEngine.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.dropbear
-
-import com.dropbear.ffi.NativeEngine
-
-class DropbearEngine {
-    var nativeEngine: NativeEngine? = null
-
-    // figure out how to create a new class
-    /**
-     * Fetches the currently active entity the script is attached to.
-     *
-     * If there is no entity this script is attached to, it will return
-     * a `null` in the form of a [Result]
-     */
-    fun getActiveEntity(): Result<EntityRef> {
-        return Result.failure(Exception("Function not implemented"))
-    }
-
-    /**
-     * 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]
-     */
-    fun getEntity(label: String): EntityRef? {
-        return null
-    }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/EntityRef.kt b/src/main/kotlin/com/dropbear/EntityRef.kt
deleted file mode 100644
index 7bccd3c..0000000
--- a/src/main/kotlin/com/dropbear/EntityRef.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.dropbear
-
-import com.dropbear.math.Vector3D
-
-/**
- * A class to hold a reference to an entity.
- *
- * The dropbear engine interface is made in Rust, which uses a
- * borrow checker paradigm, which heavily utilises immutability
- * unless explicitly provided with the `mut` keyword.
- *
- * Because of this, the primary source of the World (place to store
- * entities) is stored in Rust, and passing an EntityRef (which contains
- * an ID) follows Rust's ideologies of passing references instead of the entire entity,
- * which provides immutability.
- *
- * 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 {
-    var label: String = ""
-
-    /**
-     * Sets the position of the entity by a Vector
-     */
-    fun setPosition(position: Vector3D, engine: DropbearEngine) {}
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/Example.kt b/src/main/kotlin/com/dropbear/Example.kt
deleted file mode 100644
index e1ef013..0000000
--- a/src/main/kotlin/com/dropbear/Example.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.dropbear
-
-class Example(override var engine: DropbearEngine) : RunnableScript {
-    override fun load() {
-        val entity = engine.getActiveEntity()
-
-    }
-
-    override fun update() {
-        TODO("Not yet implemented")
-    }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/RunnableScript.kt b/src/main/kotlin/com/dropbear/RunnableScript.kt
deleted file mode 100644
index d7cb338..0000000
--- a/src/main/kotlin/com/dropbear/RunnableScript.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.dropbear
-
-/**
- * The basic interface that all classes implement for the class to be run.
- */
-interface RunnableScript {
-    var engine: DropbearEngine
-    /**
-     * A function that is run once during the lifetime of the entity.
-     *
-     * It can be used to set initial properties such as health and more.
-     *
-     * ALl classes that implement RunnableScript need to implement the load function.
-     */
-    fun load()
-
-    /**
-     * A function that is run every frame during the lifetime of the entity.
-     */
-    fun update()
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/ffi/NativeEngine.java b/src/main/kotlin/com/dropbear/ffi/NativeEngine.java
deleted file mode 100644
index bbedb38..0000000
--- a/src/main/kotlin/com/dropbear/ffi/NativeEngine.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.dropbear.ffi;
-
-import com.dropbear.math.Transform;
-
-/**
- * A class for dealing with native functions that is required by the Kotlin scripting
- * and the Rust game engine.
- */
-public class NativeEngine {
-    /**
-     * Fetches the transform of the entity by its label in the editor
-     * @param label The label of the entity
-     * @return The {@link Transform} component if the entity exists, null if not.
-     */
-    public native Transform getTransformOfEntity(String label);
-
-    /**
-     * Fetches the label of the entity this script is currently attached to.
-     * @return The label of the attached entity or null if its not attached
-     */
-    public native String getLabelOfAttachedEntity();
-}
diff --git a/src/main/kotlin/com/dropbear/math/Quaternion.kt b/src/main/kotlin/com/dropbear/math/Quaternion.kt
deleted file mode 100644
index 97779eb..0000000
--- a/src/main/kotlin/com/dropbear/math/Quaternion.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.dropbear.math
-
-typealias QuaternionD = Quaternion<Double>
-
-class Quaternion<T: Number>(var x: T, var y: T, var z: T, var w: T) {
-    companion object {
-        fun identity(): Quaternion<Double> {
-            return Quaternion(0.0, 0.0, 0.0, 1.0)
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/math/Transform.kt b/src/main/kotlin/com/dropbear/math/Transform.kt
deleted file mode 100644
index bfa039f..0000000
--- a/src/main/kotlin/com/dropbear/math/Transform.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.dropbear.math
-
-/**
- * A class that keeps all the values of a Transform, which consists of a
- * position ([Vector3D]), rotation([QuaternionD]) and a scale([Vector3D]).
- */
-class Transform(var position: Vector3D, var rotation: QuaternionD, var scale: Vector3D) {
-
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/dropbear/math/Vector3.kt b/src/main/kotlin/com/dropbear/math/Vector3.kt
deleted file mode 100644
index 822dd23..0000000
--- a/src/main/kotlin/com/dropbear/math/Vector3.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.dropbear.math
-
-/**
- * A class for holding a vector of `3` values of the same type.
- */
-class Vector3<T: Number>(var x: T, var y: T, var z: T) {
-    companion object {
-        /**
-         * Creates a new [com.dropbear.math.Vector3] of type `T` with one value.
-         */
-        fun <T: Number> uniform(value: T): Vector3<T> {
-            return Vector3(value, value, value)
-        }
-
-        /**
-         * Creates a [com.dropbear.math.Vector3] of type [Double] filled with only zeroes
-         */
-        fun zero(): Vector3<Double> {
-            return Vector3(0.0, 0.0, 0.0)
-        }
-    }
-
-    fun asDoubleVector(): Vector3<Double> {
-        return Vector3(this.x.toDouble(), this.y.toDouble(), this.z.toDouble())
-    }
-
-}
-
-typealias Vector3D = Vector3<Double>
\ No newline at end of file
diff --git a/src/nativeInterop/cinterop/dropbear.def b/src/nativeInterop/cinterop/dropbear.def
new file mode 100644
index 0000000..a4b5340
--- /dev/null
+++ b/src/nativeInterop/cinterop/dropbear.def
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000..53eafe2
--- /dev/null
+++ b/src/nativeInterop/cinterop/dropbear.h
@@ -0,0 +1,9 @@
+#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/NativeEngine.native.kt b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
new file mode 100644
index 0000000..fa4bea8
--- /dev/null
+++ b/src/nativeMain/kotlin/com/dropbear/ffi/NativeEngine.native.kt
@@ -0,0 +1,13 @@
+@file:OptIn(ExperimentalForeignApi::class)
+@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
+
+package com.dropbear.ffi
+
+import kotlinx.cinterop.*
+
+actual class NativeEngine {
+    actual fun getEntity(label: String): Long? {
+        return com.dropbear.ffi.native.dropbear_get_entity(label)
+//        return 0L
+    }
+}
\ No newline at end of file