tirbofish/dropbear · diff
18 hours on this commit, and fuckass kotlin native doesnt work for shit. i hate this ugh
Signature present but could not be verified.
Unverified
@@ -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") +} @@ -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 = [] @@ -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")] @@ -5,4 +5,5 @@ pub mod logging; pub mod scripting; pub mod states; pub mod utils; -pub mod spawn; +pub mod spawn; +mod ptr; @@ -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 + } +} @@ -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, +// } @@ -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() -} @@ -0,0 +1 @@ +//! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate @@ -0,0 +1 @@ +//! Deals with Kotlin/Native library loading for different platforms. @@ -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 { @@ -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")] { @@ -17,5 +17,7 @@ pub(crate) fn show_menu_bar( log::info!("Show Entities Loaded under Debug Menu is clicked"); *signal = Signal::LogEntities; } + + }); } @@ -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()) + } + }); }); }); } @@ -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), @@ -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 ); @@ -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 => { @@ -1 +1 @@ -kotlin.code.style=official +kotlin.code.style=official @@ -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" } @@ -1,4 +1,8 @@ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" -} -rootProject.name = "dropbear" +rootProject.name = "dropbear" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} @@ -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))) + } + }) + } +} @@ -0,0 +1,3 @@ +package com.dropbear + +data class EntityId(val id: Long) @@ -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) {} +} @@ -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() + ), + ) + } +} @@ -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> +} @@ -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() { + } +} @@ -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() +} @@ -0,0 +1,7 @@ +package com.dropbear + +internal data class ScriptEntry( + val tags: List<String>, + val functionName: String, + val invoker: (DropbearEngine, EntityId, Double) -> Unit +) @@ -0,0 +1,6 @@ +package com.dropbear + +data class ScriptRegistration( + val tags: List<String>, + val script: System +) @@ -0,0 +1,5 @@ +package com.dropbear + +fun interface System { + fun update(engine: DropbearEngine, current_entity: EntityId, deltaTime: Float) +} @@ -0,0 +1,7 @@ +package com.dropbear.ffi + +import com.dropbear.EntityRef + +expect class NativeEngine { + fun getEntity(label: String): Long? +} @@ -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) + } + } +} @@ -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) { + +} @@ -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> @@ -0,0 +1,5 @@ +package com.dropbear.ffi; + +public class JNINative { + public native long getEntity(String label); +} @@ -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) + } +} @@ -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 - } -} @@ -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) {} -} @@ -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") - } -} @@ -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() -} @@ -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(); -} @@ -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) - } - } -} @@ -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) { - -} @@ -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> @@ -0,0 +1,7 @@ +headers = dropbear.h +headerFilter = dropbear.h +package = com.dropbear.ffi.native + +--- + +long long dropbear_get_entity(const char* param); @@ -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 @@ -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 + } +}