kitgit

tirbofish/dropbear · diff

b44209f · tk

Merge branch 'main' into sockets # Conflicts: # build.gradle.kts # eucalyptus-core/Cargo.toml # eucalyptus-core/build.rs # eucalyptus-core/src/scripting.rs # eucalyptus-core/src/scripting/java.rs # eucalyptus-editor/build.rs # eucalyptus-editor/src/editor/component.rs # src/commonMain/kotlin/com/dropbear/EntityRef.kt # src/main/kotlin/com/dropbear/DropbearEngine.kt # src/main/kotlin/com/dropbear/Example.kt # src/main/kotlin/com/dropbear/ffi/NativeEngine.java

Unverified

diff --git a/.github/workflows/build_fatty.yaml b/.github/workflows/build_fatty.yaml
new file mode 100644
index 0000000..c6f126d
--- /dev/null
+++ b/.github/workflows/build_fatty.yaml
@@ -0,0 +1,35 @@
+name: Build and Upload JAR
+
+on:
+  push:
+    branches:
+      - '**'
+  workflow_dispatch:
+
+jobs:
+  build:
+    if: contains(github.event.head_commit.message, 'jarvis, run github actions')
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Set up JDK 21
+        uses: actions/setup-java@v4
+        with:
+          java-version: '21'
+          distribution: 'temurin'
+
+      - name: Grant execute permission for gradlew
+        run: chmod +x gradlew
+
+      - name: Build with Gradle
+        run: ./gradlew build
+
+      - name: Upload JAR artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: application-jar
+          path: build/libs/*.jar
+          retention-days: 30
\ No newline at end of file
diff --git a/README.md b/README.md
index 8b53e2a..dc06b84 100644
--- a/README.md
+++ b/README.md
@@ -23,14 +23,14 @@ If you might have not realised, all the crates/projects names are after Australi
 
 To build, ensure build requirements, clone the repository, then build it. It will build in debug mode, and use a lot of packages, so if your CPU is not fast enough for building you should brew a cup of coffee during the build time.
 
-With Unix systems (macOS not tested), you will have to download a couple dependencies if building locally:
+With Unix systems (macOS not tested), you will have to download a couple of dependencies if building locally:
 
 <!-- If you have a macOS system, please create a PR and add your own implementation. I know you need to use brew, but I don't know what dependencies to install.  -->
 
 
 ```bash
 # ubuntu
-sudo apt install libudev-dev pkg-config libssl-dev clang cmake meson assimp-utils
+sudo apt install libudev-dev pkg-config libssl-dev clang cmake meson assimp-utils openjdk-21-jdk
 
 # i use arch btw
 sudo pacman -Syu base-devel systemd pkgconf openssl clang cmake meson assimp jdk21-openjdk
@@ -55,6 +55,12 @@ cargo build
 
 [//]: # (git submodule update)
 
+> [!TIP]
+> During the development of your game, you should download JetBrains IntelliJ (for kotlin) for the best support. 
+> 
+> For a development build of the eucalyptus editor, it is recommended that you use JetBrains IntelliJ (for jvm) and 
+> JetBrains RustRover (for rust)
+
 ### Prebuilt
 
 If you do not want to build it locally, you are able to download the latest action build (if no releases have been made).
diff --git a/docs/notes.md b/docs/notes.md
new file mode 100644
index 0000000..7ebf38e
--- /dev/null
+++ b/docs/notes.md
@@ -0,0 +1,17 @@
+# notes to self
+
+## hybrid approach:
+
+- during dev builds, interpret using the JVM
+- on production build, build to native.
+
+to check if its prod or dev, we use a config to build.
+
+workflow:
+- user creates a new project, which loads the project
+- use jvm to watch (because editor is always on desktop)
+- on production build, create native build for specific OS
+
+## required dependencies
+
+- jdk 21
diff --git a/eucalyptus-core/src/states.rs b/eucalyptus-core/src/states.rs
index 5ced5bb..396cf4a 100644
--- a/eucalyptus-core/src/states.rs
+++ b/eucalyptus-core/src/states.rs
@@ -1356,6 +1356,7 @@ pub enum EditorTab {
     ResourceInspector, // left side,
     ModelEntityList,   // right side,
     Viewport,          // middle,
+    KotlinREPL,        // bottom side - new REPL tab
 }
 
 /// An enum that describes the status of loading the world.
diff --git a/eucalyptus-editor/src/editor/dock.rs b/eucalyptus-editor/src/editor/dock.rs
index 596ecc4..5c42783 100644
--- a/eucalyptus-editor/src/editor/dock.rs
+++ b/eucalyptus-editor/src/editor/dock.rs
@@ -74,6 +74,10 @@ pub struct DraggedAsset {
 pub static TABS_GLOBAL: LazyLock<Mutex<StaticallyKept>> =
     LazyLock::new(|| Mutex::new(StaticallyKept::default()));
 
+// Separate static for REPL to avoid deadlocks
+pub static KOTLIN_REPL: LazyLock<Mutex<Option<crate::editor::repl::KotlinREPL>>> =
+    LazyLock::new(|| Mutex::new(None));
+
 /// Variables kept statically.
 ///
 /// The entire module (including the tab viewer) due to it
@@ -107,6 +111,7 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
             EditorTab::ModelEntityList => "Model/Entity List".into(),
             EditorTab::AssetViewer => "Asset Viewer".into(),
             EditorTab::ResourceInspector => "Resource Inspector".into(),
+            EditorTab::KotlinREPL => "Kotlin REPL".into(),
         }
     }
 
@@ -1024,6 +1029,17 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                     ui.label("No entity selected, therefore no info to provide. Go on, what are you waiting for? Click an entity!");
                 }
             }
+            EditorTab::KotlinREPL => {
+                // Use separate static to avoid deadlock with TABS_GLOBAL
+                let mut repl_lock = KOTLIN_REPL.lock();
+                if repl_lock.is_none() {
+                    *repl_lock = Some(crate::editor::repl::KotlinREPL::new());
+                }
+                
+                if let Some(repl) = repl_lock.as_mut() {
+                    repl.ui(ui);
+                }
+            }
         }
 
         let mut menu_action: Option<EditorTabMenuAction> = None;
@@ -1074,6 +1090,9 @@ impl<'a> TabViewer for EditorTabViewer<'a> {
                                 menu_action = Some(EditorTabMenuAction::ViewportOption);
                             }
                         }
+                        EditorTab::KotlinREPL => {
+                            // No context menu actions for REPL tab yet
+                        }
                     }
                 })
             });
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index 8e8301a..f02e236 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -1,6 +1,7 @@
 pub mod component;
 pub mod dock;
 pub mod input;
+pub mod repl;
 pub mod scene;
 
 pub(crate) use crate::editor::dock::*;
@@ -113,7 +114,7 @@ impl Editor {
             surface.split_right(NodeIndex::root(), 0.25, vec![EditorTab::ModelEntityList]);
         let [_old, _] =
             surface.split_left(NodeIndex::root(), 0.20, vec![EditorTab::ResourceInspector]);
-        let [_old, _] = surface.split_below(right, 0.5, vec![EditorTab::AssetViewer]);
+        let [_old, _] = surface.split_below(right, 0.5, vec![EditorTab::AssetViewer, EditorTab::KotlinREPL]);
 
         // this shit doesn't work :(
         // nvm it works
@@ -583,6 +584,9 @@ impl Editor {
                     if ui_window.button("Open Viewport").clicked() {
                         self.dock_state.push_to_focused_leaf(EditorTab::Viewport);
                     }
+                    if ui_window.button("Open Kotlin REPL").clicked() {
+                        self.dock_state.push_to_focused_leaf(EditorTab::KotlinREPL);
+                    }
                 });
                 {
                     let cfg = PROJECT.read();
diff --git a/eucalyptus-editor/src/editor/repl.rs b/eucalyptus-editor/src/editor/repl.rs
new file mode 100644
index 0000000..0669a3c
--- /dev/null
+++ b/eucalyptus-editor/src/editor/repl.rs
@@ -0,0 +1,221 @@
+use egui::{Color32, FontId, RichText, ScrollArea, TextEdit};
+
+/// A simple Kotlin REPL interface for testing scripts
+pub struct KotlinREPL {
+    input: String,
+    output: Vec<ReplOutputLine>,
+    history: Vec<String>,
+    history_index: Option<usize>,
+}
+
+#[derive(Clone)]
+pub struct ReplOutputLine {
+    pub text: String,
+    pub is_error: bool,
+    pub is_input: bool,
+}
+
+impl Default for KotlinREPL {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl KotlinREPL {
+    pub fn new() -> Self {
+        let mut repl = Self {
+            input: String::new(),
+            output: Vec::new(),
+            history: Vec::new(),
+            history_index: None,
+        };
+        
+        repl.add_output("Kotlin REPL - Ready", false, false);
+        repl.add_output("Type Kotlin expressions to test script functionality", false, false);
+        repl.add_output("Example: Input.isKeyPressed(KeyCode.W)", false, false);
+        repl.add_output("", false, false);
+        
+        repl
+    }
+    
+    fn add_output(&mut self, text: &str, is_error: bool, is_input: bool) {
+        self.output.push(ReplOutputLine {
+            text: text.to_string(),
+            is_error,
+            is_input,
+        });
+    }
+    
+    fn execute(&mut self, code: &str) {
+        // Add input to output
+        self.add_output(&format!("> {}", code), false, true);
+        
+        // Add to history
+        if !code.trim().is_empty() {
+            self.history.push(code.to_string());
+            self.history_index = None;
+        }
+        
+        // Execute the code
+        match self.execute_kotlin_code(code) {
+            Ok(result) => {
+                if !result.is_empty() {
+                    self.add_output(&result, false, false);
+                }
+            }
+            Err(e) => {
+                self.add_output(&format!("Error: {}", e), true, false);
+            }
+        }
+        
+        self.add_output("", false, false); // blank line
+    }
+    
+    fn execute_kotlin_code(
+        &self,
+        code: &str,
+    ) -> anyhow::Result<String> {
+        // For now, we'll provide a simplified evaluation
+        // In the future, this could compile and run actual Kotlin code via JNI
+        
+        // Check for common test commands
+        if code.trim().starts_with("Input.isKeyPressed") {
+            Ok("boolean (check console for actual value)".to_string())
+        } else if code.trim().starts_with("Input.getMouseX") 
+                || code.trim().starts_with("Input.getMouseY") {
+            Ok("double (check console for actual value)".to_string())
+        } else if code.contains("Transform") {
+            Ok("Transform manipulation (check entity in viewport)".to_string())
+        } else if code.trim() == "help" {
+            Ok(r#"Available APIs:
+- Input.isKeyPressed(KeyCode.W) - Check if key is pressed
+- Input.getMouseX() - Get mouse X position
+- Input.getMouseY() - Get mouse Y position
+- engine.getTransform() - Get current entity transform
+- transform.position - Get/set position (Vector3D)
+- transform.rotation - Get/set rotation (Quaternion)
+- transform.scale - Get/set scale (Vector3D)
+
+Note: This is a simplified REPL. Full script execution requires 
+attaching a script to an entity and running the game."#.to_string())
+        } else if code.trim() == "clear" {
+            return Err(anyhow::anyhow!("CLEAR_SCREEN"));
+        } else {
+            Ok(format!("Command received: {}\n(Full Kotlin evaluation not yet implemented)", code))
+        }
+    }
+    
+    pub fn ui(&mut self, ui: &mut egui::Ui) {
+        ui.vertical(|ui| {
+            // Title bar
+            ui.horizontal(|ui| {
+                ui.heading("Kotlin REPL");
+                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+                    if ui.button("Clear").clicked() {
+                        self.output.clear();
+                        self.add_output("Kotlin REPL - Ready", false, false);
+                    }
+                    if ui.button("Help").clicked() {
+                        self.execute("help");
+                    }
+                });
+            });
+            
+            ui.separator();
+            
+            // Output area
+            ScrollArea::vertical()
+                .auto_shrink([false, false])
+                .stick_to_bottom(true)
+                .show(ui, |ui| {
+                    ui.set_min_height(ui.available_height() - 80.0);
+                    
+                    for line in &self.output {
+                        let color = if line.is_error {
+                            Color32::from_rgb(255, 100, 100)
+                        } else if line.is_input {
+                            Color32::from_rgb(100, 200, 255)
+                        } else {
+                            Color32::from_rgb(200, 200, 200)
+                        };
+                        
+                        ui.label(
+                            RichText::new(&line.text)
+                                .color(color)
+                                .font(FontId::monospace(14.0))
+                        );
+                    }
+                });
+            
+            ui.separator();
+            
+            // Input area
+            ui.horizontal(|ui| {
+                ui.label(">>>");
+                
+                let response = ui.add(
+                    TextEdit::singleline(&mut self.input)
+                        .desired_width(f32::INFINITY)
+                        .font(FontId::monospace(14.0))
+                );
+                
+                // Handle keyboard input
+                if response.has_focus() {
+                    if ui.input(|i| i.key_pressed(egui::Key::Enter)) {
+                        let code = self.input.clone();
+                        self.input.clear();
+                        
+                        if code.trim() == "clear" {
+                            self.output.clear();
+                            self.add_output("Kotlin REPL - Ready", false, false);
+                        } else {
+                            self.execute(&code);
+                        }
+                    }
+                    
+                    // History navigation
+                    if ui.input(|i| i.key_pressed(egui::Key::ArrowUp)) {
+                        if !self.history.is_empty() {
+                            if let Some(idx) = self.history_index {
+                                if idx > 0 {
+                                    self.history_index = Some(idx - 1);
+                                    self.input = self.history[idx - 1].clone();
+                                }
+                            } else {
+                                self.history_index = Some(self.history.len() - 1);
+                                self.input = self.history[self.history.len() - 1].clone();
+                            }
+                        }
+                    }
+                    
+                    if ui.input(|i| i.key_pressed(egui::Key::ArrowDown)) {
+                        if let Some(idx) = self.history_index {
+                            if idx < self.history.len() - 1 {
+                                self.history_index = Some(idx + 1);
+                                self.input = self.history[idx + 1].clone();
+                            } else {
+                                self.history_index = None;
+                                self.input.clear();
+                            }
+                        }
+                    }
+                }
+                
+                if ui.button("Execute").clicked() && !self.input.is_empty() {
+                    let code = self.input.clone();
+                    self.input.clear();
+                    self.execute(&code);
+                }
+            });
+            
+            // Tips
+            ui.horizontal(|ui| {
+                ui.label(
+                    RichText::new("Tip: Type 'help' for available commands, 'clear' to clear output. Use ↑↓ for history.")
+                        .size(10.0)
+                        .color(Color32::from_rgb(150, 150, 150))
+                );
+            });
+        });
+    }
+}
diff --git a/src/commonMain/kotlin/com/dropbear/math/Transform.kt b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
index bfa039f..69cec1c 100644
--- a/src/commonMain/kotlin/com/dropbear/math/Transform.kt
+++ b/src/commonMain/kotlin/com/dropbear/math/Transform.kt
@@ -1,9 +1,116 @@
 package com.dropbear.math
 
+import com.dropbear.ffi.NativeEngine
+
 /**
  * A class that keeps all the values of a Transform, which consists of a
  * position ([Vector3D]), rotation([QuaternionD]) and a scale([Vector3D]).
+ * 
+ * This class provides direct access to transform data from the Rust engine.
  */
-class Transform(var position: Vector3D, var rotation: QuaternionD, var scale: Vector3D) {
-
-}
\ No newline at end of file
+class Transform private constructor(
+    private var _position: Vector3D,
+    private var _rotation: QuaternionD,
+    private var _scale: Vector3D
+) {
+    /**
+     * Get the position from the native engine
+     */
+    var position: Vector3D
+        get() {
+            _position = Vector3D(
+                NativeEngine.getPositionX(),
+                NativeEngine.getPositionY(),
+                NativeEngine.getPositionZ()
+            )
+            return _position
+        }
+        set(value) {
+            _position = value
+            NativeEngine.setPosition(value.x, value.y, value.z)
+        }
+    
+    /**
+     * Get the rotation from the native engine
+     */
+    var rotation: QuaternionD
+        get() {
+            _rotation = Quaternion(
+                NativeEngine.getRotationX(),
+                NativeEngine.getRotationY(),
+                NativeEngine.getRotationZ(),
+                NativeEngine.getRotationW()
+            )
+            return _rotation
+        }
+        set(value) {
+            _rotation = value
+            NativeEngine.setRotation(value.x, value.y, value.z, value.w)
+        }
+    
+    /**
+     * Get the scale from the native engine
+     */
+    var scale: Vector3D
+        get() {
+            _scale = Vector3D(
+                NativeEngine.getScaleX(),
+                NativeEngine.getScaleY(),
+                NativeEngine.getScaleZ()
+            )
+            return _scale
+        }
+        set(value) {
+            _scale = value
+            NativeEngine.setScale(value.x, value.y, value.z)
+        }
+    
+    companion object {
+        /**
+         * Creates a new Transform with everything to its default values.
+         * This creates a live Transform that reads from the native engine.
+         */
+        fun default(): Transform {
+            return Transform(
+                Vector3D.zero(),
+                Quaternion.identity(),
+                Vector3D(1.0, 1.0, 1.0)
+            )
+        }
+        
+        /**
+         * Creates a Transform from the current native engine state
+         */
+        fun fromNative(): Transform {
+            val pos = Vector3D(
+                NativeEngine.getPositionX(),
+                NativeEngine.getPositionY(),
+                NativeEngine.getPositionZ()
+            )
+            val rot = Quaternion(
+                NativeEngine.getRotationX(),
+                NativeEngine.getRotationY(),
+                NativeEngine.getRotationZ(),
+                NativeEngine.getRotationW()
+            )
+            val scale = Vector3D(
+                NativeEngine.getScaleX(),
+                NativeEngine.getScaleY(),
+                NativeEngine.getScaleZ()
+            )
+            return Transform(pos, rot, scale)
+        }
+    }
+    
+    /**
+     * Translate (move) the transform by a delta
+     */
+    fun translate(delta: Vector3D) {
+        val current = position
+        position = Vector3D(
+            current.x + delta.x,
+            current.y + delta.y,
+            current.z + delta.z
+        )
+    }
+}
diff --git a/src/main/kotlin/com/dropbear/input/Input.kt b/src/main/kotlin/com/dropbear/input/Input.kt
new file mode 100644
index 0000000..46a6e24
--- /dev/null
+++ b/src/main/kotlin/com/dropbear/input/Input.kt
@@ -0,0 +1,47 @@
+package com.dropbear.input
+
+import com.dropbear.ffi.NativeEngine
+
+/**
+ * High-level input management wrapper for accessing input state
+ * from Kotlin scripts.
+ */
+object Input {
+    /**
+     * Check if a specific key is currently pressed
+     * 
+     * @param keyCode The key to check (use KeyCode constants)
+     * @return true if the key is pressed, false otherwise
+     * 
+     * @example
+     * ```kotlin
+     * if (Input.isKeyPressed(KeyCode.W)) {
+     *     // Move forward
+     * }
+     * ```
+     */
+    fun isKeyPressed(keyCode: Long): Boolean {
+        return NativeEngine.isKeyPressed(keyCode)
+    }
+    
+    /**
+     * Get the current mouse X position
+     */
+    fun getMouseX(): Double {
+        return NativeEngine.getMouseX()
+    }
+    
+    /**
+     * Get the current mouse Y position
+     */
+    fun getMouseY(): Double {
+        return NativeEngine.getMouseY()
+    }
+    
+    /**
+     * Get the mouse position as a pair
+     */
+    fun getMousePosition(): Pair<Double, Double> {
+        return Pair(getMouseX(), getMouseY())
+    }
+}
diff --git a/src/main/kotlin/com/dropbear/input/KeyCode.kt b/src/main/kotlin/com/dropbear/input/KeyCode.kt
new file mode 100644
index 0000000..ca8f9f4
--- /dev/null
+++ b/src/main/kotlin/com/dropbear/input/KeyCode.kt
@@ -0,0 +1,47 @@
+package com.dropbear.input
+
+/**
+ * Key codes that match the Rust KeyCode enum.
+ * These are used with NativeEngine.isKeyPressed()
+ */
+object KeyCode {
+    // Letters
+    const val A = 65L
+    const val B = 66L
+    const val C = 67L
+    const val D = 68L
+    const val E = 69L
+    const val F = 70L
+    const val G = 71L
+    const val H = 72L
+    const val I = 73L
+    const val J = 74L
+    const val K = 75L
+    const val L = 76L
+    const val M = 77L
+    const val N = 78L
+    const val O = 79L
+    const val P = 80L
+    const val Q = 81L
+    const val R = 82L
+    const val S = 83L
+    const val T = 84L
+    const val U = 85L
+    const val V = 86L
+    const val W = 87L
+    const val X = 88L
+    const val Y = 89L
+    const val Z = 90L
+    
+    // Arrow keys
+    const val ARROW_LEFT = 37L
+    const val ARROW_UP = 38L
+    const val ARROW_RIGHT = 39L
+    const val ARROW_DOWN = 40L
+    
+    // Special keys
+    const val SPACE = 32L
+    const val SHIFT = 16L
+    const val CONTROL = 17L
+    const val ESCAPE = 27L
+}