From 6838119689bf3c685660c0b3e8bc1d9f99e98d11 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 16:02:06 -0500 Subject: [PATCH 001/324] Basic repl --- .cargo/config.toml | 2 + .gitattributes | 1 + .github/workflows/rust.yml | 29 +++++ .gitignore | 4 + LICENSE.md | 21 +++ README.md | 0 cargo.toml | 7 + editor/Cargo.toml | 7 + editor/src/main.rs | 18 +++ engine/.gitignore | 1 + engine/Cargo.toml | 16 +++ engine/src/core/commands.rs | 36 ++++++ engine/src/core/mod.rs | 3 + engine/src/core/repl.rs | 248 ++++++++++++++++++++++++++++++++++++ engine/src/core/splash.rs | 26 ++++ engine/src/main.rs | 21 +++ xtask/Cargo.toml | 32 +++++ xtask/src/editor.rs | 1 + xtask/src/engine.rs | 22 ++++ xtask/src/main.rs | 70 ++++++++++ 20 files changed, 565 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitattributes create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 cargo.toml create mode 100644 editor/Cargo.toml create mode 100644 editor/src/main.rs create mode 100644 engine/.gitignore create mode 100644 engine/Cargo.toml create mode 100644 engine/src/core/commands.rs create mode 100644 engine/src/core/mod.rs create mode 100644 engine/src/core/repl.rs create mode 100644 engine/src/core/splash.rs create mode 100644 engine/src/main.rs create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/editor.rs create mode 100644 xtask/src/engine.rs create mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0c612da --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --quiet --package xtask --" \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c690e03 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.obj filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d4c3ccb --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,29 @@ +name: Rust + +on: + push: + branches: [ "main", "master" ] + pull_request: + branches: [ "main", "master" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose + + - name: Check formatting + run: cargo fmt -- --check + + - name: Clippy + run: cargo clippy -- -D warnings \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..583276e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +.idea +Cargo.lock +*.log \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c918216 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +## Copyright (c) 2024 Caznix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.** diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/cargo.toml b/cargo.toml new file mode 100644 index 0000000..5f81e11 --- /dev/null +++ b/cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "2" +members = [ + "engine", + "editor", + "xtask" +] \ No newline at end of file diff --git a/editor/Cargo.toml b/editor/Cargo.toml new file mode 100644 index 0000000..800cdbe --- /dev/null +++ b/editor/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "editor" +version = "0.1.0" +edition = "2021" + +[dependencies] +floem = "0.2.0" diff --git a/editor/src/main.rs b/editor/src/main.rs new file mode 100644 index 0000000..e1a1530 --- /dev/null +++ b/editor/src/main.rs @@ -0,0 +1,18 @@ +use floem::{prelude::*, style::Style}; + +fn main() { + floem::launch(counter_view); +} + +fn counter_view() -> impl IntoView { + let mut counter = RwSignal::new(20); + v_stack({ + label("test") + }).style(Style::d); + h_stack(( + button("Increment").action(move || counter += 1), + label(move || format!("Value: {counter}")), + button("Decrement").action(move || counter -= 1), + )) + .style(|s| s.size_full().items_center().justify_center().gap(10)) +} \ No newline at end of file diff --git a/engine/.gitignore b/engine/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/engine/.gitignore @@ -0,0 +1 @@ +/target diff --git a/engine/Cargo.toml b/engine/Cargo.toml new file mode 100644 index 0000000..b5bc16e --- /dev/null +++ b/engine/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zenyx" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = "0.4.38" +colored = "2.1.0" +lazy_static = "1.5.0" +#log = "0.4.22" +log2 = "0.1.14" +parking_lot = "0.12.3" +reedline = "0.37.0" +regex = "1.11.1" +thiserror = "2.0.3" +tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } diff --git a/engine/src/core/commands.rs b/engine/src/core/commands.rs new file mode 100644 index 0000000..45884ac --- /dev/null +++ b/engine/src/core/commands.rs @@ -0,0 +1,36 @@ +use std::process::Command; + +use log2::{debug, info}; + +use crate::core::repl::COMMAND_LIST; + +pub fn say_hello() { + println!("Hello from your new command!"); +} + +pub fn echo(args: Vec) { + debug!("{}", args.join(" ")); + println!("{}", args.join(" ")) +} + +pub fn exit() { + debug!("Exiting..."); + std::process::exit(0) +} +pub fn clear() { + info!("Clearing screen..., running command"); + let _result = if cfg!(target_os = "windows") { + debug!("target_os is windows"); + Command::new("cmd").args(["/c", "cls"]).spawn() + } else { + debug!("target_os was unix"); + // "clear" or "tput reset" + Command::new("tput").arg("reset").spawn() + }; +} +pub fn cmds() { + println!("Commands:"); + for cmd in COMMAND_LIST.commands.read().iter() { + println!("{:#}", cmd); + } +} diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs new file mode 100644 index 0000000..7c5620e --- /dev/null +++ b/engine/src/core/mod.rs @@ -0,0 +1,3 @@ +pub mod commands; +pub mod repl; +pub mod splash; diff --git a/engine/src/core/repl.rs b/engine/src/core/repl.rs new file mode 100644 index 0000000..899c606 --- /dev/null +++ b/engine/src/core/repl.rs @@ -0,0 +1,248 @@ +use super::commands; +use chrono::Local; +use lazy_static::lazy_static; +use log2::{debug, error, info}; +use parking_lot::RwLock; +use reedline::{Prompt, Reedline, Signal}; +use regex::Regex; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + +struct ZPrompt { + left_text: String, + right_text: String, +} + +#[derive(Clone, Debug)] +enum Callable { + Simple(fn()), + WithArgs(fn(Vec)), +} +#[derive(Debug)] +pub struct Command { + pub name: &'static str, + pub description: Option<&'static str>, + function: Callable, + pub arg_count: u8, +} + +impl Command { + pub fn execute(&self, args: Option>) { + //debug!("Executing command: {}", self.name); + match &self.function { + Callable::Simple(f) => { + if let Some(args) = args { + error!( + "Command expected 0 arguments but {} args were given. Ignoring..", + args.len() + ); + } + f() + } + Callable::WithArgs(f) => match args { + Some(args) => f(args), + None => error!("Command expected arguments but received 0"), + }, + } + } +} + +impl std::fmt::Display for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Name: {}\n\t{}", + self.name, + self.description.unwrap_or("No description") + ) + } +} + +lazy_static! { + pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); +} + +pub struct CommandList { + pub commands: RwLock>, + pub aliases: RwLock>, +} + +impl CommandList { + fn new() -> Self { + CommandList { + commands: RwLock::new(Vec::new()), + aliases: RwLock::new(HashMap::new()), + } + } + + fn add_command( + &self, + name: &'static str, + description: Option<&'static str>, + func: Callable, + arg_count: Option, + ) { + debug!("Adding command: {}", name); + let mut commands = self.commands.write(); + + commands.push(Command { + name, + description, + function: func, + arg_count: arg_count.unwrap_or(0), + }); + } + fn add_alias(&self, name: String, alias: String) { + //println!("Input alias: {}", alias); + if self.aliases.read().contains_key(&alias) { + error!("Alias: '{}' already exists", alias); + return; + } + let mut commands = self.commands.write(); + if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { + info!("Adding alias: {} for cmd: {}", alias, command.name); + self.aliases + .write() + .insert(alias.to_string(), name.to_string()); + } else { + error!("Command: '{}' was not found", name); + } + } + + fn execute_command(&self, mut name: String, args: Option>) { + //info!("received input command: {}", name); + let commands = self.commands.borrow(); + if self.aliases.read().contains_key(&name) { + name = self + .aliases + .read() + .get_key_value(&name) + .unwrap() + .1 + .to_string(); + debug!("changed to {}", name); + } + if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { match (command.arg_count, args.as_ref()) { + (expected, Some(args_vec)) if args_vec.len() != expected as usize => { + eprintln!( + "Command: '{}' expected {} arguments but received {}", + name, + expected, + args_vec.len() + ); + } + (_, _) => command.execute(args), + } } + } +} + +impl Prompt for ZPrompt { + fn render_prompt_left(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.left_text) + } + + fn render_prompt_right(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.right_text) + } + + fn render_prompt_history_search_indicator( + &self, + _history_search: reedline::PromptHistorySearch, + ) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("") + } + + fn render_prompt_indicator( + &self, + prompt_mode: reedline::PromptEditMode, + ) -> std::borrow::Cow { + match prompt_mode { + reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), + reedline::PromptEditMode::Emacs => { + let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); + std::borrow::Cow::Owned(timestamp) + } + reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), + reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), + } + } + + fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("><") + } +} + +fn setup() { + COMMAND_LIST.add_command( + "hello", + Some("test"), + Callable::Simple(commands::say_hello), + None, + ); + COMMAND_LIST.add_command("exit", None, Callable::Simple(commands::exit), None); + COMMAND_LIST.add_command("clear", None, Callable::Simple(commands::clear), None); + COMMAND_LIST.add_command("echo", None, Callable::WithArgs(commands::echo), Some(1)); + COMMAND_LIST.add_command("cmds", None, Callable::Simple(commands::cmds), None); + COMMAND_LIST.add_alias("cmds".to_string(), "help".to_string()); + COMMAND_LIST.add_alias("cmds".to_string(), "cmd_list".to_string()); + COMMAND_LIST.add_alias("hello".to_string(), "exit".to_string()); + COMMAND_LIST.add_alias("clear".to_string(), "exit".to_string()); +} + +pub async fn handle_repl() { + let mut line_editor = Reedline::create(); + setup(); + + loop { + let sig = line_editor.read_line(&ZPrompt { + left_text: String::new(), + right_text: "<<".to_string(), + }); + + match sig { + Ok(Signal::Success(buffer)) => { + if buffer == "exit" { + std::process::exit(0); + } else { + evaluate_command(&buffer); + } + } + Ok(Signal::CtrlC) => { + println!("\nCONTROL+C RECEIVED, TERMINATING"); + std::process::exit(0); + } + err => { + eprintln!("Error: {:?}", err); + } + } + } +} + +fn evaluate_command(input: &str) { + if input.trim().is_empty() { + return; + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(input).collect(); + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens: Vec<&str> = command.split_whitespace().collect(); + if tokens.is_empty() { + return; + } + + let cmd_name = tokens[0]; + let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); + + COMMAND_LIST.execute_command( + cmd_name.to_string(), + if args.is_empty() { None } else { Some(args) }, + ); + } +} diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs new file mode 100644 index 0000000..f35c870 --- /dev/null +++ b/engine/src/core/splash.rs @@ -0,0 +1,26 @@ +use colored::Colorize; + +pub fn print_splash() { + println!( + r#" + &&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&& + && &&&&&&&&& + && &&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&& &&&&&&&&&&&& + &&&&&&&&& && + &&&&&&&&& && + &&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&& + + Version: {} + "#, + env!("CARGO_PKG_VERSION").yellow().italic().underline() + ); +} diff --git a/engine/src/main.rs b/engine/src/main.rs new file mode 100644 index 0000000..b94b1eb --- /dev/null +++ b/engine/src/main.rs @@ -0,0 +1,21 @@ +use std::io; + +use log2::info; + +pub mod core; + +#[tokio::main] +async fn main() -> Result<(), io::Error> { + let _log2 = log2::open("z.log").tee(true).level("trace").start(); + info!("Initalizing Engine"); + let shell_thread = tokio::task::spawn(async { + info!("Shell thread started"); + core::repl::handle_repl().await; + } +); + + core::splash::print_splash(); + info!("Engine Initalized"); + shell_thread.await?; + Ok(()) +} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..7e4ef1a --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + + +[dependencies] +clap = { version = "4.5.20", features = ["derive"] } + +[profile.dev] +rpath = false +panic = "abort" +lto = "off" +opt-level = 0 +debug = false +overflow-checks = false +incremental = true +codegen-units = 256 + +strip = "symbols" +debug-assertions = false + +[profile.dev.package."*"] +opt-level = 0 +debug = false +overflow-checks = false +incremental = true +codegen-units = 256 + +strip = "symbols" +debug-assertions = false + diff --git a/xtask/src/editor.rs b/xtask/src/editor.rs new file mode 100644 index 0000000..8cfa650 --- /dev/null +++ b/xtask/src/editor.rs @@ -0,0 +1 @@ +pub fn build_editor() {} \ No newline at end of file diff --git a/xtask/src/engine.rs b/xtask/src/engine.rs new file mode 100644 index 0000000..ecdf677 --- /dev/null +++ b/xtask/src/engine.rs @@ -0,0 +1,22 @@ +use std::process::Stdio; + + +pub fn build_engine() { + +} + +pub fn build_core() { + let threads = format!("-j{}",std::thread::available_parallelism().unwrap().get()); + let mut run = std::process::Command::new("cargo") + .arg("run") + + .arg(threads) + .arg("--bin") + .arg("zenyx") + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap(); + run.wait().unwrap(); +} \ No newline at end of file diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..b146421 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,70 @@ + +use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; +pub mod engine; +pub mod editor; + +#[derive(Parser)] +#[command(version, about, long_about = None,disable_version_flag = true,disable_help_flag = true)] +struct Cli { + #[arg(short,long)] + release: bool, + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + Run { + #[arg()] + task: Task + }, + Config, + +} +#[derive(Clone,ValueEnum)] +enum Task { + Engine, // Builds both editor and core + Editor, // Builds editor only + Core, // Builds engine core only + Help, +} + + + +fn main() { + let cli = Cli::parse(); + + + + + if cli.release { + println!("Running in release mode") + } + + match &cli.command { + + None => { + Cli::command().print_help().map_err(|e| { + println!("Could not run Xtask: {e}"); + + }).unwrap(); + } + Some(Commands::Run { task }) => { + match task { + Task::Engine => engine::build_engine(), + Task::Editor => todo!("Editor is not being actively worked on"), + Task::Core => { + engine::build_core(); + }, + Task::Help => { + println!("The following options are avalible to run"); + todo!() + }, + } + } + Some(Commands::Config) => { + todo!() + } + } + +} \ No newline at end of file From 93eeec76e9e366a0a0c973d2f1491726f4c46f10 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:06:27 -0500 Subject: [PATCH 002/324] update workflow --- .github/workflows/rust.yml | 32 ++++++++++++++++++++++++-------- editor/Cargo.toml | 2 +- editor/src/main.rs | 17 +---------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d4c3ccb..8c7a87b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,19 +11,35 @@ env: jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + target: x86_64-unknown-linux-gnu + - name: Build - run: cargo build --verbose + run: cargo build --release - name: Run tests run: cargo test --verbose - - - name: Check formatting - run: cargo fmt -- --check - - - name: Clippy - run: cargo clippy -- -D warnings \ No newline at end of file + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ runner.os }}-binary + path: | + target/release/*.exe + target/release/* + !target/release/*.d + !target/release/*.pdb + + \ No newline at end of file diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 800cdbe..f584ecb 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -floem = "0.2.0" + diff --git a/editor/src/main.rs b/editor/src/main.rs index e1a1530..6bd4269 100644 --- a/editor/src/main.rs +++ b/editor/src/main.rs @@ -1,18 +1,3 @@ -use floem::{prelude::*, style::Style}; - fn main() { - floem::launch(counter_view); -} - -fn counter_view() -> impl IntoView { - let mut counter = RwSignal::new(20); - v_stack({ - label("test") - }).style(Style::d); - h_stack(( - button("Increment").action(move || counter += 1), - label(move || format!("Value: {counter}")), - button("Decrement").action(move || counter -= 1), - )) - .style(|s| s.size_full().items_center().justify_center().gap(10)) + println!("editor") } \ No newline at end of file From ebc92f9d4c4257f2416b2e71a9e780bca99eb230 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:09:41 -0500 Subject: [PATCH 003/324] test new workflow --- cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cargo.toml b/cargo.toml index 5f81e11..f2fa59a 100644 --- a/cargo.toml +++ b/cargo.toml @@ -3,5 +3,5 @@ resolver = "2" members = [ "engine", "editor", - "xtask" + "xtask", ] \ No newline at end of file From b998f6cd842ba36fcd5412d1e0dc36950ebd7401 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Sun, 1 Dec 2024 18:10:14 -0500 Subject: [PATCH 004/324] Rename cargo.toml to Cargo.toml --- cargo.toml => Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename cargo.toml => Cargo.toml (97%) diff --git a/cargo.toml b/Cargo.toml similarity index 97% rename from cargo.toml rename to Cargo.toml index f2fa59a..63ba671 100644 --- a/cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ members = [ "engine", "editor", "xtask", -] \ No newline at end of file +] From 7ea61749771e7c0cd3a730554be12ca28300ea26 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:23:20 -0500 Subject: [PATCH 005/324] add multiple architectures --- .github/workflows/rust.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8c7a87b..3db2488 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,6 +14,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] + arch: [x86_64, aarch64] + include: + - arch: x86_64 + target: x86_64-unknown-linux-gnu + - arch: aarch64 + target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.os }} steps: @@ -24,22 +30,18 @@ jobs: with: toolchain: stable override: true - target: x86_64-unknown-linux-gnu + target: ${{ matrix.target }} - name: Build - run: cargo build --release + run: cargo build --release --target ${{ matrix.target }} - name: Run tests - run: cargo test --verbose + run: cargo test --verbose --target ${{ matrix.target }} - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ${{ runner.os }}-binary + name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary path: | - target/release/*.exe - target/release/* - !target/release/*.d - !target/release/*.pdb - - \ No newline at end of file + target/${{ matrix.target }}/release/*.exe + target/${{ matrix.target }}/release/*[^.]* \ No newline at end of file From 57257ee90ac4e0afa948ad7ce4f04bbeedb9de6b Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:27:09 -0500 Subject: [PATCH 006/324] fix foreign architectures --- .github/workflows/rust.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3db2488..5624de5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,11 +32,21 @@ jobs: override: true target: ${{ matrix.target }} + - name: Install cross-compilation tools + if: matrix.arch == 'aarch64' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + - name: Build run: cargo build --release --target ${{ matrix.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Run tests run: cargo test --verbose --target ${{ matrix.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Upload artifacts uses: actions/upload-artifact@v3 From d64b8c56521a37e9e9824105a621301673d18748 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:32:55 -0500 Subject: [PATCH 007/324] fix cross compilation? --- .github/workflows/rust.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5624de5..c403fc4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,11 +15,19 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] arch: [x86_64, aarch64] + exclude: + - os: windows-latest + arch: aarch64 include: - - arch: x86_64 + - os: ubuntu-latest + arch: x86_64 target: x86_64-unknown-linux-gnu - - arch: aarch64 + - os: ubuntu-latest + arch: aarch64 target: aarch64-unknown-linux-gnu + - os: windows-latest + arch: x86_64 + target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: @@ -33,7 +41,7 @@ jobs: target: ${{ matrix.target }} - name: Install cross-compilation tools - if: matrix.arch == 'aarch64' + if: matrix.arch == 'aarch64' && matrix.os == 'ubuntu-latest' run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu @@ -51,7 +59,5 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary - path: | - target/${{ matrix.target }}/release/*.exe - target/${{ matrix.target }}/release/*[^.]* \ No newline at end of file + name: Zenyx-${{ matrix.os == 'windows-latest' && 'windows' || 'linux' }}-${{ matrix.arch }}-binary + path: target/${{ matrix.target }}/release/${{ matrix.os == 'windows-latest' && '*.exe' || '*[^.]*' }} \ No newline at end of file From 3da89d4e9bd7a47fcc97bf51840a8bc87b62a49a Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:41:00 -0500 Subject: [PATCH 008/324] maybe arm? --- .github/workflows/rust.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c403fc4..449b859 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,18 +16,18 @@ jobs: os: [ubuntu-latest, windows-latest] arch: [x86_64, aarch64] exclude: - - os: windows-latest + - os: ubuntu-latest arch: aarch64 include: - os: ubuntu-latest arch: x86_64 target: x86_64-unknown-linux-gnu - - os: ubuntu-latest - arch: aarch64 - target: aarch64-unknown-linux-gnu - os: windows-latest arch: x86_64 - target: x86_64-pc-windows-msvc + target: x86_64-pc-windows-gnu + - os: windows-latest + arch: aarch64 + target: aarch64-pc-windows-gnu runs-on: ${{ matrix.os }} steps: @@ -52,6 +52,7 @@ jobs: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Run tests + if: matrix.arch != 'aarch64' run: cargo test --verbose --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc From 59d8e893faae5f71005645be966f9e98b9d2266c Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:43:33 -0500 Subject: [PATCH 009/324] nvm --- .github/workflows/rust.yml | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 449b859..1ba865c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,21 +13,14 @@ jobs: build: strategy: matrix: - os: [ubuntu-latest, windows-latest] - arch: [x86_64, aarch64] - exclude: - - os: ubuntu-latest - arch: aarch64 + os: [ubuntu-latest, windows-latest, macos-latest] + arch: [x86_64] include: - - os: ubuntu-latest - arch: x86_64 + - arch: x86_64 target: x86_64-unknown-linux-gnu - - os: windows-latest + - os: macos-latest arch: x86_64 - target: x86_64-pc-windows-gnu - - os: windows-latest - arch: aarch64 - target: aarch64-pc-windows-gnu + target: x86_64-apple-darwin runs-on: ${{ matrix.os }} steps: @@ -40,25 +33,16 @@ jobs: override: true target: ${{ matrix.target }} - - name: Install cross-compilation tools - if: matrix.arch == 'aarch64' && matrix.os == 'ubuntu-latest' - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - - name: Build run: cargo build --release --target ${{ matrix.target }} - env: - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Run tests - if: matrix.arch != 'aarch64' run: cargo test --verbose --target ${{ matrix.target }} - env: - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: Zenyx-${{ matrix.os == 'windows-latest' && 'windows' || 'linux' }}-${{ matrix.arch }}-binary - path: target/${{ matrix.target }}/release/${{ matrix.os == 'windows-latest' && '*.exe' || '*[^.]*' }} \ No newline at end of file + name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary + path: | + target/${{ matrix.target }}/release/*.exe + target/${{ matrix.target }}/release/*[^.]* \ No newline at end of file From 15e58a29ab8643845ee7d651003fb9979091bbc8 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:47:33 -0500 Subject: [PATCH 010/324] only include binaries --- .github/workflows/rust.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1ba865c..641ed49 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,13 +14,18 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - arch: [x86_64] + arch: [x86_64, aarch64] include: - arch: x86_64 target: x86_64-unknown-linux-gnu - os: macos-latest arch: x86_64 target: x86_64-apple-darwin + - arch: aarch64 + target: aarch64-unknown-linux-gnu + - os: macos-latest + arch: aarch64 + target: aarch64-apple-darwin runs-on: ${{ matrix.os }} steps: @@ -43,6 +48,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary - path: | - target/${{ matrix.target }}/release/*.exe - target/${{ matrix.target }}/release/*[^.]* \ No newline at end of file + path: target/${{ matrix.target }}/release/zenyx* \ No newline at end of file From 997194200bf87c0a1c6ccbb9b6db31852c4ae025 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:50:47 -0500 Subject: [PATCH 011/324] cross tools --- .github/workflows/rust.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 641ed49..b73e3ad 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,6 +31,17 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install cross-compilation dependencies (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + + - name: Install cross-compilation dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew install FiloSottile/musl-cross/musl-cross + - name: Install Rust uses: actions-rs/toolchain@v1 with: From f1a98dd4ef021d94f33242aae2153ac411a24882 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:53:22 -0500 Subject: [PATCH 012/324] idfk at this point --- .github/workflows/rust.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b73e3ad..91c3546 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,6 +26,9 @@ jobs: - os: macos-latest arch: aarch64 target: aarch64-apple-darwin + exclude: + - os: windows-latest + arch: aarch64 runs-on: ${{ matrix.os }} steps: @@ -48,12 +51,23 @@ jobs: toolchain: stable override: true target: ${{ matrix.target }} + profile: minimal - name: Build - run: cargo build --release --target ${{ matrix.target }} + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{ matrix.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Run tests - run: cargo test --verbose --target ${{ matrix.target }} + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --target ${{ matrix.target }} + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: Upload artifacts uses: actions/upload-artifact@v3 From 5f7841aacb1a9c039397d80ca11fa759dc895d00 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 18:54:26 -0500 Subject: [PATCH 013/324] disable fail-fast --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 91c3546..7a39039 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -12,6 +12,7 @@ env: jobs: build: strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] arch: [x86_64, aarch64] From 2137bccbf8d0364ac6e38917e828afce0fda0d52 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:01:09 -0500 Subject: [PATCH 014/324] run aarch64 in qemu??? im running out of ideas --- .github/workflows/rust.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7a39039..121fa46 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,6 +27,9 @@ jobs: - os: macos-latest arch: aarch64 target: aarch64-apple-darwin + - os: windows-latest + arch: x86_64 + target: x86_64-pc-windows-msvc exclude: - os: windows-latest arch: aarch64 @@ -39,7 +42,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user - name: Install cross-compilation dependencies (macOS) if: runner.os == 'macOS' @@ -69,6 +72,7 @@ jobs: args: --verbose --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + QEMU_LD_PREFIX: /usr/aarch64-linux-gnu - name: Upload artifacts uses: actions/upload-artifact@v3 From 86eadc4fb28de2fe75efc48569d1d431badcc091 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:35:48 -0500 Subject: [PATCH 015/324] seperate jobs --- .github/workflows/rust.yml | 78 +++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 121fa46..d127464 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,4 +1,4 @@ -name: Rust +name: Compile & test Zenyx โšก on: push: @@ -36,20 +36,21 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - name: ๐Ÿ“ฅ Clone repository + uses: actions/checkout@v3 - - name: Install cross-compilation dependencies (Ubuntu) + - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (Ubuntu๐Ÿง) if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user - - name: Install cross-compilation dependencies (macOS) + - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (macOS๐ŸŽ) if: runner.os == 'macOS' run: | brew install FiloSottile/musl-cross/musl-cross - - name: Install Rust + - name: ๐Ÿฆ€ Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -57,25 +58,74 @@ jobs: target: ${{ matrix.target }} profile: minimal - - name: Build + - name: ๐Ÿ—๏ธ Build uses: actions-rs/cargo@v1 with: command: build args: --release --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + + - name: ๐Ÿ“ฆ Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary + path: target/${{ matrix.target }}/release/zenyx* + + test: + needs: build + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + arch: [x86_64, aarch64] + include: + - arch: x86_64 + target: x86_64-unknown-linux-gnu + - os: macos-latest + arch: x86_64 + target: x86_64-apple-darwin + - arch: aarch64 + target: aarch64-unknown-linux-gnu + - os: macos-latest + arch: aarch64 + target: aarch64-apple-darwin + - os: windows-latest + arch: x86_64 + target: x86_64-pc-windows-msvc + exclude: + - os: windows-latest + arch: aarch64 + runs-on: ${{ matrix.os }} + + steps: + - name: ๐Ÿ“ฅ Clone repository + uses: actions/checkout@v3 + + - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (Ubuntu๐Ÿง) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user + + - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (macOS๐ŸŽ) + if: runner.os == 'macOS' + run: | + brew install FiloSottile/musl-cross/musl-cross + + - name: ๐Ÿฆ€ Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + target: ${{ matrix.target }} + profile: minimal - - name: Run tests + - name: ๐Ÿงช Run tests uses: actions-rs/cargo@v1 with: command: test args: --verbose --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - QEMU_LD_PREFIX: /usr/aarch64-linux-gnu - - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary - path: target/${{ matrix.target }}/release/zenyx* \ No newline at end of file + QEMU_LD_PREFIX: /usr/aarch64-linux-gnu \ No newline at end of file From 9679c8bc5fbc43ddc5780160353b9c97fd1f404b Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:45:15 -0500 Subject: [PATCH 016/324] un seperate them cuz they compile twice? --- .github/workflows/rust.yml | 69 ++++++-------------------------------- 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d127464..1e905cd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,4 +1,4 @@ -name: Compile & test Zenyx โšก +name: Build Zenyx โšก on: push: @@ -39,7 +39,7 @@ jobs: - name: ๐Ÿ“ฅ Clone repository uses: actions/checkout@v3 - - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (Ubuntu๐Ÿง) + - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (Ubuntu) if: runner.os == 'Linux' run: | sudo apt-get update @@ -50,7 +50,7 @@ jobs: run: | brew install FiloSottile/musl-cross/musl-cross - - name: ๐Ÿฆ€ Install Rust + - name: ๐Ÿ”ง Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -65,61 +65,6 @@ jobs: args: --release --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - - - name: ๐Ÿ“ฆ Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary - path: target/${{ matrix.target }}/release/zenyx* - - test: - needs: build - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - arch: [x86_64, aarch64] - include: - - arch: x86_64 - target: x86_64-unknown-linux-gnu - - os: macos-latest - arch: x86_64 - target: x86_64-apple-darwin - - arch: aarch64 - target: aarch64-unknown-linux-gnu - - os: macos-latest - arch: aarch64 - target: aarch64-apple-darwin - - os: windows-latest - arch: x86_64 - target: x86_64-pc-windows-msvc - exclude: - - os: windows-latest - arch: aarch64 - runs-on: ${{ matrix.os }} - - steps: - - name: ๐Ÿ“ฅ Clone repository - uses: actions/checkout@v3 - - - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (Ubuntu๐Ÿง) - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user - - - name: ๐Ÿ› ๏ธ Install cross-compilation dependencies (macOS๐ŸŽ) - if: runner.os == 'macOS' - run: | - brew install FiloSottile/musl-cross/musl-cross - - - name: ๐Ÿฆ€ Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - target: ${{ matrix.target }} - profile: minimal - name: ๐Ÿงช Run tests uses: actions-rs/cargo@v1 @@ -128,4 +73,10 @@ jobs: args: --verbose --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - QEMU_LD_PREFIX: /usr/aarch64-linux-gnu \ No newline at end of file + QEMU_LD_PREFIX: /usr/aarch64-linux-gnu + + - name: ๐Ÿ“ฆ Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary + path: target/${{ matrix.target }}/release/zenyx* \ No newline at end of file From 3d2cc9367b434f9e5b4a74f733ed5ef39ffbb71c Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:46:45 -0500 Subject: [PATCH 017/324] rename output file to bin instead of binary --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1e905cd..9eecf59 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -78,5 +78,5 @@ jobs: - name: ๐Ÿ“ฆ Upload artifacts uses: actions/upload-artifact@v3 with: - name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-binary + name: Zenyx-${{ runner.os }}-${{ matrix.arch }}-bin path: target/${{ matrix.target }}/release/zenyx* \ No newline at end of file From d3b34ac1d5ac531ec3d5c2f66046c36978b71348 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:53:17 -0500 Subject: [PATCH 018/324] test of github actions will actually use aarch64 this time --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9eecf59..c2d600e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,9 +30,9 @@ jobs: - os: windows-latest arch: x86_64 target: x86_64-pc-windows-msvc - exclude: - os: windows-latest arch: aarch64 + target: aarch64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: From e4cfe09a339c6764f8fb229de09c55d508695c1a Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 19:58:26 -0500 Subject: [PATCH 019/324] =?UTF-8?q?no=20windows=20on=20arm=20=F0=9F=98=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c2d600e..9eecf59 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,9 +30,9 @@ jobs: - os: windows-latest arch: x86_64 target: x86_64-pc-windows-msvc + exclude: - os: windows-latest arch: aarch64 - target: aarch64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: From ef455582726c79e08211bd7390b8f675ac685fbf Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 20:50:11 -0500 Subject: [PATCH 020/324] WINIT WINDOW!!!! --- engine/Cargo.toml | 3 +++ engine/src/core/mod.rs | 13 ++++++++++ engine/src/core/renderer/mod.rs | 43 +++++++++++++++++++++++++++++++++ engine/src/main.rs | 5 ++-- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 engine/src/core/renderer/mod.rs diff --git a/engine/Cargo.toml b/engine/Cargo.toml index b5bc16e..d9f748d 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = "1.0.93" chrono = "0.4.38" colored = "2.1.0" lazy_static = "1.5.0" @@ -14,3 +15,5 @@ reedline = "0.37.0" regex = "1.11.1" thiserror = "2.0.3" tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } +wgpu = "23.0.1" +winit = "0.30.5" diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index 7c5620e..deea4b7 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,3 +1,16 @@ pub mod commands; pub mod repl; pub mod splash; +pub mod renderer; +use anyhow::Result; +use renderer::App; +use winit::event_loop::{ControlFlow, EventLoop}; + + + +pub fn init_render() -> Result<()> { + let event_loop = EventLoop::new().unwrap(); + event_loop.set_control_flow(ControlFlow::Poll); + let mut app = App::default(); + Ok(event_loop.run_app(&mut app)?) +} \ No newline at end of file diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs new file mode 100644 index 0000000..791f600 --- /dev/null +++ b/engine/src/core/renderer/mod.rs @@ -0,0 +1,43 @@ +use log2::{debug, error}; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; +use winit::window::{Window, WindowId}; + +#[derive(Default)] +pub struct App { + window: Option, +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + if self.window.is_none() { + let win_attr = Window::default_attributes().with_title("Zenyx"); + let window = event_loop + .create_window(win_attr) + .expect("create window err."); + self.window = Some(window); + } + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); + debug!("Window closed, exiting"); + std::process::exit(0) + } + WindowEvent::Resized(size) => { + let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); + //self.window.as_ref().unwrap().set_title(&format!("you reszed the window to {size_str}")); + debug!("Window resized to {:?}", size_str); + } + _ => error!("Unhandled window event"), + } + } +} \ No newline at end of file diff --git a/engine/src/main.rs b/engine/src/main.rs index b94b1eb..c5e5729 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,11 +1,11 @@ -use std::io; +use anyhow::Result; use log2::info; pub mod core; #[tokio::main] -async fn main() -> Result<(), io::Error> { +async fn main() -> Result<()> { let _log2 = log2::open("z.log").tee(true).level("trace").start(); info!("Initalizing Engine"); let shell_thread = tokio::task::spawn(async { @@ -16,6 +16,7 @@ async fn main() -> Result<(), io::Error> { core::splash::print_splash(); info!("Engine Initalized"); + core::init_render()?; shell_thread.await?; Ok(()) } From 7ceaa5e177f877d533a817f737a40709ac61a819 Mon Sep 17 00:00:00 2001 From: Caznix Date: Sun, 1 Dec 2024 22:58:52 -0500 Subject: [PATCH 021/324] draw with wgpu --- engine/src/core/mod.rs | 2 +- engine/src/core/renderer/ctx.rs | 100 ++++++++++++++++++++++++++++++++ engine/src/core/renderer/mod.rs | 35 +++++++---- engine/src/main.rs | 8 +-- 4 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 engine/src/core/renderer/ctx.rs diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index deea4b7..cebc91e 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -8,7 +8,7 @@ use winit::event_loop::{ControlFlow, EventLoop}; -pub fn init_render() -> Result<()> { +pub fn init_renderer() -> Result<()> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs new file mode 100644 index 0000000..690ed0b --- /dev/null +++ b/engine/src/core/renderer/ctx.rs @@ -0,0 +1,100 @@ +use std::sync::Arc; + +use anyhow::Result; +use tokio::task::spawn_blocking; +use winit::window::Window; + +pub struct WgpuCtx<'window> { + device: wgpu::Device, + queue: wgpu::Queue, + surface: wgpu::Surface<'window>, + surface_config: wgpu::SurfaceConfiguration, + adapter: wgpu::Adapter, +} + +impl<'window> WgpuCtx<'window> { + pub async fn new(window: Arc) -> Result> { + let instnace = wgpu::Instance::default(); + let surface = instnace.create_surface(Arc::clone(&window))?; + let adapter = instnace + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, + compatible_surface: Some(&surface), + }) + .await + .expect("Failed to obtain render adapter"); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::downlevel_webgl2_defaults() + .using_resolution(adapter.limits()), + memory_hints: wgpu::MemoryHints::Performance, + }, + None, + ) + .await + .expect("Failed to create rendering device"); + + let size = window.inner_size(); + let width = size.width.max(1); + let height = size.height.max(1); + + let surface_config = surface.get_default_config(&adapter, width, height).unwrap(); + surface.configure(&device, &surface_config); + + Ok(WgpuCtx { + device: device, + queue: queue, + surface: surface, + surface_config: surface_config, + adapter: adapter, + }) + } + pub fn new_blocking(window: Arc) -> Result> { + tokio::task::block_in_place(|| { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { WgpuCtx::new(window).await }) + }) + } + pub fn resize(&mut self, new_size: (u32, u32)) { + let (width, height) = new_size; + self.surface_config.width = width.max(1); + self.surface_config.height = height.max(1); + self.surface.configure(&self.device, &self.surface_config); + } + pub fn draw(&mut self) { + let surface_texture = self + .surface + .get_current_texture() + .expect("Failed to get surface texture"); + let view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + } + self.queue.submit(Some(encoder.finish())); + surface_texture.present(); + } +} diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs index 791f600..c6fcdab 100644 --- a/engine/src/core/renderer/mod.rs +++ b/engine/src/core/renderer/mod.rs @@ -1,22 +1,28 @@ -use log2::{debug, error}; +use std::sync::Arc; + +use ctx::WgpuCtx; +use log2::{debug, error, trace}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; -use winit::window::{Window, WindowId}; - +use winit::window::{self, Window, WindowId}; +pub mod ctx; #[derive(Default)] -pub struct App { - window: Option, +pub struct App<'window> { + window: Option>, + ctx: Option>, } -impl ApplicationHandler for App { +impl ApplicationHandler for App<'_> { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.window.is_none() { let win_attr = Window::default_attributes().with_title("Zenyx"); - let window = event_loop + let window = Arc::new(event_loop .create_window(win_attr) - .expect("create window err."); - self.window = Some(window); + .expect("create window err.")); + self.window = Some(window.clone()); + let wgpu_ctx = WgpuCtx::new_blocking(window.clone()).unwrap(); + self.ctx = Some(wgpu_ctx) } } @@ -32,12 +38,21 @@ impl ApplicationHandler for App { debug!("Window closed, exiting"); std::process::exit(0) } + WindowEvent::RedrawRequested => { + if let Some(ctx) = &mut self.ctx { + ctx.draw(); + } + } WindowEvent::Resized(size) => { + if let (Some(wgpu_ctx),Some(window)) = (&mut self.ctx, &self.window) { + wgpu_ctx.resize(size.into()); + window.request_redraw(); let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); //self.window.as_ref().unwrap().set_title(&format!("you reszed the window to {size_str}")); debug!("Window resized to {:?}", size_str); } - _ => error!("Unhandled window event"), + } + _ => trace!("Unhandled window event"), } } } \ No newline at end of file diff --git a/engine/src/main.rs b/engine/src/main.rs index c5e5729..5baa920 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,4 +1,3 @@ - use anyhow::Result; use log2::info; @@ -6,17 +5,16 @@ pub mod core; #[tokio::main] async fn main() -> Result<()> { - let _log2 = log2::open("z.log").tee(true).level("trace").start(); + let _log2 = log2::open("z.log").tee(true).level("debug").start(); info!("Initalizing Engine"); let shell_thread = tokio::task::spawn(async { info!("Shell thread started"); core::repl::handle_repl().await; - } -); + }); core::splash::print_splash(); info!("Engine Initalized"); - core::init_render()?; + core::init_renderer()?; shell_thread.await?; Ok(()) } From 3ea44c94295b9cd0c1eb45ff6cf3eb43c952d572 Mon Sep 17 00:00:00 2001 From: Jason Spalti Date: Sun, 1 Dec 2024 23:52:12 -0600 Subject: [PATCH 022/324] Add descriptions to commands and improve REPL display formatting --- engine/src/core/mod.rs | 10 +- engine/src/core/repl.rs | 248 ------------------------- engine/src/core/{ => repl}/commands.rs | 20 +- engine/src/core/repl/mod.rs | 135 ++++++++++++++ engine/src/core/repl/repl.rs | 146 +++++++++++++++ engine/src/core/splash.rs | 26 --- engine/src/main.rs | 30 ++- 7 files changed, 323 insertions(+), 292 deletions(-) delete mode 100644 engine/src/core/repl.rs rename engine/src/core/{ => repl}/commands.rs (72%) create mode 100644 engine/src/core/repl/mod.rs create mode 100644 engine/src/core/repl/repl.rs delete mode 100644 engine/src/core/splash.rs diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index cebc91e..fe2a8cd 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,16 +1,14 @@ -pub mod commands; -pub mod repl; -pub mod splash; pub mod renderer; +pub mod repl; + use anyhow::Result; use renderer::App; use winit::event_loop::{ControlFlow, EventLoop}; - - pub fn init_renderer() -> Result<()> { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); Ok(event_loop.run_app(&mut app)?) -} \ No newline at end of file +} + diff --git a/engine/src/core/repl.rs b/engine/src/core/repl.rs deleted file mode 100644 index 899c606..0000000 --- a/engine/src/core/repl.rs +++ /dev/null @@ -1,248 +0,0 @@ -use super::commands; -use chrono::Local; -use lazy_static::lazy_static; -use log2::{debug, error, info}; -use parking_lot::RwLock; -use reedline::{Prompt, Reedline, Signal}; -use regex::Regex; -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; - -struct ZPrompt { - left_text: String, - right_text: String, -} - -#[derive(Clone, Debug)] -enum Callable { - Simple(fn()), - WithArgs(fn(Vec)), -} -#[derive(Debug)] -pub struct Command { - pub name: &'static str, - pub description: Option<&'static str>, - function: Callable, - pub arg_count: u8, -} - -impl Command { - pub fn execute(&self, args: Option>) { - //debug!("Executing command: {}", self.name); - match &self.function { - Callable::Simple(f) => { - if let Some(args) = args { - error!( - "Command expected 0 arguments but {} args were given. Ignoring..", - args.len() - ); - } - f() - } - Callable::WithArgs(f) => match args { - Some(args) => f(args), - None => error!("Command expected arguments but received 0"), - }, - } - } -} - -impl std::fmt::Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Name: {}\n\t{}", - self.name, - self.description.unwrap_or("No description") - ) - } -} - -lazy_static! { - pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); -} - -pub struct CommandList { - pub commands: RwLock>, - pub aliases: RwLock>, -} - -impl CommandList { - fn new() -> Self { - CommandList { - commands: RwLock::new(Vec::new()), - aliases: RwLock::new(HashMap::new()), - } - } - - fn add_command( - &self, - name: &'static str, - description: Option<&'static str>, - func: Callable, - arg_count: Option, - ) { - debug!("Adding command: {}", name); - let mut commands = self.commands.write(); - - commands.push(Command { - name, - description, - function: func, - arg_count: arg_count.unwrap_or(0), - }); - } - fn add_alias(&self, name: String, alias: String) { - //println!("Input alias: {}", alias); - if self.aliases.read().contains_key(&alias) { - error!("Alias: '{}' already exists", alias); - return; - } - let mut commands = self.commands.write(); - if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { - info!("Adding alias: {} for cmd: {}", alias, command.name); - self.aliases - .write() - .insert(alias.to_string(), name.to_string()); - } else { - error!("Command: '{}' was not found", name); - } - } - - fn execute_command(&self, mut name: String, args: Option>) { - //info!("received input command: {}", name); - let commands = self.commands.borrow(); - if self.aliases.read().contains_key(&name) { - name = self - .aliases - .read() - .get_key_value(&name) - .unwrap() - .1 - .to_string(); - debug!("changed to {}", name); - } - if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { match (command.arg_count, args.as_ref()) { - (expected, Some(args_vec)) if args_vec.len() != expected as usize => { - eprintln!( - "Command: '{}' expected {} arguments but received {}", - name, - expected, - args_vec.len() - ); - } - (_, _) => command.execute(args), - } } - } -} - -impl Prompt for ZPrompt { - fn render_prompt_left(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.left_text) - } - - fn render_prompt_right(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.right_text) - } - - fn render_prompt_history_search_indicator( - &self, - _history_search: reedline::PromptHistorySearch, - ) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("") - } - - fn render_prompt_indicator( - &self, - prompt_mode: reedline::PromptEditMode, - ) -> std::borrow::Cow { - match prompt_mode { - reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), - reedline::PromptEditMode::Emacs => { - let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); - std::borrow::Cow::Owned(timestamp) - } - reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), - reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), - } - } - - fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("><") - } -} - -fn setup() { - COMMAND_LIST.add_command( - "hello", - Some("test"), - Callable::Simple(commands::say_hello), - None, - ); - COMMAND_LIST.add_command("exit", None, Callable::Simple(commands::exit), None); - COMMAND_LIST.add_command("clear", None, Callable::Simple(commands::clear), None); - COMMAND_LIST.add_command("echo", None, Callable::WithArgs(commands::echo), Some(1)); - COMMAND_LIST.add_command("cmds", None, Callable::Simple(commands::cmds), None); - COMMAND_LIST.add_alias("cmds".to_string(), "help".to_string()); - COMMAND_LIST.add_alias("cmds".to_string(), "cmd_list".to_string()); - COMMAND_LIST.add_alias("hello".to_string(), "exit".to_string()); - COMMAND_LIST.add_alias("clear".to_string(), "exit".to_string()); -} - -pub async fn handle_repl() { - let mut line_editor = Reedline::create(); - setup(); - - loop { - let sig = line_editor.read_line(&ZPrompt { - left_text: String::new(), - right_text: "<<".to_string(), - }); - - match sig { - Ok(Signal::Success(buffer)) => { - if buffer == "exit" { - std::process::exit(0); - } else { - evaluate_command(&buffer); - } - } - Ok(Signal::CtrlC) => { - println!("\nCONTROL+C RECEIVED, TERMINATING"); - std::process::exit(0); - } - err => { - eprintln!("Error: {:?}", err); - } - } - } -} - -fn evaluate_command(input: &str) { - if input.trim().is_empty() { - return; - } - - let pattern = Regex::new(r"[;|\n]").unwrap(); - let commands: Vec<&str> = pattern.split(input).collect(); - - for command in commands { - let command = command.trim(); - if command.is_empty() { - println!("Empty command, skipping."); - continue; - } - - let tokens: Vec<&str> = command.split_whitespace().collect(); - if tokens.is_empty() { - return; - } - - let cmd_name = tokens[0]; - let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); - - COMMAND_LIST.execute_command( - cmd_name.to_string(), - if args.is_empty() { None } else { Some(args) }, - ); - } -} diff --git a/engine/src/core/commands.rs b/engine/src/core/repl/commands.rs similarity index 72% rename from engine/src/core/commands.rs rename to engine/src/core/repl/commands.rs index 45884ac..c4d2437 100644 --- a/engine/src/core/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,34 +1,34 @@ +use super::COMMAND_LIST; use std::process::Command; - use log2::{debug, info}; -use crate::core::repl::COMMAND_LIST; - -pub fn say_hello() { - println!("Hello from your new command!"); +pub(crate) fn say_hello() { + println!("Hello, World!"); } -pub fn echo(args: Vec) { +pub(crate) fn echo(args: Vec) { debug!("{}", args.join(" ")); println!("{}", args.join(" ")) } -pub fn exit() { +pub(crate) fn exit() { debug!("Exiting..."); std::process::exit(0) } -pub fn clear() { + +pub(crate) fn clear() { info!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { debug!("target_os is windows"); Command::new("cmd").args(["/c", "cls"]).spawn() } else { - debug!("target_os was unix"); + debug!("target_os is unix"); // "clear" or "tput reset" Command::new("tput").arg("reset").spawn() }; } -pub fn cmds() { + +pub(crate) fn help() { println!("Commands:"); for cmd in COMMAND_LIST.commands.read().iter() { println!("{:#}", cmd); diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs new file mode 100644 index 0000000..6a0ad4c --- /dev/null +++ b/engine/src/core/repl/mod.rs @@ -0,0 +1,135 @@ +pub mod commands; +pub mod repl; + +use lazy_static::lazy_static; +use log2::{debug, error, info}; +use parking_lot::RwLock; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + +lazy_static! { + pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); +} + +#[derive(Clone, Debug)] +enum Callable { + Simple(fn()), + WithArgs(fn(Vec)), +} + +#[derive(Debug)] +pub struct Command { + pub name: &'static str, + pub description: Option<&'static str>, + function: Callable, + pub arg_count: u8, +} + +impl Command { + pub fn execute(&self, args: Option>) { + //debug!("Executing command: {}", self.name); + match &self.function { + Callable::Simple(f) => { + if let Some(args) = args { + error!( + "Command expected 0 arguments but {} args were given. Ignoring..", + args.len() + ); + } + f() + } + Callable::WithArgs(f) => match args { + Some(args) => f(args), + None => error!("Command expected arguments but received 0"), + }, + } + } +} + +impl std::fmt::Display for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + " {:<10} {}", + self.name, + self.description.unwrap_or("No description available") + ) + } +} + +pub struct CommandList { + pub commands: RwLock>, + pub aliases: RwLock>, +} + +impl CommandList { + fn new() -> Self { + CommandList { + commands: RwLock::new(Vec::new()), + aliases: RwLock::new(HashMap::new()), + } + } + + fn add_command( + &self, + name: &'static str, + description: Option<&'static str>, + func: Callable, + arg_count: Option, + ) { + debug!("Adding command: {}", name); + let mut commands = self.commands.write(); + + commands.push(Command { + name, + description, + function: func, + arg_count: arg_count.unwrap_or(0), + }); + } + + fn add_alias(&self, name: String, alias: String) { + //println!("Input alias: {}", alias); + if self.aliases.read().contains_key(&alias) { + error!("Alias: '{}' already exists", alias); + return; + } + let mut commands = self.commands.write(); + if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { + info!("Adding alias: {} for cmd: {}", alias, command.name); + self.aliases + .write() + .insert(alias.to_string(), name.to_string()); + } else { + error!("Command: '{}' was not found", name); + } + } + + fn execute_command(&self, mut name: String, args: Option>) { + //info!("received input command: {}", name); + let commands = self.commands.borrow(); + if self.aliases.read().contains_key(&name) { + name = self + .aliases + .read() + .get_key_value(&name) + .unwrap() + .1 + .to_string(); + + debug!("changed to {}", name); + } + if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { + match (command.arg_count, args.as_ref()) { + (expected, Some(args_vec)) if args_vec.len() != expected as usize => { + eprintln!( + "Command: '{}' expected {} arguments but received {}", + name, + expected, + args_vec.len() + ); + } + (_, _) => command.execute(args), + } + } + } +} diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs new file mode 100644 index 0000000..12c5667 --- /dev/null +++ b/engine/src/core/repl/repl.rs @@ -0,0 +1,146 @@ +use super::{commands, Callable, COMMAND_LIST}; +use chrono::Local; +use reedline::{Prompt, Reedline, Signal}; +use regex::Regex; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + +fn register_commands() { + COMMAND_LIST.add_command( + "hello", + Some("Displays \"Hello World\"!"), + Callable::Simple(commands::say_hello), + None, + ); + + COMMAND_LIST.add_command( + "exit", + Some("Exits the application gracefully."), + Callable::Simple(commands::exit), + None, + ); + + COMMAND_LIST.add_command( + "clear", + Some("Clears the terminal screen."), + Callable::Simple(commands::clear), + None, + ); + + COMMAND_LIST.add_command( + "echo", + Some("Prints the provided arguments back to the terminal."), + Callable::WithArgs(commands::echo), + Some(1), // Requires at least one argument + ); + + COMMAND_LIST.add_command( + "help", + Some("Displays a list of all available commands."), + Callable::Simple(commands::help), + None, + ); + + // EXAMPLE + // Adding aliases for commands + COMMAND_LIST.add_alias("cls".to_string(), "clear".to_string()); // Likely unintended; consider removing or renaming. +} + +struct ZPrompt { + left_text: String, + right_text: String, +} + +impl Prompt for ZPrompt { + fn render_prompt_left(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.left_text) + } + + fn render_prompt_right(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed(&self.right_text) + } + + fn render_prompt_history_search_indicator( + &self, + _history_search: reedline::PromptHistorySearch, + ) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("") + } + + fn render_prompt_indicator( + &self, + prompt_mode: reedline::PromptEditMode, + ) -> std::borrow::Cow { + match prompt_mode { + reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), + reedline::PromptEditMode::Emacs => { + let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); + std::borrow::Cow::Owned(timestamp) + } + reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), + reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), + } + } + + fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { + std::borrow::Cow::Borrowed("><") + } +} + +fn evaluate_command(input: &str) { + if input.trim().is_empty() { + return; + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(input).collect(); + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens: Vec<&str> = command.split_whitespace().collect(); + if tokens.is_empty() { + return; + } + + let cmd_name = tokens[0]; + let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); + + COMMAND_LIST.execute_command( + cmd_name.to_string(), + if args.is_empty() { None } else { Some(args) }, + ); + } +} + +pub async fn handle_repl() { + let mut line_editor = Reedline::create(); + register_commands(); + + loop { + let sig = line_editor.read_line(&ZPrompt { + left_text: String::new(), + right_text: "<<".to_string(), + }); + + match sig { + Ok(Signal::Success(buffer)) => { + if buffer == "exit" { + std::process::exit(0); + } else { + evaluate_command(&buffer); + } + } + Ok(Signal::CtrlC) => { + println!("\nCONTROL+C RECEIVED, TERMINATING"); + std::process::exit(0); + } + err => { + eprintln!("Error: {:?}", err); + } + } + } +} diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs deleted file mode 100644 index f35c870..0000000 --- a/engine/src/core/splash.rs +++ /dev/null @@ -1,26 +0,0 @@ -use colored::Colorize; - -pub fn print_splash() { - println!( - r#" - &&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&&&&&& - && &&&&&&&&& - && &&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&& &&&&&&&&&&&& - &&&&&&&&& && - &&&&&&&&& && - &&&&&&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&& - - Version: {} - "#, - env!("CARGO_PKG_VERSION").yellow().italic().underline() - ); -} diff --git a/engine/src/main.rs b/engine/src/main.rs index 5baa920..ec45bad 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,18 +1,44 @@ use anyhow::Result; +use colored::Colorize; use log2::info; pub mod core; +pub fn print_splash() { + println!( + r#" + &&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&& + && &&&&&&&&& + && &&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&& &&&&&&&&&&&& + &&&&&&&&& && + &&&&&&&&& && + &&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&& + + Version: {} + "#, + env!("CARGO_PKG_VERSION").yellow().italic().underline() + ); +} + #[tokio::main] async fn main() -> Result<()> { let _log2 = log2::open("z.log").tee(true).level("debug").start(); info!("Initalizing Engine"); let shell_thread = tokio::task::spawn(async { info!("Shell thread started"); - core::repl::handle_repl().await; + core::repl::repl::handle_repl().await; }); - core::splash::print_splash(); + print_splash(); info!("Engine Initalized"); core::init_renderer()?; shell_thread.await?; From a0845b471970252478ee65b705e6303f8a12b110 Mon Sep 17 00:00:00 2001 From: Jason Spalti Date: Mon, 2 Dec 2024 11:51:39 -0600 Subject: [PATCH 023/324] Refactor logging system to switch between stdout and file logging * Refactor logging to switch between stdout and file logging * Use "clear" instead of "tput reset" for unix * Remove redundant comments --- editor/Cargo.toml | 2 - editor/src/main.rs | 5 ++- engine/Cargo.toml | 5 ++- engine/src/core/mod.rs | 1 - engine/src/core/renderer/ctx.rs | 74 +++++++++++++++++--------------- engine/src/core/renderer/mod.rs | 31 +++++++------ engine/src/core/repl/commands.rs | 10 ++--- engine/src/core/repl/mod.rs | 19 ++++---- engine/src/core/repl/repl.rs | 3 +- engine/src/main.rs | 68 +++++++++++++++-------------- engine/src/utils/logger.rs | 72 +++++++++++++++++++++++++++++++ engine/src/utils/mod.rs | 2 + engine/src/utils/splash.rs | 30 +++++++++++++ xtask/src/editor.rs | 5 ++- xtask/src/engine.rs | 17 ++++---- xtask/src/main.rs | 56 +++++++++++------------- 16 files changed, 252 insertions(+), 148 deletions(-) create mode 100644 engine/src/utils/logger.rs create mode 100644 engine/src/utils/mod.rs create mode 100644 engine/src/utils/splash.rs diff --git a/editor/Cargo.toml b/editor/Cargo.toml index f584ecb..b9c38f2 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -3,5 +3,3 @@ name = "editor" version = "0.1.0" edition = "2021" -[dependencies] - diff --git a/editor/src/main.rs b/editor/src/main.rs index 6bd4269..dcfd9e3 100644 --- a/editor/src/main.rs +++ b/editor/src/main.rs @@ -1,3 +1,4 @@ fn main() { - println!("editor") -} \ No newline at end of file + todo!() +} + diff --git a/engine/Cargo.toml b/engine/Cargo.toml index d9f748d..8032f37 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -6,10 +6,11 @@ edition = "2021" [dependencies] anyhow = "1.0.93" chrono = "0.4.38" +clap = { version = "4.5.21", features = ["derive"] } colored = "2.1.0" lazy_static = "1.5.0" -#log = "0.4.22" -log2 = "0.1.14" +log = "0.4.22" +once_cell = "1.20.2" parking_lot = "0.12.3" reedline = "0.37.0" regex = "1.11.1" diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index fe2a8cd..5615f54 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -11,4 +11,3 @@ pub fn init_renderer() -> Result<()> { let mut app = App::default(); Ok(event_loop.run_app(&mut app)?) } - diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs index 690ed0b..fca1090 100644 --- a/engine/src/core/renderer/ctx.rs +++ b/engine/src/core/renderer/ctx.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use anyhow::Result; -use tokio::task::spawn_blocking; use winit::window::Window; pub struct WgpuCtx<'window> { @@ -46,13 +45,14 @@ impl<'window> WgpuCtx<'window> { surface.configure(&device, &surface_config); Ok(WgpuCtx { - device: device, - queue: queue, - surface: surface, - surface_config: surface_config, - adapter: adapter, + device, + queue, + surface, + surface_config, + adapter, }) } + pub fn new_blocking(window: Arc) -> Result> { tokio::task::block_in_place(|| { tokio::runtime::Runtime::new() @@ -60,41 +60,47 @@ impl<'window> WgpuCtx<'window> { .block_on(async { WgpuCtx::new(window).await }) }) } + pub fn resize(&mut self, new_size: (u32, u32)) { let (width, height) = new_size; self.surface_config.width = width.max(1); self.surface_config.height = height.max(1); self.surface.configure(&self.device, &self.surface_config); } + pub fn draw(&mut self) { let surface_texture = self - .surface - .get_current_texture() - .expect("Failed to get surface texture"); - let view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - } - self.queue.submit(Some(encoder.finish())); - surface_texture.present(); + .surface + .get_current_texture() + .expect("Failed to get surface texture"); + let view = surface_texture + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + } + self.queue.submit(Some(encoder.finish())); + surface_texture.present(); } } diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs index c6fcdab..65486a3 100644 --- a/engine/src/core/renderer/mod.rs +++ b/engine/src/core/renderer/mod.rs @@ -1,12 +1,13 @@ -use std::sync::Arc; - +pub mod ctx; use ctx::WgpuCtx; -use log2::{debug, error, trace}; + +use log::{debug, trace}; +use std::sync::Arc; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; -use winit::window::{self, Window, WindowId}; -pub mod ctx; +use winit::window::{Window, WindowId}; + #[derive(Default)] pub struct App<'window> { window: Option>, @@ -17,9 +18,11 @@ impl ApplicationHandler for App<'_> { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.window.is_none() { let win_attr = Window::default_attributes().with_title("Zenyx"); - let window = Arc::new(event_loop - .create_window(win_attr) - .expect("create window err.")); + let window = Arc::new( + event_loop + .create_window(win_attr) + .expect("create window err."), + ); self.window = Some(window.clone()); let wgpu_ctx = WgpuCtx::new_blocking(window.clone()).unwrap(); self.ctx = Some(wgpu_ctx) @@ -44,15 +47,15 @@ impl ApplicationHandler for App<'_> { } } WindowEvent::Resized(size) => { - if let (Some(wgpu_ctx),Some(window)) = (&mut self.ctx, &self.window) { + if let (Some(wgpu_ctx), Some(window)) = (&mut self.ctx, &self.window) { wgpu_ctx.resize(size.into()); window.request_redraw(); - let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); - //self.window.as_ref().unwrap().set_title(&format!("you reszed the window to {size_str}")); - debug!("Window resized to {:?}", size_str); + + let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); + debug!("Window resized to {:?}", size_str); + } } - } _ => trace!("Unhandled window event"), } } -} \ No newline at end of file +} diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index c4d2437..d500ca8 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,30 +1,28 @@ use super::COMMAND_LIST; use std::process::Command; -use log2::{debug, info}; +use log::debug; pub(crate) fn say_hello() { println!("Hello, World!"); } pub(crate) fn echo(args: Vec) { - debug!("{}", args.join(" ")); println!("{}", args.join(" ")) } pub(crate) fn exit() { - debug!("Exiting..."); + println!("Exiting..."); std::process::exit(0) } pub(crate) fn clear() { - info!("Clearing screen..., running command"); + println!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { debug!("target_os is windows"); Command::new("cmd").args(["/c", "cls"]).spawn() } else { debug!("target_os is unix"); - // "clear" or "tput reset" - Command::new("tput").arg("reset").spawn() + Command::new("clear").spawn() }; } diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 6a0ad4c..19a0cdf 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -2,7 +2,7 @@ pub mod commands; pub mod repl; use lazy_static::lazy_static; -use log2::{debug, error, info}; +use log::{debug, info}; use parking_lot::RwLock; use std::{borrow::Borrow, collections::HashMap, sync::Arc}; @@ -26,11 +26,10 @@ pub struct Command { impl Command { pub fn execute(&self, args: Option>) { - //debug!("Executing command: {}", self.name); match &self.function { Callable::Simple(f) => { if let Some(args) = args { - error!( + eprintln!( "Command expected 0 arguments but {} args were given. Ignoring..", args.len() ); @@ -39,7 +38,7 @@ impl Command { } Callable::WithArgs(f) => match args { Some(args) => f(args), - None => error!("Command expected arguments but received 0"), + None => eprintln!("Command expected arguments but received 0"), }, } } @@ -76,7 +75,7 @@ impl CommandList { func: Callable, arg_count: Option, ) { - debug!("Adding command: {}", name); + info!("Adding command: {}", name); let mut commands = self.commands.write(); commands.push(Command { @@ -88,24 +87,22 @@ impl CommandList { } fn add_alias(&self, name: String, alias: String) { - //println!("Input alias: {}", alias); if self.aliases.read().contains_key(&alias) { - error!("Alias: '{}' already exists", alias); + eprintln!("Alias: '{}' already exists", alias); return; } let mut commands = self.commands.write(); if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { - info!("Adding alias: {} for cmd: {}", alias, command.name); + debug!("Adding alias: {} for cmd: {}", alias, command.name); self.aliases .write() .insert(alias.to_string(), name.to_string()); } else { - error!("Command: '{}' was not found", name); + eprintln!("Command: '{}' was not found", name); } } fn execute_command(&self, mut name: String, args: Option>) { - //info!("received input command: {}", name); let commands = self.commands.borrow(); if self.aliases.read().contains_key(&name) { name = self @@ -116,7 +113,7 @@ impl CommandList { .1 .to_string(); - debug!("changed to {}", name); + debug!("changed to {}", &name); } if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { match (command.arg_count, args.as_ref()) { diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 12c5667..673d4ea 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -2,7 +2,6 @@ use super::{commands, Callable, COMMAND_LIST}; use chrono::Local; use reedline::{Prompt, Reedline, Signal}; use regex::Regex; -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; fn register_commands() { COMMAND_LIST.add_command( @@ -42,7 +41,7 @@ fn register_commands() { // EXAMPLE // Adding aliases for commands - COMMAND_LIST.add_alias("cls".to_string(), "clear".to_string()); // Likely unintended; consider removing or renaming. + COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); } struct ZPrompt { diff --git a/engine/src/main.rs b/engine/src/main.rs index ec45bad..5553944 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,46 +1,48 @@ use anyhow::Result; -use colored::Colorize; -use log2::info; +use clap::Parser; +use log::{info, warn, LevelFilter}; pub mod core; +pub mod utils; -pub fn print_splash() { - println!( - r#" - &&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&&&&&& - && &&&&&&&&& - && &&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&& &&&&&&&&&&&& - &&&&&&&&& && - &&&&&&&&& && - &&&&&&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&& +use utils::{logger::LOGGER, splash::print_splash}; - Version: {} - "#, - env!("CARGO_PKG_VERSION").yellow().italic().underline() - ); +#[derive(Parser)] +struct Cli { + #[arg(long, short, help = "Enable logging output")] + log: bool, } #[tokio::main] async fn main() -> Result<()> { - let _log2 = log2::open("z.log").tee(true).level("debug").start(); - info!("Initalizing Engine"); - let shell_thread = tokio::task::spawn(async { - info!("Shell thread started"); - core::repl::repl::handle_repl().await; - }); + let cli = Cli::parse(); + + log::set_logger(&*LOGGER).unwrap(); + log::set_max_level(LevelFilter::Debug); print_splash(); - info!("Engine Initalized"); - core::init_renderer()?; - shell_thread.await?; + + if cli.log { + info!("Initializing Engine with logging to stdout enabled"); + warn!("REPL cannot be used with logging enabled due to ReedLine not supporting writing to stdout"); + + core::init_renderer()?; + } else { + LOGGER.write_to_stdout(); + info!("Initializing Engine with logging to stdout disabled"); + warn!("REPL cannot be used with logging enabled due to ReedLine not supporting writing to stdout"); + info!("Writing all logs to file z.log"); + + LOGGER.write_to_file("z.log"); + info!("Logging back to file z.log"); + + let shell_thread = tokio::task::spawn(async { + core::repl::repl::handle_repl().await; + }); + + core::init_renderer()?; + shell_thread.await?; + } + Ok(()) } diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs new file mode 100644 index 0000000..c542396 --- /dev/null +++ b/engine/src/utils/logger.rs @@ -0,0 +1,72 @@ +use colored::Colorize; +use log::{Level, Log, Metadata, Record}; +use once_cell::sync::Lazy; +use std::fs::OpenOptions; +use std::io::{self, Write}; +use std::sync::{Arc, Mutex}; + +pub static LOGGER: Lazy = Lazy::new(DynamicLogger::new); + +// A logger that dynamically switches between file and stdout +pub struct DynamicLogger { + pub writer: Arc>>, +} + +impl DynamicLogger { + pub fn new() -> Self { + Self { + writer: Arc::new(Mutex::new(Box::new(io::stdout()))), + } + } + + pub fn write_to_file(&self, file_path: &str) { + let file = OpenOptions::new() + .create(true) + .write(true) + .append(true) + .open(file_path) + .expect("Failed to open log file"); + + *self.writer.lock().unwrap() = Box::new(file); + } + + pub fn write_to_stdout(&self) { + *self.writer.lock().unwrap() = Box::new(io::stdout()); + } + + fn colorize_level(level: Level) -> colored::ColoredString { + match level { + Level::Error => "ERROR".red(), + Level::Warn => "WARN".yellow(), + Level::Info => "INFO".green(), + Level::Debug => "DEBUG".blue(), + Level::Trace => "TRACE".cyan(), + } + } +} + +impl Log for DynamicLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= Level::Debug + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let mut writer = self.writer.lock().unwrap(); + let level = Self::colorize_level(record.level()); // Apply coloring + writeln!( + writer, + "{} [{}] - {}", + chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + level, + record.args() + ) + .unwrap(); + } + } + + fn flush(&self) { + let mut writer = self.writer.lock().unwrap(); + writer.flush().unwrap(); + } +} diff --git a/engine/src/utils/mod.rs b/engine/src/utils/mod.rs new file mode 100644 index 0000000..a0f8995 --- /dev/null +++ b/engine/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod logger; +pub mod splash; diff --git a/engine/src/utils/splash.rs b/engine/src/utils/splash.rs new file mode 100644 index 0000000..5f55560 --- /dev/null +++ b/engine/src/utils/splash.rs @@ -0,0 +1,30 @@ +use colored::Colorize; +use log::info; + +pub fn print_splash() { + info!( + "{}", + format!( + r#" + &&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&&&&&& + && &&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&& +&&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&&& &&&&&&&&&&&&& +&&&&&&&&&&& &&&&&&&&&&&& + &&&&&&&&& && + &&&&&&&&& && + &&&&&&&&&&&&&&&&&&&&& + &&&&&&&&&&&&&&&&& + &&&&&&&&&&& + + Version: {} + "#, + env!("CARGO_PKG_VERSION").color("yellow") + ) + .bright_black() + ); +} diff --git a/xtask/src/editor.rs b/xtask/src/editor.rs index 8cfa650..2fb341f 100644 --- a/xtask/src/editor.rs +++ b/xtask/src/editor.rs @@ -1 +1,4 @@ -pub fn build_editor() {} \ No newline at end of file +pub fn build_editor() { + todo!() +} + diff --git a/xtask/src/engine.rs b/xtask/src/engine.rs index ecdf677..4e901f2 100644 --- a/xtask/src/engine.rs +++ b/xtask/src/engine.rs @@ -1,22 +1,23 @@ use std::process::Stdio; - pub fn build_engine() { - + todo!() } pub fn build_core() { - let threads = format!("-j{}",std::thread::available_parallelism().unwrap().get()); + let threads = format!("-j{}", std::thread::available_parallelism().unwrap().get()); + let mut run = std::process::Command::new("cargo") .arg("run") - .arg(threads) .arg("--bin") .arg("zenyx") - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) .spawn() .unwrap(); + run.wait().unwrap(); -} \ No newline at end of file +} + diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b146421..e3e07c8 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,12 +1,11 @@ - use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; -pub mod engine; pub mod editor; +pub mod engine; #[derive(Parser)] #[command(version, about, long_about = None,disable_version_flag = true,disable_help_flag = true)] struct Cli { - #[arg(short,long)] + #[arg(short, long)] release: bool, #[command(subcommand)] command: Option, @@ -16,55 +15,48 @@ struct Cli { enum Commands { Run { #[arg()] - task: Task + task: Task, }, Config, - } -#[derive(Clone,ValueEnum)] + +#[derive(Clone, ValueEnum)] enum Task { Engine, // Builds both editor and core Editor, // Builds editor only - Core, // Builds engine core only - Help, + Core, // Builds engine core only + Help, } - - fn main() { let cli = Cli::parse(); - - - if cli.release { println!("Running in release mode") } match &cli.command { - None => { - Cli::command().print_help().map_err(|e| { - println!("Could not run Xtask: {e}"); - - }).unwrap(); + Cli::command() + .print_help() + .map_err(|e| { + println!("Could not run Xtask: {e}"); + }) + .unwrap(); } - Some(Commands::Run { task }) => { - match task { - Task::Engine => engine::build_engine(), - Task::Editor => todo!("Editor is not being actively worked on"), - Task::Core => { - engine::build_core(); - }, - Task::Help => { - println!("The following options are avalible to run"); - todo!() - }, + Some(Commands::Run { task }) => match task { + Task::Engine => engine::build_engine(), + Task::Editor => todo!("Editor is not being actively worked on"), + Task::Core => { + engine::build_core(); } - } + Task::Help => { + println!("The following options are avalible to run"); + todo!() + } + }, Some(Commands::Config) => { todo!() } } - -} \ No newline at end of file +} From 88fd7d6d50f5762b6c4a6d1672ecb8f2938675a9 Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 12:49:08 -0500 Subject: [PATCH 024/324] remove --verbose from tests --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9eecf59..f4bc5da 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -70,7 +70,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --verbose --target ${{ matrix.target }} + args: --target ${{ matrix.target }} env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc QEMU_LD_PREFIX: /usr/aarch64-linux-gnu From 7ff22911eea4b3612a6b11b0e9dd298391d887d2 Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 14:19:00 -0500 Subject: [PATCH 025/324] add readme with branding --- CONTRIBUTING.md | 0 README.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++ assets/Badge.png | Bin 0 -> 26215 bytes assets/Badge.svg | 9 ++++ assets/Logo.png | Bin 0 -> 16329 bytes assets/Logo.svg | 10 ++++ 6 files changed, 138 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 assets/Badge.png create mode 100644 assets/Badge.svg create mode 100644 assets/Logo.png create mode 100644 assets/Logo.svg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index e69de29..37a0977 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,119 @@ +
+ +Zenyx engine + + +![Version](https://img.shields.io/badge/version-0.1.0-blue)![License](https://img.shields.io/github/license/Caznix/Zenyx) +![Build](https://img.shields.io/github/actions/workflow/status/Caznix/Zenyx/rust.yml?branch=main) + + +![platform](https://img.shields.io/badge/platform-windows%20%7C%20linux%20%7C%20macos-informational) + +
+ +--- +## What is zenyx? + +Zenyx is a game engine built on top of [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://github.com/rust-windowing/winit). It is designed to be an intuitive and innovative game engine that can both adapt to your workflow and bring new ideas to the table. + +### Features + +> ๐Ÿ’ก Note: The following features are goals and are not currently implemented: +Zenyx + +***Zenyx aims to provide:*** +* **Cross platform support** - Windows, Linux and macOS support +* **Support for multiple renderers** - Zenyx can use Vulkan. OpenGL, and DirectX. In both 2D and 3D +* **Safe Performance** - Written in Rust for speed and safety +* **Intuitive Design** - User-friendly workflows and highly extensible tools +* **Flexible Scripting** - Easy to use scripting language without sacrificing performance +* **Extensible Architecture** - Modular design for easy customization +* **Fully Featured Toolset** - Built in tools for playing spatial audio and level editing +* **Real-time Editor** - Live preview and Incredible User experience +* **Asset Management** - Efficient resource handling system +* **Physics Integration** - Built-in physics simulation +* **Improved Scene Management** - Flexible scene organization tools + + +## FAQ + +
+What platforms does Zenyx support? + +Zenyx primarily supports Windows and Linux, with secondary support for macOS (requires MoltenVK). See the [Platform support table](#what-platforms-will-be-supported) for more information. +
+ +
+Is Zenyx ready for production use? + +Zenyx is currently in early development and is not yet ready for any simple use cases, but we're working hard to make it the best it can be before we release 1.0. If this interests you and you're interested in helping, please check out the [contribution section](CONTRIBUTING.md) for the ways you can help. +
+ +
+How can I contribute to Zenyx? + +We welcome contributions! Please check our contribution guidelines and open a pull request on GitHub, if you arent a developer, you can also report bugs or feature requests on our [issue tracker](https://github.com/Caznix/Zenyx/issues). For more information, please see the [Contributing section](#contributing). +
+ +
+What are the system requirements? + +Detailed system requirements will be provided as the engine matures. Currently, the only requirement is a modern OS and a system with atleast a semi-recent GPU. +
+ +
+Is Zenyx free to use? + +Yes, Zenyx is open-source software licensed under MIT. You can Modify, Distribute, and use Zenyx for any purpose you wish. +
+ + +### What platforms will be supported? +| Platform | Support Priority | Status | Notes | +|----------|-----------------|--------|--------| +| Windows | Primary | โœ… | | +| Linux/*BSD | Primary | โœ… | | +| macOS | Secondary | ๐ŸŒ‹ | Requires MoltenVK for both editor and exported games due to Wgpu's lack of support for Metal | + +## Documentation + +### Getting Started + +Zenyx is not yet ready to be used but this may change in the near future. +## Contributing + +We welcome contributions of any kind! If you're interested in contributing, please check out our CONTRIBUTING.md file for coding standards and guidelines. + +Even if you're not a developer, you can still help tremendously by spreading the word about Zenyx, reporting bugs, suggesting features, or helping improve our documentation. + +If you would like to contribute code to Zenyx, follow the instructions below for your platform: + +### Prerequisites (all platforms) +0. Install [Rust](https://www.rust-lang.org/tools/install) + +1. Install [git](https://git-scm.com/downloads) +### Building + +**1**. Clone the repository: +```ps1 +git clone https://github.com/Caznix/Zenyx.git +``` + +**2**. +cd into the project directory +```PS1 +cd ./Zenyx +``` +**3**. Build the project using the xtask: +```ps1 +cargo xtask run core # also use editor for editor, or engine for both +``` + + +### macOS +โš ๏ธ as of this current time, we do not have any macOS contributors to write this guide, so follow the [General](#building) instructions and try to fill in the gaps, you can also ask questions on the [Discord]() + + + + + diff --git a/assets/Badge.png b/assets/Badge.png new file mode 100644 index 0000000000000000000000000000000000000000..a9b7e38b56b188e57d94188e249e5317513b4a53 GIT binary patch literal 26215 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 + + + + + + + + diff --git a/assets/Logo.png b/assets/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bc92691aad9d7c6f046fd511d376dbcce5e8e9f8 GIT binary patch literal 16329 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 literal 0 HcmV?d00001 diff --git a/assets/Logo.svg b/assets/Logo.svg new file mode 100644 index 0000000..ef382b7 --- /dev/null +++ b/assets/Logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From a3474803ae4dceb7bf1389fbbc59077fe44792c6 Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 14:26:37 -0500 Subject: [PATCH 026/324] bump logo size --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37a0977..68d01fe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-Zenyx engine +Zenyx engine ![Version](https://img.shields.io/badge/version-0.1.0-blue)![License](https://img.shields.io/github/license/Caznix/Zenyx) From 5b746f6e219570c3c1d00573e96223dd2fc4b859 Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 15:10:17 -0500 Subject: [PATCH 027/324] improve formatting --- README.md | 109 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 68d01fe..c3c936b 100644 --- a/README.md +++ b/README.md @@ -2,118 +2,147 @@ Zenyx engine - -![Version](https://img.shields.io/badge/version-0.1.0-blue)![License](https://img.shields.io/github/license/Caznix/Zenyx) +![Version](https://img.shields.io/badge/version-0.1.0-blue) +![License](https://img.shields.io/github/license/Caznix/Zenyx) ![Build](https://img.shields.io/github/actions/workflow/status/Caznix/Zenyx/rust.yml?branch=main) - ![platform](https://img.shields.io/badge/platform-windows%20%7C%20linux%20%7C%20macos-informational)
--- + ## What is zenyx? -Zenyx is a game engine built on top of [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://github.com/rust-windowing/winit). It is designed to be an intuitive and innovative game engine that can both adapt to your workflow and bring new ideas to the table. +Zenyx is a game engine built on top of [wgpu](https://github.com/gfx-rs/wgpu) +and [winit](https://github.com/rust-windowing/winit). It is designed to be an +intuitive and innovative game engine that can both adapt to your workflow and +bring new ideas to the table. ### Features > ๐Ÿ’ก Note: The following features are goals and are not currently implemented: -Zenyx +> Zenyx -***Zenyx aims to provide:*** -* **Cross platform support** - Windows, Linux and macOS support -* **Support for multiple renderers** - Zenyx can use Vulkan. OpenGL, and DirectX. In both 2D and 3D -* **Safe Performance** - Written in Rust for speed and safety -* **Intuitive Design** - User-friendly workflows and highly extensible tools -* **Flexible Scripting** - Easy to use scripting language without sacrificing performance -* **Extensible Architecture** - Modular design for easy customization -* **Fully Featured Toolset** - Built in tools for playing spatial audio and level editing -* **Real-time Editor** - Live preview and Incredible User experience -* **Asset Management** - Efficient resource handling system -* **Physics Integration** - Built-in physics simulation -* **Improved Scene Management** - Flexible scene organization tools +_**Zenyx aims to provide:**_ +- **Cross platform support** - Windows, Linux and macOS support +- **Support for multiple renderers** - Zenyx can use Vulkan. OpenGL, and + DirectX. In both 2D and 3D +- **Safe Performance** - Written in Rust for speed and safety +- **Intuitive Design** - User-friendly workflows and highly extensible tools +- **Flexible Scripting** - Easy to use scripting language without sacrificing + performance +- **Extensible Architecture** - Modular design for easy customization +- **Fully Featured Toolset** - Built in tools for playing spatial audio and + level editing +- **Real-time Editor** - Live preview and Incredible User experience +- **Asset Management** - Efficient resource handling system +- **Physics Integration** - Built-in physics simulation +- **Improved Scene Management** - Flexible scene organization tools ## FAQ
What platforms does Zenyx support? -Zenyx primarily supports Windows and Linux, with secondary support for macOS (requires MoltenVK). See the [Platform support table](#what-platforms-will-be-supported) for more information. +Zenyx primarily supports Windows and Linux, with secondary support for macOS +(requires MoltenVK). See the +[Platform support table](#what-platforms-will-be-supported) for more +information. +
Is Zenyx ready for production use? -Zenyx is currently in early development and is not yet ready for any simple use cases, but we're working hard to make it the best it can be before we release 1.0. If this interests you and you're interested in helping, please check out the [contribution section](CONTRIBUTING.md) for the ways you can help. +Zenyx is currently in early development and is not yet ready for any simple use +cases, but we're working hard to make it the best it can be before we release +1.0. If this interests you and you're interested in helping, please check out +the [contribution section](CONTRIBUTING.md) for the ways you can help. +
How can I contribute to Zenyx? -We welcome contributions! Please check our contribution guidelines and open a pull request on GitHub, if you arent a developer, you can also report bugs or feature requests on our [issue tracker](https://github.com/Caznix/Zenyx/issues). For more information, please see the [Contributing section](#contributing). +We welcome contributions! Please check our contribution guidelines and open a +pull request on GitHub, if you arent a developer, you can also report bugs or +feature requests on our [issue tracker](https://github.com/Caznix/Zenyx/issues). +For more information, please see the [Contributing section](#contributing). +
What are the system requirements? -Detailed system requirements will be provided as the engine matures. Currently, the only requirement is a modern OS and a system with atleast a semi-recent GPU. +Detailed system requirements will be provided as the engine matures. Currently, +the only requirement is a modern OS and a system with atleast a semi-recent GPU. +
Is Zenyx free to use? -Yes, Zenyx is open-source software licensed under MIT. You can Modify, Distribute, and use Zenyx for any purpose you wish. +Yes, Zenyx is open-source software licensed under MIT. You can Modify, +Distribute, and use Zenyx for any purpose you wish. +
- ### What platforms will be supported? -| Platform | Support Priority | Status | Notes | -|----------|-----------------|--------|--------| -| Windows | Primary | โœ… | | -| Linux/*BSD | Primary | โœ… | | -| macOS | Secondary | ๐ŸŒ‹ | Requires MoltenVK for both editor and exported games due to Wgpu's lack of support for Metal | + +| Platform | Support Priority | Status | Notes | +| ---------- | ---------------- | ------ | -------------------------------------------------------------------------------------------- | +| Windows | Primary | โœ… | | +| Linux/*BSD | Primary | โœ… | | +| macOS | Secondary | ๐ŸŒ‹ | Requires MoltenVK for both editor and exported games due to Wgpu's lack of support for Metal | ## Documentation ### Getting Started Zenyx is not yet ready to be used but this may change in the near future. + ## Contributing -We welcome contributions of any kind! If you're interested in contributing, please check out our CONTRIBUTING.md file for coding standards and guidelines. +We welcome contributions of any kind! If you're interested in contributing, +please check out our CONTRIBUTING.md file for coding standards and guidelines. -Even if you're not a developer, you can still help tremendously by spreading the word about Zenyx, reporting bugs, suggesting features, or helping improve our documentation. +Even if you're not a developer, you can still help tremendously by spreading the +word about Zenyx, reporting bugs, suggesting features, or helping improve our +documentation. -If you would like to contribute code to Zenyx, follow the instructions below for your platform: +If you would like to contribute code to Zenyx, follow the instructions below for +your platform: ### Prerequisites (all platforms) + 0. Install [Rust](https://www.rust-lang.org/tools/install) 1. Install [git](https://git-scm.com/downloads) + ### Building **1**. Clone the repository: + ```ps1 git clone https://github.com/Caznix/Zenyx.git ``` -**2**. -cd into the project directory +**2**. cd into the project directory + ```PS1 cd ./Zenyx ``` + **3**. Build the project using the xtask: + ```ps1 -cargo xtask run core # also use editor for editor, or engine for both +cargo xtask run core # also use editor for editor, or engine for both ``` - ### macOS -โš ๏ธ as of this current time, we do not have any macOS contributors to write this guide, so follow the [General](#building) instructions and try to fill in the gaps, you can also ask questions on the [Discord]() - - - - +โš ๏ธ as of this current time, we do not have any macOS contributors to write this +guide, so follow the [General](#building) instructions and try to fill in the +gaps, you can also ask questions on the [Discord]() From ce1be977d480b3bced075dedf0cac7d9a95e76ef Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 15:43:39 -0500 Subject: [PATCH 028/324] replace reedline with rustyline --- engine/Cargo.toml | 2 +- engine/src/core/repl/repl.rs | 83 +++++++++++------------------------- engine/src/main.rs | 6 +-- 3 files changed, 29 insertions(+), 62 deletions(-) diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 8032f37..a635866 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -12,8 +12,8 @@ lazy_static = "1.5.0" log = "0.4.22" once_cell = "1.20.2" parking_lot = "0.12.3" -reedline = "0.37.0" regex = "1.11.1" +rustyline = "15.0.0" thiserror = "2.0.3" tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } wgpu = "23.0.1" diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 673d4ea..8ac6710 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -1,7 +1,10 @@ use super::{commands, Callable, COMMAND_LIST}; +use anyhow::Result; use chrono::Local; -use reedline::{Prompt, Reedline, Signal}; +use colored::Colorize; +use log::debug; use regex::Regex; +use rustyline::DefaultEditor; fn register_commands() { COMMAND_LIST.add_command( @@ -44,46 +47,7 @@ fn register_commands() { COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); } -struct ZPrompt { - left_text: String, - right_text: String, -} -impl Prompt for ZPrompt { - fn render_prompt_left(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.left_text) - } - - fn render_prompt_right(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed(&self.right_text) - } - - fn render_prompt_history_search_indicator( - &self, - _history_search: reedline::PromptHistorySearch, - ) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("") - } - - fn render_prompt_indicator( - &self, - prompt_mode: reedline::PromptEditMode, - ) -> std::borrow::Cow { - match prompt_mode { - reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"), - reedline::PromptEditMode::Emacs => { - let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string(); - std::borrow::Cow::Owned(timestamp) - } - reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"), - reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"), - } - } - - fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow { - std::borrow::Cow::Borrowed("><") - } -} fn evaluate_command(input: &str) { if input.trim().is_empty() { @@ -115,30 +79,35 @@ fn evaluate_command(input: &str) { } } -pub async fn handle_repl() { - let mut line_editor = Reedline::create(); +pub async fn handle_repl() -> rustyline::Result<()> { + let mut line_editor = DefaultEditor::new()?; + if line_editor.load_history("history.txt").is_err() { + debug!("No previous history."); + } + let time = Local::now().format("%H:%M:%S.%3f").to_string(); + let prompt = format!("[{}/{}] {}", time,"SHELL", ">>\t"); register_commands(); loop { - let sig = line_editor.read_line(&ZPrompt { - left_text: String::new(), - right_text: "<<".to_string(), - }); - + let sig = line_editor.readline( + &prompt.bright_white() + ); match sig { - Ok(Signal::Success(buffer)) => { - if buffer == "exit" { - std::process::exit(0); - } else { - evaluate_command(&buffer); - } + Ok(line) => { + line_editor.add_history_entry(line.as_str())?; + evaluate_command(line.as_str()); } - Ok(Signal::CtrlC) => { - println!("\nCONTROL+C RECEIVED, TERMINATING"); + Err(rustyline::error::ReadlineError::Interrupted) => { + println!("CTRL+C received, exiting..."); std::process::exit(0); } - err => { - eprintln!("Error: {:?}", err); + Err(rustyline::error::ReadlineError::Eof) => { + println!("Error: CTRL+D pressed. Exiting..."); + std::process::exit(0); + } + Err(err) => { + println!("Error: {}", err); + } } } diff --git a/engine/src/main.rs b/engine/src/main.rs index 5553944..089f508 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -24,24 +24,22 @@ async fn main() -> Result<()> { if cli.log { info!("Initializing Engine with logging to stdout enabled"); - warn!("REPL cannot be used with logging enabled due to ReedLine not supporting writing to stdout"); core::init_renderer()?; } else { LOGGER.write_to_stdout(); info!("Initializing Engine with logging to stdout disabled"); - warn!("REPL cannot be used with logging enabled due to ReedLine not supporting writing to stdout"); info!("Writing all logs to file z.log"); LOGGER.write_to_file("z.log"); info!("Logging back to file z.log"); let shell_thread = tokio::task::spawn(async { - core::repl::repl::handle_repl().await; + core::repl::repl::handle_repl().await }); core::init_renderer()?; - shell_thread.await?; + shell_thread.await??; // LOL - Caz } Ok(()) From 85d8ef14a8054613245d5fe47a0b1bebbf0ee8e1 Mon Sep 17 00:00:00 2001 From: Caznix Date: Mon, 2 Dec 2024 18:57:00 -0500 Subject: [PATCH 029/324] Remove seperate editor and xtask --- .cargo/config.toml | 1 - Cargo.toml | 33 +++++++++++++++++++++--- README.md | 10 ++++---- editor/Cargo.toml | 5 ---- editor/src/main.rs | 4 --- xtask/Cargo.toml | 32 ----------------------- xtask/src/editor.rs | 4 --- xtask/src/engine.rs | 23 ----------------- xtask/src/main.rs | 62 --------------------------------------------- 9 files changed, 35 insertions(+), 139 deletions(-) delete mode 100644 editor/Cargo.toml delete mode 100644 editor/src/main.rs delete mode 100644 xtask/Cargo.toml delete mode 100644 xtask/src/editor.rs delete mode 100644 xtask/src/engine.rs delete mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 0c612da..ae0e944 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1 @@ [alias] -xtask = "run --quiet --package xtask --" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 63ba671..62e0d78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,34 @@ [workspace] resolver = "2" members = [ - "engine", - "editor", - "xtask", + "engine" ] + +[profile.dev] +rpath = false +panic = "abort" +lto = "off" +opt-level = 0 +debug = false +overflow-checks = false +incremental = true +codegen-units = 512 + +strip = "symbols" +debug-assertions = false + +[profile.dev.package."*"] +opt-level = 0 +debug = false +overflow-checks = false +incremental = true +codegen-units = 512 + +strip = "symbols" +debug-assertions = false +[profile.dev.build-override] +opt-level = 0 +debug = false +overflow-checks = false +incremental = true +codegen-units = 512 \ No newline at end of file diff --git a/README.md b/README.md index c3c936b..14ef1e8 100644 --- a/README.md +++ b/README.md @@ -125,20 +125,20 @@ your platform: **1**. Clone the repository: -```ps1 +```bash git clone https://github.com/Caznix/Zenyx.git ``` **2**. cd into the project directory -```PS1 +```bash cd ./Zenyx ``` -**3**. Build the project using the xtask: +**3**. Build the project -```ps1 -cargo xtask run core # also use editor for editor, or engine for both +```bash +cargo run ``` ### macOS diff --git a/editor/Cargo.toml b/editor/Cargo.toml deleted file mode 100644 index b9c38f2..0000000 --- a/editor/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "editor" -version = "0.1.0" -edition = "2021" - diff --git a/editor/src/main.rs b/editor/src/main.rs deleted file mode 100644 index dcfd9e3..0000000 --- a/editor/src/main.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - todo!() -} - diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml deleted file mode 100644 index 7e4ef1a..0000000 --- a/xtask/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "xtask" -version = "0.1.0" -edition = "2021" - - -[dependencies] -clap = { version = "4.5.20", features = ["derive"] } - -[profile.dev] -rpath = false -panic = "abort" -lto = "off" -opt-level = 0 -debug = false -overflow-checks = false -incremental = true -codegen-units = 256 - -strip = "symbols" -debug-assertions = false - -[profile.dev.package."*"] -opt-level = 0 -debug = false -overflow-checks = false -incremental = true -codegen-units = 256 - -strip = "symbols" -debug-assertions = false - diff --git a/xtask/src/editor.rs b/xtask/src/editor.rs deleted file mode 100644 index 2fb341f..0000000 --- a/xtask/src/editor.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub fn build_editor() { - todo!() -} - diff --git a/xtask/src/engine.rs b/xtask/src/engine.rs deleted file mode 100644 index 4e901f2..0000000 --- a/xtask/src/engine.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::process::Stdio; - -pub fn build_engine() { - todo!() -} - -pub fn build_core() { - let threads = format!("-j{}", std::thread::available_parallelism().unwrap().get()); - - let mut run = std::process::Command::new("cargo") - .arg("run") - .arg(threads) - .arg("--bin") - .arg("zenyx") - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn() - .unwrap(); - - run.wait().unwrap(); -} - diff --git a/xtask/src/main.rs b/xtask/src/main.rs deleted file mode 100644 index e3e07c8..0000000 --- a/xtask/src/main.rs +++ /dev/null @@ -1,62 +0,0 @@ -use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; -pub mod editor; -pub mod engine; - -#[derive(Parser)] -#[command(version, about, long_about = None,disable_version_flag = true,disable_help_flag = true)] -struct Cli { - #[arg(short, long)] - release: bool, - #[command(subcommand)] - command: Option, -} - -#[derive(Subcommand)] -enum Commands { - Run { - #[arg()] - task: Task, - }, - Config, -} - -#[derive(Clone, ValueEnum)] -enum Task { - Engine, // Builds both editor and core - Editor, // Builds editor only - Core, // Builds engine core only - Help, -} - -fn main() { - let cli = Cli::parse(); - - if cli.release { - println!("Running in release mode") - } - - match &cli.command { - None => { - Cli::command() - .print_help() - .map_err(|e| { - println!("Could not run Xtask: {e}"); - }) - .unwrap(); - } - Some(Commands::Run { task }) => match task { - Task::Engine => engine::build_engine(), - Task::Editor => todo!("Editor is not being actively worked on"), - Task::Core => { - engine::build_core(); - } - Task::Help => { - println!("The following options are avalible to run"); - todo!() - } - }, - Some(Commands::Config) => { - todo!() - } - } -} From 0c9aa6ad7a742e0c9ae367a671a52304579ffd7f Mon Sep 17 00:00:00 2001 From: Caznix Date: Tue, 3 Dec 2024 01:12:03 -0500 Subject: [PATCH 030/324] Add press kit section --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 14ef1e8..1b45789 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-Zenyx engine +Zenyx engine ![Version](https://img.shields.io/badge/version-0.1.0-blue) ![License](https://img.shields.io/github/license/Caznix/Zenyx) @@ -146,3 +146,14 @@ cargo run โš ๏ธ as of this current time, we do not have any macOS contributors to write this guide, so follow the [General](#building) instructions and try to fill in the gaps, you can also ask questions on the [Discord]() + +## Press kit +## Typefaces + +Main typeface + +Zenyx Typeface + +## Colored badges + +Zenyx Logo \ No newline at end of file From a2c092bc7db0b82dc37bcf67b426e93b814ecbd6 Mon Sep 17 00:00:00 2001 From: Caznix Date: Tue, 3 Dec 2024 01:12:33 -0500 Subject: [PATCH 031/324] add zephyr ECS subcrate --- Cargo.toml | 12 +++++++----- engine/Cargo.toml | 1 + engine/src/main.rs | 2 ++ subcrates/zephyr/Cargo.toml | 6 ++++++ subcrates/zephyr/src/lib.rs | 14 ++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 subcrates/zephyr/Cargo.toml create mode 100644 subcrates/zephyr/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 62e0d78..1a67ef2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] resolver = "2" -members = [ - "engine" -] +members = ["engine", "subcrates/zephyr"] [profile.dev] rpath = false @@ -17,7 +15,7 @@ codegen-units = 512 strip = "symbols" debug-assertions = false -[profile.dev.package."*"] +[profile.dev.package."*"] opt-level = 0 debug = false overflow-checks = false @@ -31,4 +29,8 @@ opt-level = 0 debug = false overflow-checks = false incremental = true -codegen-units = 512 \ No newline at end of file +codegen-units = 512 + +[workspace.dependencies] +anyhow = "1.0.93" +zephyr = { path = "./subcrates/zephyr" } diff --git a/engine/Cargo.toml b/engine/Cargo.toml index a635866..de1bd1a 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -18,3 +18,4 @@ thiserror = "2.0.3" tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } wgpu = "23.0.1" winit = "0.30.5" +zephyr.workspace = true \ No newline at end of file diff --git a/engine/src/main.rs b/engine/src/main.rs index 089f508..923c56e 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -15,6 +15,8 @@ struct Cli { #[tokio::main] async fn main() -> Result<()> { + let t = zephyr::add(0, 2); + println!("{}", t); let cli = Cli::parse(); log::set_logger(&*LOGGER).unwrap(); diff --git a/subcrates/zephyr/Cargo.toml b/subcrates/zephyr/Cargo.toml new file mode 100644 index 0000000..739c027 --- /dev/null +++ b/subcrates/zephyr/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "zephyr" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/subcrates/zephyr/src/lib.rs b/subcrates/zephyr/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/subcrates/zephyr/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 4b6ef4e988bc2847549313829181f68bc82883ae Mon Sep 17 00:00:00 2001 From: Caznix Date: Tue, 3 Dec 2024 14:22:27 -0500 Subject: [PATCH 032/324] update main readme and add readme for Zephyr --- README.md | 106 +++++++++++++++++++++---------------- subcrates/zephyr/README.md | 8 +++ 2 files changed, 69 insertions(+), 45 deletions(-) create mode 100644 subcrates/zephyr/README.md diff --git a/README.md b/README.md index 1b45789..ce50650 100644 --- a/README.md +++ b/README.md @@ -12,39 +12,49 @@ --- -## What is zenyx? +# What is zenyx? Zenyx is a game engine built on top of [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://github.com/rust-windowing/winit). It is designed to be an intuitive and innovative game engine that can both adapt to your workflow and bring new ideas to the table. -### Features +## Features -> ๐Ÿ’ก Note: The following features are goals and are not currently implemented: +> ๐Ÿš€ Note: The following features are goals and are not currently implemented: > Zenyx -_**Zenyx aims to provide:**_ +### **Zenyx aims to provide:** -- **Cross platform support** - Windows, Linux and macOS support -- **Support for multiple renderers** - Zenyx can use Vulkan. OpenGL, and +- โœจ **Cross platform support** - Windows, Linux and macOS support +- ๐ŸŽฎ **Support for multiple renderers** - Zenyx can use Vulkan. OpenGL, and DirectX. In both 2D and 3D -- **Safe Performance** - Written in Rust for speed and safety -- **Intuitive Design** - User-friendly workflows and highly extensible tools -- **Flexible Scripting** - Easy to use scripting language without sacrificing +- โšก **Safe Performance** - Written in Rust for speed and safety +- ๐ŸŽฏ **Intuitive Design** - User-friendly workflows and highly extensible tools +- ๐Ÿ“ **Flexible Scripting** - Easy to use scripting language without sacrificing performance -- **Extensible Architecture** - Modular design for easy customization -- **Fully Featured Toolset** - Built in tools for playing spatial audio and +- ๐Ÿ”ง **Extensible Architecture** - Modular design for easy customization +- ๐Ÿ› ๏ธ **Fully Featured Toolset** - Built in tools for playing spatial audio and level editing -- **Real-time Editor** - Live preview and Incredible User experience -- **Asset Management** - Efficient resource handling system -- **Physics Integration** - Built-in physics simulation -- **Improved Scene Management** - Flexible scene organization tools +- ๐Ÿ–ฅ๏ธ **Real-time Editor** - Live preview and Incredible User experience +- ๐Ÿ“ฆ **Asset Management** - Efficient resource handling system +- ๐ŸŽฒ **Physics Integration** - Built-in physics simulation +- ๐Ÿ—‚๏ธ **Improved Scene Management** - Flexible scene organization tools + +## Star history โญ + + + + + + Star History Chart + + ## FAQ
-What platforms does Zenyx support? +What platforms does Zenyx support? Zenyx primarily supports Windows and Linux, with secondary support for macOS (requires MoltenVK). See the @@ -54,7 +64,7 @@ information.
-Is Zenyx ready for production use? +Is Zenyx ready for production use? Zenyx is currently in early development and is not yet ready for any simple use cases, but we're working hard to make it the best it can be before we release @@ -64,7 +74,7 @@ the [contribution section](CONTRIBUTING.md) for the ways you can help.
-How can I contribute to Zenyx? +How can I contribute to Zenyx? We welcome contributions! Please check our contribution guidelines and open a pull request on GitHub, if you arent a developer, you can also report bugs or @@ -74,7 +84,7 @@ For more information, please see the [Contributing section](#contributing).
-What are the system requirements? +What are the system requirements? Detailed system requirements will be provided as the engine matures. Currently, the only requirement is a modern OS and a system with atleast a semi-recent GPU. @@ -82,28 +92,32 @@ the only requirement is a modern OS and a system with atleast a semi-recent GPU.
-Is Zenyx free to use? +Is Zenyx free to use? Yes, Zenyx is open-source software licensed under MIT. You can Modify, Distribute, and use Zenyx for any purpose you wish.
-### What platforms will be supported? +## What platforms will be supported in the future? -| Platform | Support Priority | Status | Notes | -| ---------- | ---------------- | ------ | -------------------------------------------------------------------------------------------- | -| Windows | Primary | โœ… | | -| Linux/*BSD | Primary | โœ… | | -| macOS | Secondary | ๐ŸŒ‹ | Requires MoltenVK for both editor and exported games due to Wgpu's lack of support for Metal | +| Platform | Support Priority | Status | Notes | +| :--------: | :--------------: | :----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Windows | Primary | โœ… | | +| Linux/*BSD | Primary | โœ… | | +| macOS | Secondary | ๐ŸŒ‹ | Requires MoltenVK for both editor and exported games due to Wgpu's lack of support for Metal | +| Android | TBD | โ“ | | +| iOS | TBD | โ“+๐ŸŒ‹ | | +| Web | TBD | โ“ | | +| Consoles | Not planned | โ›” | Consoles require specific dev kits,proprietary licenses, and substantial fees that we (Caznix & the open source contributors) currently do not have the capacity for. This may change in the future but most likely will not be soon. | -## Documentation +# Documentation -### Getting Started +## Getting Started Zenyx is not yet ready to be used but this may change in the near future. -## Contributing +# Contributing We welcome contributions of any kind! If you're interested in contributing, please check out our CONTRIBUTING.md file for coding standards and guidelines. @@ -115,40 +129,40 @@ documentation. If you would like to contribute code to Zenyx, follow the instructions below for your platform: -### Prerequisites (all platforms) +## Prerequisites (all platforms) 0. Install [Rust](https://www.rust-lang.org/tools/install) 1. Install [git](https://git-scm.com/downloads) -### Building +## Building **1**. Clone the repository: -```bash -git clone https://github.com/Caznix/Zenyx.git -``` + git clone https://github.com/Caznix/Zenyx.git **2**. cd into the project directory -```bash -cd ./Zenyx -``` + cd ./Zenyx -**3**. Build the project +**3**. Build the project -```bash -cargo run -``` + cargo run -### macOS +## macOS โš ๏ธ as of this current time, we do not have any macOS contributors to write this guide, so follow the [General](#building) instructions and try to fill in the gaps, you can also ask questions on the [Discord]() -## Press kit -## Typefaces +## Subcrates +Zenyx is split into multiple crates to make it easier to maintain and update, you can find the crates in the `subcrates` directory, each crate has its own README file so you can find more information for each crate there. Here is a list of the crates: + +* [Zephyr (ECS)](subcrates/zephyr/README.md) + * Aims to provides a lightweight and boilerplate-free ECS framework for modular game development. +# Press kit + +## Typefaces Main typeface @@ -156,4 +170,6 @@ Main typeface ## Colored badges -Zenyx Logo \ No newline at end of file +Zenyx Logo + + diff --git a/subcrates/zephyr/README.md b/subcrates/zephyr/README.md new file mode 100644 index 0000000..ee6059c --- /dev/null +++ b/subcrates/zephyr/README.md @@ -0,0 +1,8 @@ +# Zephyr ECS
+ + +๐Ÿšง **Work In Progress** ๐Ÿšง + +This README is currently under construction. Please check back later for more detailed information about the project. + +
\ No newline at end of file From 17f904a6087be992088b6addefe88fbe0cb35e4e Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Tue, 3 Dec 2024 18:15:01 -0500 Subject: [PATCH 033/324] Create dependabot.yml --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5251d0f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "cargo" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From e3555ef52799a53912d4d2095a3f65df49c185d9 Mon Sep 17 00:00:00 2001 From: Caznix Date: Tue, 3 Dec 2024 18:19:40 -0500 Subject: [PATCH 034/324] Add release checking to rust workflow --- .github/workflows/rust.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f4bc5da..b0c08ac 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,6 +10,29 @@ env: CARGO_TERM_COLOR: always jobs: + # Credit to https://github.com/Far-Beyond-Dev/Horizon/blob/main/.github/workflows/main.yml + check-version: + runs-on: ubuntu-latest + outputs: + should_release: ${{ steps.check.outputs.should_release }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - name: Check if Cargo.toml version changed + id: check + run: | + CURRENT_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) + git checkout HEAD^1 + PREVIOUS_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) + if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then + echo "should_release=true" >> $GITHUB_OUTPUT + echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT + else + echo "should_release=false" >> $GITHUB_OUTPUT + fi build: strategy: fail-fast: false From 2cbf0277acfee29e213d7d45a50b238c4f4f0cbc Mon Sep 17 00:00:00 2001 From: eatmynerds Date: Thu, 5 Dec 2024 01:33:44 -0600 Subject: [PATCH 035/324] Control logging using key events --- Cargo.toml | 1 - engine/Cargo.toml | 5 +- engine/src/core/repl/commands.rs | 3 - engine/src/core/repl/repl.rs | 99 ++++++++++++++++++++++++++------ engine/src/main.rs | 31 ++-------- engine/src/utils/logger.rs | 7 ++- 6 files changed, 94 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a67ef2..3d77c9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,5 +32,4 @@ incremental = true codegen-units = 512 [workspace.dependencies] -anyhow = "1.0.93" zephyr = { path = "./subcrates/zephyr" } diff --git a/engine/Cargo.toml b/engine/Cargo.toml index de1bd1a..ac4eabe 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -6,16 +6,15 @@ edition = "2021" [dependencies] anyhow = "1.0.93" chrono = "0.4.38" -clap = { version = "4.5.21", features = ["derive"] } colored = "2.1.0" lazy_static = "1.5.0" log = "0.4.22" once_cell = "1.20.2" parking_lot = "0.12.3" regex = "1.11.1" -rustyline = "15.0.0" +rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } thiserror = "2.0.3" tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } wgpu = "23.0.1" winit = "0.30.5" -zephyr.workspace = true \ No newline at end of file +zephyr.workspace = true diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index d500ca8..97b95c8 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,6 +1,5 @@ use super::COMMAND_LIST; use std::process::Command; -use log::debug; pub(crate) fn say_hello() { println!("Hello, World!"); @@ -18,10 +17,8 @@ pub(crate) fn exit() { pub(crate) fn clear() { println!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { - debug!("target_os is windows"); Command::new("cmd").args(["/c", "cls"]).spawn() } else { - debug!("target_os is unix"); Command::new("clear").spawn() }; } diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 8ac6710..0b983a3 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -1,10 +1,68 @@ -use super::{commands, Callable, COMMAND_LIST}; +use crate::{ + core::repl::{commands, Callable, COMMAND_LIST}, + utils::logger::LOGGER, +}; use anyhow::Result; use chrono::Local; use colored::Colorize; use log::debug; use regex::Regex; -use rustyline::DefaultEditor; +use rustyline::{ + error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory, + Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper, + Hinter, KeyEvent, RepeatCount, Validator, +}; +use std::{ + borrow::Cow::{self, Borrowed, Owned}, + sync::{Arc, Mutex}, +}; + +#[derive(Completer, Helper, Hinter, Validator)] +struct MyHelper(#[rustyline(Hinter)] HistoryHinter); + +impl Highlighter for MyHelper { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> Cow<'b, str> { + if default { + Owned(prompt.bright_black().bold().to_string()) + } else { + Borrowed(prompt) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Owned(hint.bold().to_string()) + } +} + +#[derive(Clone)] +struct BacktickEventHandler { + toggle_state: Arc>, // Tracks whether logging is enabled or disabled +} + +impl ConditionalEventHandler for BacktickEventHandler { + fn handle(&self, evt: &Event, _: RepeatCount, _: bool, _: &EventContext) -> Option { + if let Some(k) = evt.get(0) { + if *k == KeyEvent::from('`') { + let mut state = self.toggle_state.lock().unwrap(); + if *state { + LOGGER.write_to_stdout(); + } else { + LOGGER.write_to_file("z.log"); + } + *state = !*state; + Some(Cmd::Noop) + } else { + None + } + } else { + unreachable!() + } + } +} fn register_commands() { COMMAND_LIST.add_command( @@ -42,13 +100,10 @@ fn register_commands() { None, ); - // EXAMPLE - // Adding aliases for commands + // Example of adding aliases for commands COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); } - - fn evaluate_command(input: &str) { if input.trim().is_empty() { return; @@ -79,35 +134,43 @@ fn evaluate_command(input: &str) { } } -pub async fn handle_repl() -> rustyline::Result<()> { - let mut line_editor = DefaultEditor::new()?; - if line_editor.load_history("history.txt").is_err() { +pub async fn handle_repl() -> Result<()> { + let mut rl = Editor::::new()?; + rl.set_helper(Some(MyHelper(HistoryHinter::new()))); + + rl.bind_sequence( + KeyEvent::from('`'), + EventHandler::Conditional(Box::new(BacktickEventHandler { + toggle_state: Arc::new(Mutex::new(false)), + })), + ); + + if rl.load_history("history.txt").is_err() { debug!("No previous history."); } - let time = Local::now().format("%H:%M:%S.%3f").to_string(); - let prompt = format!("[{}/{}] {}", time,"SHELL", ">>\t"); + register_commands(); loop { - let sig = line_editor.readline( - &prompt.bright_white() - ); + let time = Local::now().format("%H:%M:%S.%3f").to_string(); + let prompt = format!("[{}/{}] {}", time, "SHELL", ">>\t"); + let sig = rl.readline(&prompt.bright_white()); + match sig { Ok(line) => { - line_editor.add_history_entry(line.as_str())?; + rl.add_history_entry(line.as_str())?; evaluate_command(line.as_str()); } - Err(rustyline::error::ReadlineError::Interrupted) => { + Err(ReadlineError::Interrupted) => { println!("CTRL+C received, exiting..."); std::process::exit(0); } - Err(rustyline::error::ReadlineError::Eof) => { + Err(ReadlineError::Eof) => { println!("Error: CTRL+D pressed. Exiting..."); std::process::exit(0); } Err(err) => { println!("Error: {}", err); - } } } diff --git a/engine/src/main.rs b/engine/src/main.rs index 923c56e..fb575b6 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,48 +1,27 @@ use anyhow::Result; -use clap::Parser; -use log::{info, warn, LevelFilter}; +use log::LevelFilter; pub mod core; pub mod utils; use utils::{logger::LOGGER, splash::print_splash}; -#[derive(Parser)] -struct Cli { - #[arg(long, short, help = "Enable logging output")] - log: bool, -} - #[tokio::main] async fn main() -> Result<()> { let t = zephyr::add(0, 2); println!("{}", t); - let cli = Cli::parse(); log::set_logger(&*LOGGER).unwrap(); log::set_max_level(LevelFilter::Debug); print_splash(); - if cli.log { - info!("Initializing Engine with logging to stdout enabled"); + LOGGER.write_to_stdout(); - core::init_renderer()?; - } else { - LOGGER.write_to_stdout(); - info!("Initializing Engine with logging to stdout disabled"); - info!("Writing all logs to file z.log"); + let shell_thread = tokio::task::spawn(async { core::repl::repl::handle_repl().await }); - LOGGER.write_to_file("z.log"); - info!("Logging back to file z.log"); - - let shell_thread = tokio::task::spawn(async { - core::repl::repl::handle_repl().await - }); - - core::init_renderer()?; - shell_thread.await??; // LOL - Caz - } + core::init_renderer()?; + let _ = shell_thread.await?; Ok(()) } diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs index c542396..1830a45 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/utils/logger.rs @@ -47,7 +47,12 @@ impl DynamicLogger { impl Log for DynamicLogger { fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= Level::Debug + let target = metadata.target(); + let is_relevant_target = target.starts_with("wgpu") + || target.starts_with("winit") + || target.starts_with(env!("CARGO_PKG_NAME")); // Current crate name + + is_relevant_target && metadata.level() <= Level::Debug } fn log(&self, record: &Record) { From 642efa3012346cdedaab2374d0b4f7e3a8101a03 Mon Sep 17 00:00:00 2001 From: Caznix Date: Thu, 5 Dec 2024 10:46:36 -0500 Subject: [PATCH 036/324] print logging state upon change --- engine/src/core/repl/repl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 0b983a3..354cb89 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -48,6 +48,7 @@ impl ConditionalEventHandler for BacktickEventHandler { if let Some(k) = evt.get(0) { if *k == KeyEvent::from('`') { let mut state = self.toggle_state.lock().unwrap(); + println!("Stdout Logging: {}", if *state { "ON".green() } else { "OFF".red() }); if *state { LOGGER.write_to_stdout(); } else { From a25076c7a39cdeacba7d3305694aa3d9a29fe07a Mon Sep 17 00:00:00 2001 From: Caznix Date: Thu, 5 Dec 2024 11:00:08 -0500 Subject: [PATCH 037/324] add rust formatting rules --- engine/src/core/renderer/mod.rs | 6 +++--- engine/src/core/repl/commands.rs | 3 ++- engine/src/core/repl/mod.rs | 3 ++- engine/src/core/repl/repl.rs | 19 ++++++++++++------- engine/src/utils/logger.rs | 7 ++++--- rust-toolchain.toml | 2 ++ rustfmt.toml | 7 +++++++ 7 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs index 65486a3..7a9147b 100644 --- a/engine/src/core/renderer/mod.rs +++ b/engine/src/core/renderer/mod.rs @@ -1,8 +1,8 @@ pub mod ctx; -use ctx::WgpuCtx; - -use log::{debug, trace}; use std::sync::Arc; + +use ctx::WgpuCtx; +use log::{debug, trace}; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 97b95c8..518b79c 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,6 +1,7 @@ -use super::COMMAND_LIST; use std::process::Command; +use super::COMMAND_LIST; + pub(crate) fn say_hello() { println!("Hello, World!"); } diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 19a0cdf..87776ec 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,10 +1,11 @@ pub mod commands; pub mod repl; +use std::{borrow::Borrow, collections::HashMap, sync::Arc}; + use lazy_static::lazy_static; use log::{debug, info}; use parking_lot::RwLock; -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; lazy_static! { pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 354cb89..bb675ee 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -1,7 +1,8 @@ -use crate::{ - core::repl::{commands, Callable, COMMAND_LIST}, - utils::logger::LOGGER, +use std::{ + borrow::Cow::{self, Borrowed, Owned}, + sync::{Arc, Mutex}, }; + use anyhow::Result; use chrono::Local; use colored::Colorize; @@ -12,9 +13,10 @@ use rustyline::{ Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper, Hinter, KeyEvent, RepeatCount, Validator, }; -use std::{ - borrow::Cow::{self, Borrowed, Owned}, - sync::{Arc, Mutex}, + +use crate::{ + core::repl::{commands, Callable, COMMAND_LIST}, + utils::logger::LOGGER, }; #[derive(Completer, Helper, Hinter, Validator)] @@ -48,7 +50,10 @@ impl ConditionalEventHandler for BacktickEventHandler { if let Some(k) = evt.get(0) { if *k == KeyEvent::from('`') { let mut state = self.toggle_state.lock().unwrap(); - println!("Stdout Logging: {}", if *state { "ON".green() } else { "OFF".red() }); + println!( + "Stdout Logging: {}", + if *state { "ON".green() } else { "OFF".red() } + ); if *state { LOGGER.write_to_stdout(); } else { diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs index 1830a45..63b3dad 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/utils/logger.rs @@ -1,10 +1,11 @@ -use colored::Colorize; -use log::{Level, Log, Metadata, Record}; -use once_cell::sync::Lazy; use std::fs::OpenOptions; use std::io::{self, Write}; use std::sync::{Arc, Mutex}; +use colored::Colorize; +use log::{Level, Log, Metadata, Record}; +use once_cell::sync::Lazy; + pub static LOGGER: Lazy = Lazy::new(DynamicLogger::new); // A logger that dynamically switches between file and stdout diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..2678f0b --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +indent_style = "Block" +wrap_comments = true +format_code_in_doc_comments = true +trailing_comma = "Vertical" +group_imports = "StdExternalCrate" +reorder_impl_items = true +unstable_features = true \ No newline at end of file From 5c1c8e6adf9a52ec6c58fc051f79eb2fb932d0de Mon Sep 17 00:00:00 2001 From: Caznix Date: Thu, 5 Dec 2024 11:16:40 -0500 Subject: [PATCH 038/324] fix unwrap errors --- engine/src/core/mod.rs | 2 +- engine/src/core/renderer/ctx.rs | 8 ++++---- engine/src/core/repl/repl.rs | 5 +++-- engine/src/main.rs | 4 +++- engine/src/utils/logger.rs | 11 ++++++----- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index 5615f54..2e15980 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -6,7 +6,7 @@ use renderer::App; use winit::event_loop::{ControlFlow, EventLoop}; pub fn init_renderer() -> Result<()> { - let event_loop = EventLoop::new().unwrap(); + let event_loop = EventLoop::new()?; event_loop.set_control_flow(ControlFlow::Poll); let mut app = App::default(); Ok(event_loop.run_app(&mut app)?) diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs index fca1090..88ff9ae 100644 --- a/engine/src/core/renderer/ctx.rs +++ b/engine/src/core/renderer/ctx.rs @@ -41,7 +41,9 @@ impl<'window> WgpuCtx<'window> { let width = size.width.max(1); let height = size.height.max(1); - let surface_config = surface.get_default_config(&adapter, width, height).unwrap(); + let surface_config = surface + .get_default_config(&adapter, width, height) + .expect("Failed to get default surface configuration"); surface.configure(&device, &surface_config); Ok(WgpuCtx { @@ -55,9 +57,7 @@ impl<'window> WgpuCtx<'window> { pub fn new_blocking(window: Arc) -> Result> { tokio::task::block_in_place(|| { - tokio::runtime::Runtime::new() - .unwrap() - .block_on(async { WgpuCtx::new(window).await }) + tokio::runtime::Runtime::new()?.block_on(async { WgpuCtx::new(window).await }) }) } diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index bb675ee..21c19a3 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -1,12 +1,13 @@ use std::{ borrow::Cow::{self, Borrowed, Owned}, - sync::{Arc, Mutex}, + sync::Arc, }; use anyhow::Result; use chrono::Local; use colored::Colorize; use log::debug; +use parking_lot::Mutex; use regex::Regex; use rustyline::{ error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory, @@ -49,7 +50,7 @@ impl ConditionalEventHandler for BacktickEventHandler { fn handle(&self, evt: &Event, _: RepeatCount, _: bool, _: &EventContext) -> Option { if let Some(k) = evt.get(0) { if *k == KeyEvent::from('`') { - let mut state = self.toggle_state.lock().unwrap(); + let mut state = self.toggle_state.lock(); println!( "Stdout Logging: {}", if *state { "ON".green() } else { "OFF".red() } diff --git a/engine/src/main.rs b/engine/src/main.rs index fb575b6..1501b3f 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,3 +1,5 @@ +#![deny(clippy::unwrap_in_result)] + use anyhow::Result; use log::LevelFilter; @@ -11,7 +13,7 @@ async fn main() -> Result<()> { let t = zephyr::add(0, 2); println!("{}", t); - log::set_logger(&*LOGGER).unwrap(); + log::set_logger(&*LOGGER).ok(); log::set_max_level(LevelFilter::Debug); print_splash(); diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs index 63b3dad..bfe442d 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/utils/logger.rs @@ -1,10 +1,11 @@ use std::fs::OpenOptions; use std::io::{self, Write}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use colored::Colorize; use log::{Level, Log, Metadata, Record}; use once_cell::sync::Lazy; +use parking_lot::Mutex; pub static LOGGER: Lazy = Lazy::new(DynamicLogger::new); @@ -28,11 +29,11 @@ impl DynamicLogger { .open(file_path) .expect("Failed to open log file"); - *self.writer.lock().unwrap() = Box::new(file); + *self.writer.lock() = Box::new(file); } pub fn write_to_stdout(&self) { - *self.writer.lock().unwrap() = Box::new(io::stdout()); + *self.writer.lock() = Box::new(io::stdout()); } fn colorize_level(level: Level) -> colored::ColoredString { @@ -58,7 +59,7 @@ impl Log for DynamicLogger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { - let mut writer = self.writer.lock().unwrap(); + let mut writer = self.writer.lock(); let level = Self::colorize_level(record.level()); // Apply coloring writeln!( writer, @@ -72,7 +73,7 @@ impl Log for DynamicLogger { } fn flush(&self) { - let mut writer = self.writer.lock().unwrap(); + let mut writer = self.writer.lock(); writer.flush().unwrap(); } } From 1097c3cb4b93c57fbdd87214b125cadc64720ce2 Mon Sep 17 00:00:00 2001 From: Caznix Date: Thu, 5 Dec 2024 17:05:20 -0500 Subject: [PATCH 039/324] make repl support strings via both double and single quotes --- engine/src/core/repl/repl.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 21c19a3..0798286 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -111,6 +111,32 @@ fn register_commands() { COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); } +fn tokenize(command: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current_token = String::new(); + let mut inside_string = false; + + for char in command.chars() { + if char == '"' || char == '\'' { + inside_string = !inside_string; + } else if char.is_whitespace() && !inside_string { + if !current_token.is_empty() { + tokens.push(current_token); + current_token = String::new(); + } + } else { + current_token.push(char); + } + } + + // ignore the last token if it's empty. Who are we. Mojang? - Caz + if !current_token.is_empty() { + tokens.push(current_token); + } + + tokens +} + fn evaluate_command(input: &str) { if input.trim().is_empty() { return; @@ -126,13 +152,13 @@ fn evaluate_command(input: &str) { continue; } - let tokens: Vec<&str> = command.split_whitespace().collect(); + let tokens = tokenize(command); if tokens.is_empty() { - return; + println!("Empty command, skipping."); + continue; } - - let cmd_name = tokens[0]; - let args: Vec = tokens[1..].iter().map(|&s| s.to_string()).collect(); + let cmd_name = &tokens[0]; + let args: Vec = tokens[1..].iter().map(|s| s.to_string()).collect(); COMMAND_LIST.execute_command( cmd_name.to_string(), From 048bf80f52ae7256baefc87dd5afd1dd1d881289 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 10:56:18 -0500 Subject: [PATCH 040/324] change the look of autocompletion --- engine/src/core/repl/mod.rs | 3 +++ engine/src/core/repl/repl.rs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 87776ec..85dce9a 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -3,6 +3,7 @@ pub mod repl; use std::{borrow::Borrow, collections::HashMap, sync::Arc}; +use colored::Colorize; use lazy_static::lazy_static; use log::{debug, info}; use parking_lot::RwLock; @@ -128,6 +129,8 @@ impl CommandList { } (_, _) => command.execute(args), } + } else { + eprintln!("Command: '{}' was not found", name.red().italic()); } } } diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 0798286..ca5c598 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -37,7 +37,7 @@ impl Highlighter for MyHelper { } fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { - Owned(hint.bold().to_string()) + Owned(hint.italic().bright_black().to_string()) } } @@ -139,6 +139,7 @@ fn tokenize(command: &str) -> Vec { fn evaluate_command(input: &str) { if input.trim().is_empty() { + println!("Empty command, skipping. type 'help' for a list of commands."); return; } From 1fc704c7298fd3a898655531ae231d30581b3790 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 11:19:12 -0500 Subject: [PATCH 041/324] check command similarity --- engine/src/core/repl/mod.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 85dce9a..1545816 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -62,6 +62,24 @@ pub struct CommandList { pub aliases: RwLock>, } +fn check_similarity(target: &str, strings: &[String]) -> Option { + strings + .iter().filter(|s| { + target.chars().zip(s.chars()).any(|(c1, c2)| c1 == c2) + }) + .min_by_key(|s| { + let mut diff_count = 0; + for (c1, c2) in target.chars().zip(s.chars()) { + if c1 != c2 { + diff_count += 1; + } + } + diff_count += target.len().abs_diff(s.len()); + diff_count + }) + .cloned() +} + impl CommandList { fn new() -> Self { CommandList { @@ -131,6 +149,25 @@ impl CommandList { } } else { eprintln!("Command: '{}' was not found", name.red().italic()); + + let most_similar = check_similarity( + &name, + &self + .commands + .read() + .iter() + .map(|cmd| cmd.name.to_string()) + .collect::>(), + ); + match most_similar { + Some(similar) => { + eprintln!( + "Did you mean: '{}'?", + similar.green().italic().bold() + ); + } + None => {} + } } } } From 252527ebc030ba8fff92dd69f5d51c9f4f9ea125 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 15:54:31 -0500 Subject: [PATCH 042/324] exec .zenshell files + shell extensions Co-authored-by: Tristan Poland (Trident_For_U) --- Cargo.toml | 2 +- engine/Cargo.toml | 3 + engine/src/core/repl/commands.rs | 48 ++++++- engine/src/core/repl/mod.rs | 44 +++--- engine/src/core/repl/repl.rs | 22 ++- engine/src/main.rs | 16 ++- engine/src/utils/mathi.rs | 1 + engine/src/utils/mod.rs | 1 + engine/src/utils/splash.rs | 3 +- plugin_api/Cargo.toml | 25 ++++ plugin_api/build.rs | 224 +++++++++++++++++++++++++++++++ plugin_api/src/lib.rs | 77 +++++++++++ plugin_api/src/plugin_imports.rs | 18 +++ plugins/player_lib/Cargo.toml | 11 ++ plugins/player_lib/src/lib.rs | 36 +++++ test.zensh | 0 16 files changed, 494 insertions(+), 37 deletions(-) create mode 100644 engine/src/utils/mathi.rs create mode 100644 plugin_api/Cargo.toml create mode 100644 plugin_api/build.rs create mode 100644 plugin_api/src/lib.rs create mode 100644 plugin_api/src/plugin_imports.rs create mode 100644 plugins/player_lib/Cargo.toml create mode 100644 plugins/player_lib/src/lib.rs create mode 100644 test.zensh diff --git a/Cargo.toml b/Cargo.toml index 3d77c9d..6e96b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["engine", "subcrates/zephyr"] +members = ["engine", "subcrates/zephyr", "plugin_api"] [profile.dev] rpath = false diff --git a/engine/Cargo.toml b/engine/Cargo.toml index ac4eabe..3dc51af 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -11,6 +11,7 @@ lazy_static = "1.5.0" log = "0.4.22" once_cell = "1.20.2" parking_lot = "0.12.3" +rand = "0.8.5" regex = "1.11.1" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } thiserror = "2.0.3" @@ -18,3 +19,5 @@ tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } wgpu = "23.0.1" winit = "0.30.5" zephyr.workspace = true +plugin_api = { path = "../plugin_api" } +horizon-plugin-api = "0.1.13" diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 518b79c..dad0fa2 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,32 +1,66 @@ -use std::process::Command; +use std::{ffi::OsStr, process::Command}; + + + + + +use crate::core::repl::repl::evaluate_command; use super::COMMAND_LIST; -pub(crate) fn say_hello() { +pub(crate) fn say_hello() -> anyhow::Result<()> { println!("Hello, World!"); + Ok(()) } -pub(crate) fn echo(args: Vec) { - println!("{}", args.join(" ")) +pub(crate) fn echo(args: Vec) -> anyhow::Result<()> { + println!("{}", args.join(" ")); + Ok(()) } -pub(crate) fn exit() { +pub(crate) fn exit() -> anyhow::Result<()> { println!("Exiting..."); std::process::exit(0) } -pub(crate) fn clear() { +pub(crate) fn clear() -> anyhow::Result<()> { println!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { Command::new("cmd").args(["/c", "cls"]).spawn() } else { Command::new("clear").spawn() }; + Ok(()) } -pub(crate) fn help() { +pub(crate) fn help() -> anyhow::Result<()> { println!("Commands:"); for cmd in COMMAND_LIST.commands.read().iter() { println!("{:#}", cmd); } + Ok(()) +} +pub(crate) fn exec(args: Vec) -> anyhow::Result<()> { + let file_path_str = &args[0]; + let file_path = std::path::Path::new(file_path_str); + println!("File path: {:#?}", file_path); + + if !file_path.is_file() { + eprintln!("Error: File does not exist or is not a valid file: {}", file_path.display()); + return Ok(()); + } + if file_path.extension() != Some(OsStr::new("zensh")) { + eprintln!("Error: File is not a zenshell script: {:#?}", file_path.extension()); + //TODO: dont panic on this error + return Ok(()); + } + println!("Executing file: {:#?}", file_path); + let file_content = std::fs::read_to_string(file_path)?; + if file_content.is_empty() { + eprintln!("Error: file has no content. Is this a valid zenshell script?"); + return Ok(()); + } + println!("File contents:\n{file_content}"); + evaluate_command(file_content.trim())?; + Ok(()) } diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 1545816..0f84cfd 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -3,6 +3,7 @@ pub mod repl; use std::{borrow::Borrow, collections::HashMap, sync::Arc}; +use anyhow::Ok; use colored::Colorize; use lazy_static::lazy_static; use log::{debug, info}; @@ -14,8 +15,8 @@ lazy_static! { #[derive(Clone, Debug)] enum Callable { - Simple(fn()), - WithArgs(fn(Vec)), + Simple(fn() -> anyhow::Result<()>), + WithArgs(fn(Vec) -> anyhow::Result<()>), } #[derive(Debug)] @@ -27,7 +28,7 @@ pub struct Command { } impl Command { - pub fn execute(&self, args: Option>) { + pub fn execute(&self, args: Option>) -> anyhow::Result<()> { match &self.function { Callable::Simple(f) => { if let Some(args) = args { @@ -36,11 +37,16 @@ impl Command { args.len() ); } - f() + f()?; + Ok(()) } Callable::WithArgs(f) => match args { Some(args) => f(args), - None => eprintln!("Command expected arguments but received 0"), + None => { + Ok(()) + + + }, }, } } @@ -50,9 +56,14 @@ impl std::fmt::Display for Command { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - " {:<10} {}", + " {:<10} {}, {}", self.name, - self.description.unwrap_or("No description available") + self.description.unwrap_or("No description available"), + if self.arg_count > 0 { + format!("{} args", self.arg_count) + } else { + "No args".to_string() + } ) } } @@ -64,9 +75,8 @@ pub struct CommandList { fn check_similarity(target: &str, strings: &[String]) -> Option { strings - .iter().filter(|s| { - target.chars().zip(s.chars()).any(|(c1, c2)| c1 == c2) - }) + .iter() + .filter(|s| target.chars().zip(s.chars()).any(|(c1, c2)| c1 == c2)) .min_by_key(|s| { let mut diff_count = 0; for (c1, c2) in target.chars().zip(s.chars()) { @@ -111,6 +121,7 @@ impl CommandList { eprintln!("Alias: '{}' already exists", alias); return; } + let mut commands = self.commands.write(); if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { debug!("Adding alias: {} for cmd: {}", alias, command.name); @@ -122,7 +133,7 @@ impl CommandList { } } - fn execute_command(&self, mut name: String, args: Option>) { + fn execute_command(&self, mut name: String, args: Option>) -> anyhow::Result<()> { let commands = self.commands.borrow(); if self.aliases.read().contains_key(&name) { name = self @@ -144,6 +155,7 @@ impl CommandList { expected, args_vec.len() ); + Ok(()) } (_, _) => command.execute(args), } @@ -161,12 +173,12 @@ impl CommandList { ); match most_similar { Some(similar) => { - eprintln!( - "Did you mean: '{}'?", - similar.green().italic().bold() - ); + eprintln!("Did you mean: '{}'?", similar.green().italic().bold()); + Ok(()) + } + None => { + Ok(()) } - None => {} } } } diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index ca5c598..5ef467d 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -3,7 +3,8 @@ use std::{ sync::Arc, }; -use anyhow::Result; + + use chrono::Local; use colored::Colorize; use log::debug; @@ -106,6 +107,12 @@ fn register_commands() { Callable::Simple(commands::help), None, ); + COMMAND_LIST.add_command( + "exec", + Some("Executes a .nyx file."), + Callable::WithArgs(commands::exec), + Some(1), + ); // Example of adding aliases for commands COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); @@ -137,10 +144,10 @@ fn tokenize(command: &str) -> Vec { tokens } -fn evaluate_command(input: &str) { +pub fn evaluate_command(input: &str) -> anyhow::Result<()> { if input.trim().is_empty() { println!("Empty command, skipping. type 'help' for a list of commands."); - return; + return Ok(()); } let pattern = Regex::new(r"[;|\n]").unwrap(); @@ -164,11 +171,12 @@ fn evaluate_command(input: &str) { COMMAND_LIST.execute_command( cmd_name.to_string(), if args.is_empty() { None } else { Some(args) }, - ); - } + )?; + }; + Ok(()) } -pub async fn handle_repl() -> Result<()> { +pub async fn handle_repl() -> anyhow::Result<()> { let mut rl = Editor::::new()?; rl.set_helper(Some(MyHelper(HistoryHinter::new()))); @@ -193,7 +201,7 @@ pub async fn handle_repl() -> Result<()> { match sig { Ok(line) => { rl.add_history_entry(line.as_str())?; - evaluate_command(line.as_str()); + evaluate_command(line.as_str())?; } Err(ReadlineError::Interrupted) => { println!("CTRL+C received, exiting..."); diff --git a/engine/src/main.rs b/engine/src/main.rs index 1501b3f..1dc0458 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -2,6 +2,8 @@ use anyhow::Result; use log::LevelFilter; +use plugin_api::plugin_imports::*; +use plugin_api::{get_plugin, PluginManager}; pub mod core; pub mod utils; @@ -10,20 +12,26 @@ use utils::{logger::LOGGER, splash::print_splash}; #[tokio::main] async fn main() -> Result<()> { - let t = zephyr::add(0, 2); - println!("{}", t); + // Load all plugins log::set_logger(&*LOGGER).ok(); - log::set_max_level(LevelFilter::Debug); + log::set_max_level(LevelFilter::Off); print_splash(); + let mut plugin_manager = PluginManager::new(); + let plugins = plugin_manager.load_all(); + println!("Plugins loaded: {:?}", plugins); + + // Get the player plugin + let player_lib = get_plugin!(player_lib, plugins); + player_lib.test(); LOGGER.write_to_stdout(); let shell_thread = tokio::task::spawn(async { core::repl::repl::handle_repl().await }); core::init_renderer()?; - let _ = shell_thread.await?; + let _ = shell_thread.await??; Ok(()) } diff --git a/engine/src/utils/mathi.rs b/engine/src/utils/mathi.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/engine/src/utils/mathi.rs @@ -0,0 +1 @@ + diff --git a/engine/src/utils/mod.rs b/engine/src/utils/mod.rs index a0f8995..df309c5 100644 --- a/engine/src/utils/mod.rs +++ b/engine/src/utils/mod.rs @@ -1,2 +1,3 @@ pub mod logger; +pub mod mathi; pub mod splash; diff --git a/engine/src/utils/splash.rs b/engine/src/utils/splash.rs index 5f55560..051d11e 100644 --- a/engine/src/utils/splash.rs +++ b/engine/src/utils/splash.rs @@ -1,8 +1,7 @@ use colored::Colorize; -use log::info; pub fn print_splash() { - info!( + println!( "{}", format!( r#" diff --git a/plugin_api/Cargo.toml b/plugin_api/Cargo.toml new file mode 100644 index 0000000..f702587 --- /dev/null +++ b/plugin_api/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "plugin_api" +version = "0.3.0" +authors = ["Tristan Poland "] +description = "Horizon Plugins API" +license = "MIT" +edition = "2021" + +[build-dependencies] +toml_edit = "0.22.22" +pathdiff = "0.2.3" + +[dependencies] +async-trait = "0.1.83" +tokio = { version = "1.42.0", features = ["rt", "net", "rt-multi-thread"] } +uuid = "1.11.0" +socketioxide = "0.15.0" +horizon-plugin-api = "0.1.11" +# +# +# +# +###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ###### +player_lib = { path = "../plugins/player_lib", version = "0.1.0" } +###### END AUTO-GENERATED PLUGIN DEPENDENCIES ###### diff --git a/plugin_api/build.rs b/plugin_api/build.rs new file mode 100644 index 0000000..801cf3a --- /dev/null +++ b/plugin_api/build.rs @@ -0,0 +1,224 @@ +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::path::Path; + +fn main() { + // Get the path to the plugins directory + let plugins_dir = Path::new("..").join("plugins"); + + println!("cargo:warning=Looking for plugins in: {:?}", plugins_dir); + + // Ensure the plugins directory exists + if !plugins_dir.exists() { + println!( + "cargo:warning=Plugins directory not found at {:?}", + plugins_dir + ); + return; + } + + // Find all valid plugin directories + let plugin_paths = discover_plugins(&plugins_dir); + println!("cargo:warning=Found {} plugins", plugin_paths.len()); + + // Update Cargo.toml with plugin dependencies + if let Err(e) = update_cargo_toml(&plugin_paths) { + println!("cargo:warning=Failed to update Cargo.toml: {}", e); + std::process::exit(1); + } + + // Generate the plugin macro and imports files + if let Err(e) = generate_plugin_files(&plugin_paths) { + println!("cargo:warning=Failed to generate plugin files: {}", e); + std::process::exit(1); + } + + // Tell Cargo to rerun this script if the plugins directory or Cargo.toml + // changes + println!("cargo:rerun-if-changed=../plugins"); + println!("cargo:rerun-if-changed=Cargo.toml"); +} + +fn discover_plugins(plugins_dir: &Path) -> Vec<(String, String, String)> { + let mut valid_plugins = Vec::new(); + + if let Ok(entries) = fs::read_dir(plugins_dir) { + for entry in entries.flatten() { + let path = entry.path(); + + // Check if this is a directory + if !path.is_dir() { + continue; + } + + let plugin_name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("") + .to_string(); + + // Skip if empty plugin name + if plugin_name.is_empty() { + continue; + } + + // Check for required files/directories + let cargo_toml = path.join("Cargo.toml"); + let src_dir = path.join("src"); + let lib_rs = path.join("src").join("lib.rs"); + + if cargo_toml.exists() && src_dir.exists() && lib_rs.exists() { + // Read the Cargo.toml to get the package name and version + if let Ok(mut file) = File::open(&cargo_toml) { + let mut contents = String::new(); + if file.read_to_string(&mut contents).is_ok() { + // Simple parsing for package name and version + let mut name = None; + let mut version = None; + + for line in contents.lines() { + let line = line.trim(); + if line.starts_with("name") { + name = line + .split('=') + .nth(1) + .map(|s| s.trim().trim_matches('"').to_string()); + } else if line.starts_with("version") { + version = line + .split('=') + .nth(1) + .map(|s| s.trim().trim_matches('"').to_string()); + } + } + + if let (Some(name), Some(version)) = (name, version) { + println!( + "cargo:warning=Found plugin: {} v{} in {}", + name, version, plugin_name + ); + valid_plugins.push((name, version, plugin_name)); + } + } + } + } + } + } + + valid_plugins +} + +const AUTO_GENERATED_START: &str = + "###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ######"; +const AUTO_GENERATED_END: &str = "###### END AUTO-GENERATED PLUGIN DEPENDENCIES ######"; + +fn update_cargo_toml(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { + let cargo_path = "Cargo.toml"; + let mut contents = String::new(); + File::open(cargo_path)?.read_to_string(&mut contents)?; + + // Normalize line endings to \n for consistent processing + contents = contents.replace("\r\n", "\n"); + + // Find the boundaries of the auto-generated section + let start_idx = contents.find(AUTO_GENERATED_START); + let end_idx = contents.find(AUTO_GENERATED_END); + + let base_contents = match (start_idx, end_idx) { + (Some(start), Some(end)) => { + // If an existing section is found, take everything before it + contents[..start].trim_end().to_string() + } + _ => { + // If no section exists, use all current contents + contents.trim_end().to_string() + } + }; + + // Generate the new dependencies section + let mut new_section = String::new(); + new_section.push('\n'); // Add a newline before the section + new_section.push_str(AUTO_GENERATED_START); + new_section.push('\n'); // Add newline after start marker + + // Sort plugins by name for consistent output + let mut sorted_plugins = plugin_paths.to_vec(); + sorted_plugins.sort_by(|a, b| a.0.cmp(&b.0)); + + for (name, version, plugin_dir) in sorted_plugins { + new_section.push_str(&format!( + "{} = {{ path = \"../plugins/{}\", version = \"{}\" }}\n", + name, plugin_dir, version + )); + } + + new_section.push_str(AUTO_GENERATED_END); + + // Combine the base contents with the new section + let mut final_contents = base_contents; + final_contents.push_str(&new_section); + + // Ensure file ends with a single newline + if !final_contents.ends_with('\n') { + final_contents.push('\n'); + } + + // Write the updated Cargo.toml + fs::write(cargo_path, final_contents)?; + + Ok(()) +} + +fn generate_plugin_files(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { + // Create the output directory if it doesn't exist + let out_dir = Path::new("src"); + fs::create_dir_all(out_dir)?; + + // Then generate the imports file that uses the macro + generate_imports_file(plugin_paths, out_dir)?; + + Ok(()) +} + +fn generate_imports_file( + plugin_paths: &[(String, String, String)], + out_dir: &Path, +) -> std::io::Result<()> { + let mut file = fs::File::create(out_dir.join("plugin_imports.rs"))?; + + // Write the header + writeln!(file, "// This file is automatically generated by build.rs")?; + writeln!(file, "// Do not edit this file manually!\n")?; + writeln!( + file, + "use horizon_plugin_api::{{Pluginstate, LoadedPlugin, Plugin}};" + )?; + writeln!(file, "use std::collections::HashMap;\n")?; + for (i, (name, _, _)) in plugin_paths.iter().enumerate() { + write!(file, "pub use {};\n", name)?; + write!(file, "pub use {}::*;\n", name)?; + write!(file, "pub use {}::Plugin as {}_plugin;\n", name, name)?; + } + writeln!(file, "\n"); + + // Use the macro with discovered plugins + writeln!(file, "// Invoke the macro with all discovered plugins")?; + writeln!( + file, + "pub fn load_plugins() -> HashMap {{" + )?; + write!(file, " let plugins = crate::load_plugins!(")?; + + // Add each plugin to the macro invocation + for (i, (name, _, _)) in plugin_paths.iter().enumerate() { + if i > 0 { + write!(file, ",")?; + } + write!(file, "\n {}", name)?; + } + + writeln!(file, "\n );")?; + writeln!(file, " plugins")?; + writeln!(file, "}}")?; + + Ok(()) +} diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs new file mode 100644 index 0000000..426ed2e --- /dev/null +++ b/plugin_api/src/lib.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; + +pub use horizon_plugin_api::{get_plugin, LoadedPlugin, Plugin, Pluginstate, Version}; + +pub mod plugin_imports; + +// Define the current plugin version +const PLUGIN_API_VERSION: Version = Version { + major: 0, + minor: 1, + hotfix: 0, +}; + +#[derive(Clone)] +pub struct PluginManager { + plugins: HashMap, +} + +#[macro_export] +macro_rules! load_plugins { + ($($plugin:ident),* $(,)?) => { + { + let mut plugins = HashMap::new(); + $( + plugins.insert( + stringify!($plugin).to_string(), + (Pluginstate::ACTIVE, <$plugin::Plugin as $plugin::PluginConstruct>::new(plugins.clone())), + ); + )* + + plugins + } + }; +} + +impl PluginManager { + /// Allow instantiation of the ``PluginManager`` struct + pub fn new() -> PluginManager { + let new_manager = PluginManager { + plugins: HashMap::new(), + }; + + new_manager + } + + pub fn load_plugin(mut self, name: String, plugin: Plugin) { + self.plugins.insert(name, (Pluginstate::ACTIVE, plugin)); + } + + pub fn unload_plugin(mut self, name: String) { + self.plugins.remove(&name); + } + + pub fn get_plugins(self) -> HashMap { + self.plugins + } + + pub fn load_all(&mut self) -> HashMap { + self.plugins = plugin_imports::load_plugins(); + + //let my_test_plugin = get_plugin!(test_plugin, plugins); + //let result = my_test_plugin.thing(); + + let mut loaded_plugins = HashMap::new(); + for (name, (state, plugin)) in &self.plugins { + if *state == Pluginstate::ACTIVE { + loaded_plugins.insert( + name.clone(), + LoadedPlugin { + instance: plugin.clone(), + }, + ); + } + } + loaded_plugins + } +} diff --git a/plugin_api/src/plugin_imports.rs b/plugin_api/src/plugin_imports.rs new file mode 100644 index 0000000..7875142 --- /dev/null +++ b/plugin_api/src/plugin_imports.rs @@ -0,0 +1,18 @@ +// This file is automatically generated by build.rs +// Do not edit this file manually! + +use horizon_plugin_api::{Pluginstate, LoadedPlugin, Plugin}; +use std::collections::HashMap; + +pub use player_lib; +pub use player_lib::*; +pub use player_lib::Plugin as player_lib_plugin; + + +// Invoke the macro with all discovered plugins +pub fn load_plugins() -> HashMap { + let plugins = crate::load_plugins!( + player_lib + ); + plugins +} diff --git a/plugins/player_lib/Cargo.toml b/plugins/player_lib/Cargo.toml new file mode 100644 index 0000000..567b135 --- /dev/null +++ b/plugins/player_lib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "player_lib" +version = "0.1.0" +edition = "2021" + +[dependencies] +PebbleVault = "0.6.1" +horizon-plugin-api = "0.1.13" +horizon_data_types = "0.4.0" +socketioxide = "0.15.1" +parking_lot = "0.12.3" diff --git a/plugins/player_lib/src/lib.rs b/plugins/player_lib/src/lib.rs new file mode 100644 index 0000000..9fbadb7 --- /dev/null +++ b/plugins/player_lib/src/lib.rs @@ -0,0 +1,36 @@ +use std::collections::HashMap; + +pub use horizon_plugin_api::{LoadedPlugin, Plugin, Pluginstate}; + +// Define the trait properly +pub trait PluginAPI { + fn test(&self); +} + +pub trait PluginConstruct { + fn get_structs(&self) -> Vec<&str>; + // If you want default implementations, mark them with 'default' + fn new(plugins: HashMap) -> Plugin; +} + +// Implement constructor separately +impl PluginConstruct for Plugin { + fn new(plugins: HashMap) -> Plugin { + Plugin {} + } + + fn get_structs(&self) -> Vec<&str> { + vec!["MyPlayer"] + } +} + +// Implement the trait for Plugin +impl PluginAPI for Plugin { + fn test(&self) { + println!("test"); + } +} + +//----------------------------------------------------------------------------- +// Plugin Implementation +//----------------------------------------------------------------------------- diff --git a/test.zensh b/test.zensh new file mode 100644 index 0000000..e69de29 From 2999601046ffd826b7804a10ffbfe86f9cef2c21 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 16:20:16 -0500 Subject: [PATCH 043/324] fix tokio runtime stack overflow due to recursion --- engine/src/core/repl/commands.rs | 15 ++++++++++++++- test.zensh | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index dad0fa2..e9b756d 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,12 +1,18 @@ use std::{ffi::OsStr, process::Command}; - +use parking_lot::Mutex; +use lazy_static::lazy_static; use crate::core::repl::repl::evaluate_command; use super::COMMAND_LIST; +const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk - Caz + +lazy_static! { + static ref RECURSION_DEPTH: Mutex = parking_lot::Mutex::new(0); +} pub(crate) fn say_hello() -> anyhow::Result<()> { println!("Hello, World!"); @@ -41,6 +47,13 @@ pub(crate) fn help() -> anyhow::Result<()> { Ok(()) } pub(crate) fn exec(args: Vec) -> anyhow::Result<()> { + *RECURSION_DEPTH.lock() += 1; + if *RECURSION_DEPTH.lock() > MAX_RECURSION_DEPTH { + eprintln!("Maximum recursion depth reached. Aborting."); + *RECURSION_DEPTH.lock() = 0; + return Ok(()); + } + println!("Recursion depth: {}", *RECURSION_DEPTH.lock()); let file_path_str = &args[0]; let file_path = std::path::Path::new(file_path_str); println!("File path: {:#?}", file_path); diff --git a/test.zensh b/test.zensh index e69de29..0fbb7d4 100644 --- a/test.zensh +++ b/test.zensh @@ -0,0 +1,4 @@ +echo ping +echo pong +exec test.zensh +break \ No newline at end of file From f54145e1af544566b09e6484417f3e4bb318dbe3 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 16:55:49 -0500 Subject: [PATCH 044/324] fix formatting --- engine/src/core/repl/commands.rs | 22 +++++++++++++--------- engine/src/core/repl/mod.rs | 14 ++++---------- engine/src/core/repl/repl.rs | 6 ++---- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index e9b756d..cf59b48 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,14 +1,12 @@ use std::{ffi::OsStr, process::Command}; - -use parking_lot::Mutex; use lazy_static::lazy_static; - - -use crate::core::repl::repl::evaluate_command; +use parking_lot::Mutex; use super::COMMAND_LIST; -const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk - Caz +use crate::core::repl::repl::evaluate_command; +const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk - + // Caz lazy_static! { static ref RECURSION_DEPTH: Mutex = parking_lot::Mutex::new(0); @@ -59,11 +57,17 @@ pub(crate) fn exec(args: Vec) -> anyhow::Result<()> { println!("File path: {:#?}", file_path); if !file_path.is_file() { - eprintln!("Error: File does not exist or is not a valid file: {}", file_path.display()); - return Ok(()); + eprintln!( + "Error: File does not exist or is not a valid file: {}", + file_path.display() + ); + return Ok(()); } if file_path.extension() != Some(OsStr::new("zensh")) { - eprintln!("Error: File is not a zenshell script: {:#?}", file_path.extension()); + eprintln!( + "Error: File is not a zenshell script: {:#?}", + file_path.extension() + ); //TODO: dont panic on this error return Ok(()); } diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 0f84cfd..ebafd40 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -3,7 +3,7 @@ pub mod repl; use std::{borrow::Borrow, collections::HashMap, sync::Arc}; -use anyhow::Ok; +use anyhow::Context; use colored::Colorize; use lazy_static::lazy_static; use log::{debug, info}; @@ -42,11 +42,7 @@ impl Command { } Callable::WithArgs(f) => match args { Some(args) => f(args), - None => { - Ok(()) - - - }, + None => Ok(()), }, } } @@ -140,7 +136,7 @@ impl CommandList { .aliases .read() .get_key_value(&name) - .unwrap() + .context("Failed to get alias")? .1 .to_string(); @@ -176,9 +172,7 @@ impl CommandList { eprintln!("Did you mean: '{}'?", similar.green().italic().bold()); Ok(()) } - None => { - Ok(()) - } + None => Ok(()), } } } diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/repl.rs index 5ef467d..eae0dc9 100644 --- a/engine/src/core/repl/repl.rs +++ b/engine/src/core/repl/repl.rs @@ -3,8 +3,6 @@ use std::{ sync::Arc, }; - - use chrono::Local; use colored::Colorize; use log::debug; @@ -111,7 +109,7 @@ fn register_commands() { "exec", Some("Executes a .nyx file."), Callable::WithArgs(commands::exec), - Some(1), + Some(1), ); // Example of adding aliases for commands @@ -172,7 +170,7 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> { cmd_name.to_string(), if args.is_empty() { None } else { Some(args) }, )?; - }; + } Ok(()) } From 1cc723291b86ada684c7abbd999263c76e7f1d56 Mon Sep 17 00:00:00 2001 From: Caznix Date: Fri, 6 Dec 2024 16:57:52 -0500 Subject: [PATCH 045/324] apply clippy changes --- engine/src/core/renderer/ctx.rs | 2 +- engine/src/core/repl/commands.rs | 2 +- engine/src/core/repl/{repl.rs => exec.rs} | 0 engine/src/core/repl/mod.rs | 2 +- engine/src/main.rs | 4 ++-- engine/src/utils/logger.rs | 8 +++++++- 6 files changed, 12 insertions(+), 6 deletions(-) rename engine/src/core/repl/{repl.rs => exec.rs} (100%) diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs index 88ff9ae..d2045d8 100644 --- a/engine/src/core/renderer/ctx.rs +++ b/engine/src/core/renderer/ctx.rs @@ -80,7 +80,7 @@ impl<'window> WgpuCtx<'window> { .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index cf59b48..d41fe5f 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -4,7 +4,7 @@ use lazy_static::lazy_static; use parking_lot::Mutex; use super::COMMAND_LIST; -use crate::core::repl::repl::evaluate_command; +use crate::core::repl::exec::evaluate_command; const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk - // Caz diff --git a/engine/src/core/repl/repl.rs b/engine/src/core/repl/exec.rs similarity index 100% rename from engine/src/core/repl/repl.rs rename to engine/src/core/repl/exec.rs diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index ebafd40..6a935bc 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,5 +1,5 @@ pub mod commands; -pub mod repl; +pub mod exec; use std::{borrow::Borrow, collections::HashMap, sync::Arc}; diff --git a/engine/src/main.rs b/engine/src/main.rs index 1dc0458..4b2dc0e 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -28,10 +28,10 @@ async fn main() -> Result<()> { LOGGER.write_to_stdout(); - let shell_thread = tokio::task::spawn(async { core::repl::repl::handle_repl().await }); + let shell_thread = tokio::task::spawn(async { core::repl::exec::handle_repl().await }); core::init_renderer()?; - let _ = shell_thread.await??; + shell_thread.await??; Ok(()) } diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs index bfe442d..b927556 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/utils/logger.rs @@ -14,6 +14,12 @@ pub struct DynamicLogger { pub writer: Arc>>, } +impl Default for DynamicLogger { + fn default() -> Self { + Self::new() + } +} + impl DynamicLogger { pub fn new() -> Self { Self { @@ -24,7 +30,7 @@ impl DynamicLogger { pub fn write_to_file(&self, file_path: &str) { let file = OpenOptions::new() .create(true) - .write(true) + .append(true) .open(file_path) .expect("Failed to open log file"); From a9f8b43f50b2c9895ef0c5ef6d27b1764d353af4 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Mon, 9 Dec 2024 20:28:28 -0500 Subject: [PATCH 046/324] Improve repl autocorrect and error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Combine comparison algorithims for autocorrect * clear zephyr functions * remove redundant comments because co-pilot is stupid and i probably will never try to use it again * implement basic tab completion * fix unused items * Make workflow check code quality * split code quality into its own file * make action fail on bad formatting * change workflow to nightly * f it, code quality is considered breaking * fix forgetting to set toolchain back to nightly when rewriting workflow (๐Ÿ˜”) * Add condition for too little arguments * run cargo fmt * remove unneeded feature directive --- .github/workflows/code-quality.yml | 25 ++++++++ engine/src/core/repl/commands.rs | 5 +- engine/src/core/repl/exec.rs | 54 ++++++++++++++-- engine/src/core/repl/mod.rs | 98 +++++++++++++++++++++++++----- engine/src/main.rs | 1 - engine/src/utils/logger.rs | 1 - subcrates/zephyr/src/lib.rs | 13 ---- test.zensh | 15 +++-- 8 files changed, 170 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/code-quality.yml diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 0000000..763eaf5 --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,25 @@ +name: Code Quality + +on: [push, pull_request] + +jobs: + code-quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + components: clippy, rustfmt + + - name: Check formatting + run: cargo fmt -- --check + + - name: Run Clippy + run: cargo clippy -- -D warnings + + - name: Compilation Check + run: cargo check \ No newline at end of file diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index d41fe5f..4c1a8d9 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -5,8 +5,9 @@ use parking_lot::Mutex; use super::COMMAND_LIST; use crate::core::repl::exec::evaluate_command; -const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk - - // Caz +// increasing this value WILL cause a stack overflow +// attempt at your own risk - Caz +const MAX_RECURSION_DEPTH: usize = 500; lazy_static! { static ref RECURSION_DEPTH: Mutex = parking_lot::Mutex::new(0); diff --git a/engine/src/core/repl/exec.rs b/engine/src/core/repl/exec.rs index eae0dc9..bda5252 100644 --- a/engine/src/core/repl/exec.rs +++ b/engine/src/core/repl/exec.rs @@ -9,9 +9,9 @@ use log::debug; use parking_lot::Mutex; use regex::Regex; use rustyline::{ - error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory, - Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper, - Hinter, KeyEvent, RepeatCount, Validator, + completion::Completer, error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, + history::DefaultHistory, Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, + EventHandler, Helper, Hinter, KeyEvent, RepeatCount, Validator, }; use crate::{ @@ -19,8 +19,44 @@ use crate::{ utils::logger::LOGGER, }; +struct CommandCompleter; +impl CommandCompleter { + fn new() -> Self { + CommandCompleter {} + } +} + +impl Completer for CommandCompleter { + type Candidate = String; + + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &rustyline::Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + let binding = COMMAND_LIST.commands.read(); + let filtered_commands: Vec<_> = binding + .iter() + .filter(|command| command.name.starts_with(line)) + .collect(); + + let completions: Vec = filtered_commands + .iter() + .filter(|command| command.name.starts_with(&line[..pos])) + .map(|command| command.name[pos..].to_string()) + .collect(); + Ok((pos, completions)) + } +} + #[derive(Completer, Helper, Hinter, Validator)] -struct MyHelper(#[rustyline(Hinter)] HistoryHinter); +struct MyHelper { + #[rustyline(Hinter)] + hinter: HistoryHinter, + #[rustyline(Completer)] + completer: CommandCompleter, +} impl Highlighter for MyHelper { fn highlight_prompt<'b, 's: 'b, 'p: 'b>( @@ -142,6 +178,11 @@ fn tokenize(command: &str) -> Vec { tokens } +pub fn parse_command(input: &str) -> anyhow::Result> { + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec = pattern.split(input).map(|s| String::from(s)).collect(); + Ok(commands) +} pub fn evaluate_command(input: &str) -> anyhow::Result<()> { if input.trim().is_empty() { println!("Empty command, skipping. type 'help' for a list of commands."); @@ -176,7 +217,10 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> { pub async fn handle_repl() -> anyhow::Result<()> { let mut rl = Editor::::new()?; - rl.set_helper(Some(MyHelper(HistoryHinter::new()))); + rl.set_helper(Some(MyHelper { + hinter: HistoryHinter::new(), + completer: CommandCompleter::new(), + })); rl.bind_sequence( KeyEvent::from('`'), diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 6a935bc..698eb82 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -26,8 +26,22 @@ pub struct Command { function: Callable, pub arg_count: u8, } - +#[allow(private_interfaces)] impl Command { + pub fn new( + name: &'static str, + description: Option<&'static str>, + function: Callable, + arg_count: Option, + ) -> Self { + Command { + name, + description, + function, + arg_count: arg_count.unwrap_or(0), + } + } + pub fn execute(&self, args: Option>) -> anyhow::Result<()> { match &self.function { Callable::Simple(f) => { @@ -69,21 +83,63 @@ pub struct CommandList { pub aliases: RwLock>, } -fn check_similarity(target: &str, strings: &[String]) -> Option { - strings - .iter() - .filter(|s| target.chars().zip(s.chars()).any(|(c1, c2)| c1 == c2)) - .min_by_key(|s| { - let mut diff_count = 0; - for (c1, c2) in target.chars().zip(s.chars()) { - if c1 != c2 { - diff_count += 1; - } +fn hamming_distance(a: &str, b: &str) -> Option { + if a.len() != b.len() { + return None; + } + Some( + a.chars() + .zip(b.chars()) + .filter(|(char_a, char_b)| char_a != char_b) + .count(), + ) +} + +fn edit_distance(a: &str, b: &str) -> usize { + let m = a.len(); + let n = b.len(); + + let mut dp = vec![vec![0; n + 1]; m + 1]; + + for i in 0..=m { + for j in 0..=n { + if i == 0 { + dp[i][j] = j; + } else if j == 0 { + dp[i][j] = i; + } else if a.chars().nth(i - 1) == b.chars().nth(j - 1) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]); } - diff_count += target.len().abs_diff(s.len()); - diff_count - }) - .cloned() + } + } + + dp[m][n] +} + +fn check_similarity(target: &str, strings: &[String]) -> Option { + let max_hamming_distance: usize = 2; + let max_edit_distance: usize = 2; + let mut best_match: Option = None; + let mut best_distance = usize::MAX; + + for s in strings { + if let Some(hamming_dist) = hamming_distance(target, s) { + if hamming_dist <= max_hamming_distance && hamming_dist < best_distance { + best_distance = hamming_dist; + best_match = Some(s.clone()); + } + } else { + let edit_dist = edit_distance(target, s); + if edit_dist <= max_edit_distance && edit_dist < best_distance { + best_distance = edit_dist; + best_match = Some(s.clone()); + } + } + } + + best_match } impl CommandList { @@ -153,6 +209,13 @@ impl CommandList { ); Ok(()) } + (expected, None) => { + eprintln!( + "Command: '{}' expected {} arguments but received none", + name, expected + ); + Ok(()) + } (_, _) => command.execute(args), } } else { @@ -172,7 +235,10 @@ impl CommandList { eprintln!("Did you mean: '{}'?", similar.green().italic().bold()); Ok(()) } - None => Ok(()), + None => { + println!("Type 'help' for a list of commands"); + Ok(()) + } } } } diff --git a/engine/src/main.rs b/engine/src/main.rs index 4b2dc0e..767db02 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,5 +1,4 @@ #![deny(clippy::unwrap_in_result)] - use anyhow::Result; use log::LevelFilter; use plugin_api::plugin_imports::*; diff --git a/engine/src/utils/logger.rs b/engine/src/utils/logger.rs index b927556..765f0e8 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/utils/logger.rs @@ -30,7 +30,6 @@ impl DynamicLogger { pub fn write_to_file(&self, file_path: &str) { let file = OpenOptions::new() .create(true) - .append(true) .open(file_path) .expect("Failed to open log file"); diff --git a/subcrates/zephyr/src/lib.rs b/subcrates/zephyr/src/lib.rs index b93cf3f..8b13789 100644 --- a/subcrates/zephyr/src/lib.rs +++ b/subcrates/zephyr/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/test.zensh b/test.zensh index 0fbb7d4..f7725fc 100644 --- a/test.zensh +++ b/test.zensh @@ -1,4 +1,11 @@ -echo ping -echo pong -exec test.zensh -break \ No newline at end of file + + + + + + + + + +echo "Hello World" +echo "hello world"; hello \ No newline at end of file From 0ed2fda4c8d49e9f555e9be8f10960220e61dd66 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Thu, 19 Dec 2024 20:54:46 -0500 Subject: [PATCH 047/324] burn everything to the ground (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ --- .github/workflows/code-quality.yml | 25 -- .github/workflows/rust.yml | 39 ++- Cargo.toml | 5 +- engine/.gitignore | 1 - engine/Cargo.toml | 24 +- .../{utils/logger.rs => core/logger/mod.rs} | 2 +- engine/src/core/mod.rs | 14 +- engine/src/core/renderer/ctx.rs | 106 ------ engine/src/core/renderer/mod.rs | 61 ---- engine/src/core/repl/commands.rs | 311 ++++++++++++++---- engine/src/core/repl/handler.rs | 151 +++++++++ engine/src/core/repl/{exec.rs => input.rs} | 97 ++---- engine/src/core/repl/mod.rs | 249 +------------- engine/src/core/splash.rs | 29 ++ engine/src/main.rs | 39 +-- engine/src/utils/mathi.rs | 1 - engine/src/utils/mod.rs | 3 - engine/src/utils/splash.rs | 29 -- engine/test.zensh | 7 + plugin_api/Cargo.toml | 25 -- plugin_api/build.rs | 224 ------------- plugin_api/src/lib.rs | 77 ----- plugin_api/src/plugin_imports.rs | 18 - plugins/player_lib/Cargo.toml | 11 - plugins/player_lib/src/lib.rs | 36 -- subcrates/zen_core/Cargo.toml | 8 + subcrates/zen_core/src/lib.rs | 9 + subcrates/zephyr/Cargo.toml | 6 - subcrates/zephyr/README.md | 8 - subcrates/zephyr/src/lib.rs | 1 - 30 files changed, 528 insertions(+), 1088 deletions(-) delete mode 100644 .github/workflows/code-quality.yml delete mode 100644 engine/.gitignore rename engine/src/{utils/logger.rs => core/logger/mod.rs} (99%) delete mode 100644 engine/src/core/renderer/ctx.rs delete mode 100644 engine/src/core/renderer/mod.rs create mode 100644 engine/src/core/repl/handler.rs rename engine/src/core/repl/{exec.rs => input.rs} (68%) create mode 100644 engine/src/core/splash.rs delete mode 100644 engine/src/utils/mathi.rs delete mode 100644 engine/src/utils/mod.rs delete mode 100644 engine/src/utils/splash.rs create mode 100644 engine/test.zensh delete mode 100644 plugin_api/Cargo.toml delete mode 100644 plugin_api/build.rs delete mode 100644 plugin_api/src/lib.rs delete mode 100644 plugin_api/src/plugin_imports.rs delete mode 100644 plugins/player_lib/Cargo.toml delete mode 100644 plugins/player_lib/src/lib.rs create mode 100644 subcrates/zen_core/Cargo.toml create mode 100644 subcrates/zen_core/src/lib.rs delete mode 100644 subcrates/zephyr/Cargo.toml delete mode 100644 subcrates/zephyr/README.md delete mode 100644 subcrates/zephyr/src/lib.rs diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml deleted file mode 100644 index 763eaf5..0000000 --- a/.github/workflows/code-quality.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Code Quality - -on: [push, pull_request] - -jobs: - code-quality: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - components: clippy, rustfmt - - - name: Check formatting - run: cargo fmt -- --check - - - name: Run Clippy - run: cargo clippy -- -D warnings - - - name: Compilation Check - run: cargo check \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b0c08ac..b0a72d0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -13,26 +13,35 @@ jobs: # Credit to https://github.com/Far-Beyond-Dev/Horizon/blob/main/.github/workflows/main.yml check-version: runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - version: ${{ steps.check.outputs.version }} steps: - uses: actions/checkout@v3 with: fetch-depth: 2 - - - name: Check if Cargo.toml version changed - id: check + - name: Get binary name + id: binary run: | - CURRENT_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) - git checkout HEAD^1 - PREVIOUS_VERSION=$(grep -m1 version Cargo.toml | cut -d '"' -f2) - if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then - echo "should_release=true" >> $GITHUB_OUTPUT - echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - else - echo "should_release=false" >> $GITHUB_OUTPUT + BINARY_NAME=$(cargo metadata --format-version 1 | jq -r '.packages[0].targets[] | select(.kind[] | contains("bin")) | .name') + echo "name=$BINARY_NAME" >> "$GITHUB_OUTPUT" + - name: Check version change + id: version + run: | + git fetch + OLD_VERSION=$(git show HEAD^:Cargo.toml | grep -m 1 '^version = ' | cut -d '"' -f 2) + NEW_VERSION=$(grep -m 1 '^version = ' Cargo.toml | cut -d '"' -f 2) + if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" fi + - name: Create Release + if: steps.version.outputs.changed == 'true' + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.version.outputs.version }} + name: Release v${{ steps.version.outputs.version }} + files: target/release/${{ steps.binary.outputs.name }} + generate_release_notes: true + draft: false + prerelease: false build: strategy: fail-fast: false @@ -76,7 +85,7 @@ jobs: - name: ๐Ÿ”ง Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly override: true target: ${{ matrix.target }} profile: minimal diff --git a/Cargo.toml b/Cargo.toml index 6e96b37..dd8f14c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["engine", "subcrates/zephyr", "plugin_api"] +members = ["engine","subcrates/zen_core"] [profile.dev] rpath = false @@ -32,4 +32,5 @@ incremental = true codegen-units = 512 [workspace.dependencies] -zephyr = { path = "./subcrates/zephyr" } +lazy_static = "1.5.0" +parking_lot = "0.12.3" diff --git a/engine/.gitignore b/engine/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/engine/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 3dc51af..230f3b7 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -1,23 +1,17 @@ [package] -name = "zenyx" +name = "engine" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] -anyhow = "1.0.93" -chrono = "0.4.38" -colored = "2.1.0" -lazy_static = "1.5.0" +anyhow = "1.0.94" +chrono = "0.4.39" +colored = "2.2.0" + +lazy_static.workspace = true log = "0.4.22" once_cell = "1.20.2" -parking_lot = "0.12.3" -rand = "0.8.5" +parking_lot.workspace = true regex = "1.11.1" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } -thiserror = "2.0.3" -tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] } -wgpu = "23.0.1" -winit = "0.30.5" -zephyr.workspace = true -plugin_api = { path = "../plugin_api" } -horizon-plugin-api = "0.1.13" +tokio = { version = "1.42.0", features = ["macros", "parking_lot", "rt", "rt-multi-thread"] } diff --git a/engine/src/utils/logger.rs b/engine/src/core/logger/mod.rs similarity index 99% rename from engine/src/utils/logger.rs rename to engine/src/core/logger/mod.rs index 765f0e8..7cd8814 100644 --- a/engine/src/utils/logger.rs +++ b/engine/src/core/logger/mod.rs @@ -81,4 +81,4 @@ impl Log for DynamicLogger { let mut writer = self.writer.lock(); writer.flush().unwrap(); } -} +} \ No newline at end of file diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index 2e15980..a831946 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,13 +1,3 @@ -pub mod renderer; pub mod repl; - -use anyhow::Result; -use renderer::App; -use winit::event_loop::{ControlFlow, EventLoop}; - -pub fn init_renderer() -> Result<()> { - let event_loop = EventLoop::new()?; - event_loop.set_control_flow(ControlFlow::Poll); - let mut app = App::default(); - Ok(event_loop.run_app(&mut app)?) -} +pub mod logger; +pub mod splash; \ No newline at end of file diff --git a/engine/src/core/renderer/ctx.rs b/engine/src/core/renderer/ctx.rs deleted file mode 100644 index d2045d8..0000000 --- a/engine/src/core/renderer/ctx.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; - -use anyhow::Result; -use winit::window::Window; - -pub struct WgpuCtx<'window> { - device: wgpu::Device, - queue: wgpu::Queue, - surface: wgpu::Surface<'window>, - surface_config: wgpu::SurfaceConfiguration, - adapter: wgpu::Adapter, -} - -impl<'window> WgpuCtx<'window> { - pub async fn new(window: Arc) -> Result> { - let instnace = wgpu::Instance::default(); - let surface = instnace.create_surface(Arc::clone(&window))?; - let adapter = instnace - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - force_fallback_adapter: false, - compatible_surface: Some(&surface), - }) - .await - .expect("Failed to obtain render adapter"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()), - memory_hints: wgpu::MemoryHints::Performance, - }, - None, - ) - .await - .expect("Failed to create rendering device"); - - let size = window.inner_size(); - let width = size.width.max(1); - let height = size.height.max(1); - - let surface_config = surface - .get_default_config(&adapter, width, height) - .expect("Failed to get default surface configuration"); - surface.configure(&device, &surface_config); - - Ok(WgpuCtx { - device, - queue, - surface, - surface_config, - adapter, - }) - } - - pub fn new_blocking(window: Arc) -> Result> { - tokio::task::block_in_place(|| { - tokio::runtime::Runtime::new()?.block_on(async { WgpuCtx::new(window).await }) - }) - } - - pub fn resize(&mut self, new_size: (u32, u32)) { - let (width, height) = new_size; - self.surface_config.width = width.max(1); - self.surface_config.height = height.max(1); - self.surface.configure(&self.device, &self.surface_config); - } - - pub fn draw(&mut self) { - let surface_texture = self - .surface - .get_current_texture() - .expect("Failed to get surface texture"); - let view = surface_texture - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - { - let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - } - self.queue.submit(Some(encoder.finish())); - surface_texture.present(); - } -} diff --git a/engine/src/core/renderer/mod.rs b/engine/src/core/renderer/mod.rs deleted file mode 100644 index 7a9147b..0000000 --- a/engine/src/core/renderer/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -pub mod ctx; -use std::sync::Arc; - -use ctx::WgpuCtx; -use log::{debug, trace}; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::ActiveEventLoop; -use winit::window::{Window, WindowId}; - -#[derive(Default)] -pub struct App<'window> { - window: Option>, - ctx: Option>, -} - -impl ApplicationHandler for App<'_> { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - if self.window.is_none() { - let win_attr = Window::default_attributes().with_title("Zenyx"); - let window = Arc::new( - event_loop - .create_window(win_attr) - .expect("create window err."), - ); - self.window = Some(window.clone()); - let wgpu_ctx = WgpuCtx::new_blocking(window.clone()).unwrap(); - self.ctx = Some(wgpu_ctx) - } - } - - fn window_event( - &mut self, - event_loop: &ActiveEventLoop, - _window_id: WindowId, - event: WindowEvent, - ) { - match event { - WindowEvent::CloseRequested => { - event_loop.exit(); - debug!("Window closed, exiting"); - std::process::exit(0) - } - WindowEvent::RedrawRequested => { - if let Some(ctx) = &mut self.ctx { - ctx.draw(); - } - } - WindowEvent::Resized(size) => { - if let (Some(wgpu_ctx), Some(window)) = (&mut self.ctx, &self.window) { - wgpu_ctx.resize(size.into()); - window.request_redraw(); - - let size_str: String = size.height.to_string() + "x" + &size.width.to_string(); - debug!("Window resized to {:?}", size_str); - } - } - _ => trace!("Unhandled window event"), - } - } -} diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 4c1a8d9..0b1a38e 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,84 +1,253 @@ -use std::{ffi::OsStr, process::Command}; +use std::{fs, path::PathBuf, str::FromStr}; -use lazy_static::lazy_static; -use parking_lot::Mutex; +use anyhow::anyhow; +use parking_lot::RwLock; +use regex::Regex; -use super::COMMAND_LIST; -use crate::core::repl::exec::evaluate_command; -// increasing this value WILL cause a stack overflow -// attempt at your own risk - Caz -const MAX_RECURSION_DEPTH: usize = 500; +use crate::core::repl::handler::COMMAND_MANAGER; -lazy_static! { - static ref RECURSION_DEPTH: Mutex = parking_lot::Mutex::new(0); -} +use super::{handler::Command, input::tokenize}; -pub(crate) fn say_hello() -> anyhow::Result<()> { - println!("Hello, World!"); - Ok(()) -} +#[derive(Default)] +pub struct HelpCommand; -pub(crate) fn echo(args: Vec) -> anyhow::Result<()> { - println!("{}", args.join(" ")); - Ok(()) -} +impl Command for HelpCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + let manager = COMMAND_MANAGER.read(); + println!("Available commands:\n"); -pub(crate) fn exit() -> anyhow::Result<()> { - println!("Exiting..."); - std::process::exit(0) -} + for (_, command) in manager.get_commands() { + println!( + "Command: {}\n\tDescription: {}\n\tParameters: {}\n\tHelp: {}\n", + command.get_name(), + command.get_description(), + command.get_params(), + command.get_help() + ); + } -pub(crate) fn clear() -> anyhow::Result<()> { - println!("Clearing screen..., running command"); - let _result = if cfg!(target_os = "windows") { - Command::new("cmd").args(["/c", "cls"]).spawn() - } else { - Command::new("clear").spawn() - }; - Ok(()) -} - -pub(crate) fn help() -> anyhow::Result<()> { - println!("Commands:"); - for cmd in COMMAND_LIST.commands.read().iter() { - println!("{:#}", cmd); + if !manager.aliases.is_empty() { + println!("Aliases:"); + for (alias, command) in &manager.aliases { + println!("\t{} -> {}", alias, command); + } + } + Ok(()) } - Ok(()) -} -pub(crate) fn exec(args: Vec) -> anyhow::Result<()> { - *RECURSION_DEPTH.lock() += 1; - if *RECURSION_DEPTH.lock() > MAX_RECURSION_DEPTH { - eprintln!("Maximum recursion depth reached. Aborting."); - *RECURSION_DEPTH.lock() = 0; - return Ok(()); - } - println!("Recursion depth: {}", *RECURSION_DEPTH.lock()); - let file_path_str = &args[0]; - let file_path = std::path::Path::new(file_path_str); - println!("File path: {:#?}", file_path); - if !file_path.is_file() { - eprintln!( - "Error: File does not exist or is not a valid file: {}", - file_path.display() - ); - return Ok(()); + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("help") } - if file_path.extension() != Some(OsStr::new("zensh")) { - eprintln!( - "Error: File is not a zenshell script: {:#?}", - file_path.extension() - ); - //TODO: dont panic on this error - return Ok(()); + + fn get_help(&self) -> String { + String::from("Displays a list of available commands and their descriptions.") } - println!("Executing file: {:#?}", file_path); - let file_content = std::fs::read_to_string(file_path)?; - if file_content.is_empty() { - eprintln!("Error: file has no content. Is this a valid zenshell script?"); - return Ok(()); + + fn get_params(&self) -> String { + String::from("No parameters required.") + } + + fn get_name(&self) -> String { + String::from("Help") } - println!("File contents:\n{file_content}"); - evaluate_command(file_content.trim())?; - Ok(()) } +#[derive(Default)] +pub struct ClearCommand; + +impl Command for ClearCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + println!("Clearing screen..., running command"); + let _result = if cfg!(target_os = "windows") { + std::process::Command::new("cmd").args(["/c", "cls"]).spawn() + } else { + std::process::Command::new("clear").spawn() + }; + Ok(()) + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("A simple command that clears the terminal") + } + + fn get_name(&self) -> String { + String::from("clear") + } + + fn get_help(&self) -> String { + String::from("Clears the terminal") + } + + fn get_params(&self) -> String { + String::from("None") + } +} + +#[derive(Default)] +pub struct ExitCommand; + +impl Command for ExitCommand { + fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { + match args { + Some(args) => { + + let exit_code = args[0].parse()?; + std::process::exit(exit_code); + // Ok(()) + }, + None => { + std::process::exit(0); + }, + } + } + + fn undo(&self) { + todo!() + } + + fn redo(&self) { + todo!() + } + + fn get_description(&self) -> String { + String::from("Gracefully exists the program") + } + + fn get_name(&self) -> String { + String::from("exit") + } + + fn get_help(&self) -> String { + String::from("Exits, probably") + } + + fn get_params(&self) -> String { + String::from("None") + } +} +#[derive(Default)] +pub struct ExecFile; + +impl Command for ExecFile { + fn execute(&self, args: Option>) -> Result<(),anyhow::Error> { + match args { + Some(args) => { + + let file_path = PathBuf::from_str(&args[0])?; + if file_path.extension().is_some() && file_path.extension().unwrap() != "zensh" { + return Err(anyhow!("Selected file was not a zensh file")); + } else { + let zscript = fs::read_to_string(file_path)?; + if let Ok(command) = eval(zscript) { + println!("{:#?}",command); + for (cmd_name,cmd_args) in command { + COMMAND_MANAGER.read().execute(&cmd_name, cmd_args)? + } + } + } + Ok(()) + }, + None => { + Err(anyhow!("Not enough argumentss")) + }, + } + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("Executes a file path") + } + + fn get_name(&self) -> String { + String::from("exec") + } + + fn get_help(&self) -> String { + String::from("this will read the contents of a .zensh file, evaluate it, and run its input") + } + + fn get_params(&self) -> String { + String::from("1: File path") + } +} + +fn eval(input: String) -> Result>)>, anyhow::Error> { + if input.trim().is_empty() { + return Err(anyhow!("Input was empty")); + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(&input).collect(); + let mut evaluted = vec![]; + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens = tokenize(command); + if tokens.is_empty() { + println!("Empty command, skipping."); + continue; + } + let cmd_name = &tokens[0]; + let args: Option> = if tokens.len() > 1 { + Some(tokens[1..].iter().map(|s| s.to_string()).collect()) + } else { + None + }; + evaluted.push((cmd_name.to_owned(),args)); + } + Ok(evaluted) +} + +#[derive(Default)] +pub struct CounterCommand { + counter: RwLock, +} + +impl Command for CounterCommand { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + // Increment the counter + let mut count = self.counter.write(); + *count += 1; + println!("CounterCommand executed. Current count: {}", *count); + Ok(()) + } + + fn undo(&self) { + println!("Undo CounterCommand."); + } + + fn redo(&self) { + println!("Redo CounterCommand."); + } + + fn get_description(&self) -> String { + String::from("counter") + } + + fn get_help(&self) -> String { + String::from("Increments a counter every time it's executed.") + } + + fn get_params(&self) -> String { + String::from("No parameters for CounterCommand.") + } + + fn get_name(&self) -> String { + String::from("count") + } +} \ No newline at end of file diff --git a/engine/src/core/repl/handler.rs b/engine/src/core/repl/handler.rs new file mode 100644 index 0000000..563368d --- /dev/null +++ b/engine/src/core/repl/handler.rs @@ -0,0 +1,151 @@ +use std::collections::HashMap; +use colored::Colorize; +use lazy_static::lazy_static; +use parking_lot::RwLock; +lazy_static! { + pub static ref COMMAND_MANAGER: RwLock = RwLock::new(CommandManager::init()); +} +#[macro_export] +macro_rules! commands { + [$($command:ty),*] => [ + $( + { + let mut manager = $crate::core::repl::handler::COMMAND_MANAGER.write(); + manager.add_command(Box::new(<$command>::default())); + } + )* + ]; +} + +#[macro_export] +macro_rules! alias { + ($($alias:expr => $command:expr),*) => { + $( + { + let mut manager = $crate::COMMAND_MANAGER.write(); + manager.add_alias($alias, $command); + } + )* + }; +} + + +fn hamming_distance(a: &str, b: &str) -> Option { + if a.len() != b.len() { + return None; + } + Some( + a.chars() + .zip(b.chars()) + .filter(|(char_a, char_b)| char_a != char_b) + .count(), + ) +} + +fn edit_distance(a: &str, b: &str) -> usize { + let m = a.len(); + let n = b.len(); + + let mut dp = vec![vec![0; n + 1]; m + 1]; + + for i in 0..=m { + for j in 0..=n { + if i == 0 { + dp[i][j] = j; + } else if j == 0 { + dp[i][j] = i; + } else if a.chars().nth(i - 1) == b.chars().nth(j - 1) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]); + } + } + } + + dp[m][n] +} + +fn check_similarity(target: &str) -> Option { + let max_hamming_distance: usize = 2; + let max_edit_distance: usize = 2; + let mut best_match: Option = None; + let mut best_distance = usize::MAX; + + for (cmd_name,_) in COMMAND_MANAGER.read().get_commands() { + if let Some(hamming_dist) = hamming_distance(target, cmd_name) { + if hamming_dist <= max_hamming_distance && hamming_dist < best_distance { + best_distance = hamming_dist; + best_match = Some(String::from(cmd_name)); + } + } else { + let edit_dist = edit_distance(target, cmd_name); + if edit_dist <= max_edit_distance && edit_dist < best_distance { + best_distance = edit_dist; + best_match = Some(String::from(cmd_name)); + } + } + } + + best_match +} + +pub struct CommandManager { + pub commands: HashMap>, + pub aliases: HashMap, +} + +impl CommandManager { + pub fn init() -> CommandManager { + CommandManager { + commands: HashMap::new(), + aliases: HashMap::new(), + } + } + pub fn get_commands(&self) -> std::collections::hash_map::Iter<'_, String, Box> { + self.commands.iter() + } + pub fn execute_command(&self,command: &str,args: Option>) -> Result<(),anyhow::Error> { + if let Some(command) = self.commands.get(command) { + command.execute(args)?; + Ok(()) + } else { + println!("Command '{}' not found.", command); + let corrected_cmd = check_similarity(command); + if corrected_cmd.is_some() { + println!("Command: {} was not found. Did you mean {}?",command.red().bold(),corrected_cmd + .expect("A command was editied during execution, something has gone seriously wrong").green().bold().italic()); + return Ok(()); + } + Ok(()) + } + } + + pub fn execute(&self, command: &str,args: Option>) -> Result<(), anyhow::Error> { + match self.aliases.get(command) { + Some(command) => self.execute(command,args), + // check to see if we are using an alias or the command just doesnt exist + None => { + self.execute_command(command,args)?; + Ok(()) + }, + } + + } + + pub fn add_command(&mut self, command: Box) { + self.commands.insert(command.get_name().to_lowercase(), command); + } + pub fn add_alias(&mut self, alias: &str, command: &str) { + self.aliases.insert(alias.to_string(), command.to_string()); + } +} + +pub trait Command: Send + Sync { + fn execute(&self, args: Option>) -> Result<(),anyhow::Error>; + fn undo(&self); + fn redo(&self); + fn get_description(&self) -> String; + fn get_name(&self) -> String; + fn get_help(&self) -> String; + fn get_params(&self) -> String; +} \ No newline at end of file diff --git a/engine/src/core/repl/exec.rs b/engine/src/core/repl/input.rs similarity index 68% rename from engine/src/core/repl/exec.rs rename to engine/src/core/repl/input.rs index bda5252..7a5a52e 100644 --- a/engine/src/core/repl/exec.rs +++ b/engine/src/core/repl/input.rs @@ -9,15 +9,13 @@ use log::debug; use parking_lot::Mutex; use regex::Regex; use rustyline::{ - completion::Completer, error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, - history::DefaultHistory, Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, - EventHandler, Helper, Hinter, KeyEvent, RepeatCount, Validator, + Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper, + Hinter, KeyEvent, RepeatCount, Validator, completion::Completer, error::ReadlineError, + highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory, }; -use crate::{ - core::repl::{commands, Callable, COMMAND_LIST}, - utils::logger::LOGGER, -}; +use super::handler::COMMAND_MANAGER; +use crate::core::logger::LOGGER; struct CommandCompleter; impl CommandCompleter { @@ -35,21 +33,21 @@ impl Completer for CommandCompleter { pos: usize, _ctx: &rustyline::Context<'_>, ) -> rustyline::Result<(usize, Vec)> { - let binding = COMMAND_LIST.commands.read(); + let binding = COMMAND_MANAGER.read(); + let binding = binding.get_commands(); let filtered_commands: Vec<_> = binding - .iter() - .filter(|command| command.name.starts_with(line)) + .filter(|(command, _)| command.starts_with(line)) .collect(); let completions: Vec = filtered_commands .iter() - .filter(|command| command.name.starts_with(&line[..pos])) - .map(|command| command.name[pos..].to_string()) + .filter(|(command, _)| command.starts_with(&line[..pos])) + .map(|(command, _)| command[pos..].to_string()) .collect(); + println!("{:#?}", completions); Ok((pos, completions)) } } - #[derive(Completer, Helper, Hinter, Validator)] struct MyHelper { #[rustyline(Hinter)] @@ -106,53 +104,7 @@ impl ConditionalEventHandler for BacktickEventHandler { } } -fn register_commands() { - COMMAND_LIST.add_command( - "hello", - Some("Displays \"Hello World\"!"), - Callable::Simple(commands::say_hello), - None, - ); - - COMMAND_LIST.add_command( - "exit", - Some("Exits the application gracefully."), - Callable::Simple(commands::exit), - None, - ); - - COMMAND_LIST.add_command( - "clear", - Some("Clears the terminal screen."), - Callable::Simple(commands::clear), - None, - ); - - COMMAND_LIST.add_command( - "echo", - Some("Prints the provided arguments back to the terminal."), - Callable::WithArgs(commands::echo), - Some(1), // Requires at least one argument - ); - - COMMAND_LIST.add_command( - "help", - Some("Displays a list of all available commands."), - Callable::Simple(commands::help), - None, - ); - COMMAND_LIST.add_command( - "exec", - Some("Executes a .nyx file."), - Callable::WithArgs(commands::exec), - Some(1), - ); - - // Example of adding aliases for commands - COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string()); -} - -fn tokenize(command: &str) -> Vec { +pub fn tokenize(command: &str) -> Vec { let mut tokens = Vec::new(); let mut current_token = String::new(); let mut inside_string = false; @@ -180,12 +132,11 @@ fn tokenize(command: &str) -> Vec { pub fn parse_command(input: &str) -> anyhow::Result> { let pattern = Regex::new(r"[;|\n]").unwrap(); - let commands: Vec = pattern.split(input).map(|s| String::from(s)).collect(); + let commands: Vec = pattern.split(input).map(String::from).collect(); Ok(commands) } pub fn evaluate_command(input: &str) -> anyhow::Result<()> { if input.trim().is_empty() { - println!("Empty command, skipping. type 'help' for a list of commands."); return Ok(()); } @@ -205,12 +156,15 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> { continue; } let cmd_name = &tokens[0]; - let args: Vec = tokens[1..].iter().map(|s| s.to_string()).collect(); - - COMMAND_LIST.execute_command( - cmd_name.to_string(), - if args.is_empty() { None } else { Some(args) }, - )?; + let args: Option> = if tokens.len() > 1 { + Some(tokens[1..].iter().map(|s| s.to_string()).collect()) + } else { + None + }; + match COMMAND_MANAGER.read().execute(cmd_name, args) { + Ok(_) => continue, + Err(e) => return Err(e) + } } Ok(()) } @@ -233,8 +187,6 @@ pub async fn handle_repl() -> anyhow::Result<()> { debug!("No previous history."); } - register_commands(); - loop { let time = Local::now().format("%H:%M:%S.%3f").to_string(); let prompt = format!("[{}/{}] {}", time, "SHELL", ">>\t"); @@ -243,7 +195,10 @@ pub async fn handle_repl() -> anyhow::Result<()> { match sig { Ok(line) => { rl.add_history_entry(line.as_str())?; - evaluate_command(line.as_str())?; + match evaluate_command(line.as_str()) { + Ok(_) => continue, + Err(e) => println!("{e}"), + } } Err(ReadlineError::Interrupted) => { println!("CTRL+C received, exiting..."); diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 698eb82..a5b0613 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,245 +1,12 @@ +use commands::{ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand}; + +use crate::commands; + pub mod commands; -pub mod exec; +pub mod input; +pub mod handler; -use std::{borrow::Borrow, collections::HashMap, sync::Arc}; -use anyhow::Context; -use colored::Colorize; -use lazy_static::lazy_static; -use log::{debug, info}; -use parking_lot::RwLock; - -lazy_static! { - pub static ref COMMAND_LIST: Arc = Arc::new(CommandList::new()); -} - -#[derive(Clone, Debug)] -enum Callable { - Simple(fn() -> anyhow::Result<()>), - WithArgs(fn(Vec) -> anyhow::Result<()>), -} - -#[derive(Debug)] -pub struct Command { - pub name: &'static str, - pub description: Option<&'static str>, - function: Callable, - pub arg_count: u8, -} -#[allow(private_interfaces)] -impl Command { - pub fn new( - name: &'static str, - description: Option<&'static str>, - function: Callable, - arg_count: Option, - ) -> Self { - Command { - name, - description, - function, - arg_count: arg_count.unwrap_or(0), - } - } - - pub fn execute(&self, args: Option>) -> anyhow::Result<()> { - match &self.function { - Callable::Simple(f) => { - if let Some(args) = args { - eprintln!( - "Command expected 0 arguments but {} args were given. Ignoring..", - args.len() - ); - } - f()?; - Ok(()) - } - Callable::WithArgs(f) => match args { - Some(args) => f(args), - None => Ok(()), - }, - } - } -} - -impl std::fmt::Display for Command { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - " {:<10} {}, {}", - self.name, - self.description.unwrap_or("No description available"), - if self.arg_count > 0 { - format!("{} args", self.arg_count) - } else { - "No args".to_string() - } - ) - } -} - -pub struct CommandList { - pub commands: RwLock>, - pub aliases: RwLock>, -} - -fn hamming_distance(a: &str, b: &str) -> Option { - if a.len() != b.len() { - return None; - } - Some( - a.chars() - .zip(b.chars()) - .filter(|(char_a, char_b)| char_a != char_b) - .count(), - ) -} - -fn edit_distance(a: &str, b: &str) -> usize { - let m = a.len(); - let n = b.len(); - - let mut dp = vec![vec![0; n + 1]; m + 1]; - - for i in 0..=m { - for j in 0..=n { - if i == 0 { - dp[i][j] = j; - } else if j == 0 { - dp[i][j] = i; - } else if a.chars().nth(i - 1) == b.chars().nth(j - 1) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]); - } - } - } - - dp[m][n] -} - -fn check_similarity(target: &str, strings: &[String]) -> Option { - let max_hamming_distance: usize = 2; - let max_edit_distance: usize = 2; - let mut best_match: Option = None; - let mut best_distance = usize::MAX; - - for s in strings { - if let Some(hamming_dist) = hamming_distance(target, s) { - if hamming_dist <= max_hamming_distance && hamming_dist < best_distance { - best_distance = hamming_dist; - best_match = Some(s.clone()); - } - } else { - let edit_dist = edit_distance(target, s); - if edit_dist <= max_edit_distance && edit_dist < best_distance { - best_distance = edit_dist; - best_match = Some(s.clone()); - } - } - } - - best_match -} - -impl CommandList { - fn new() -> Self { - CommandList { - commands: RwLock::new(Vec::new()), - aliases: RwLock::new(HashMap::new()), - } - } - - fn add_command( - &self, - name: &'static str, - description: Option<&'static str>, - func: Callable, - arg_count: Option, - ) { - info!("Adding command: {}", name); - let mut commands = self.commands.write(); - - commands.push(Command { - name, - description, - function: func, - arg_count: arg_count.unwrap_or(0), - }); - } - - fn add_alias(&self, name: String, alias: String) { - if self.aliases.read().contains_key(&alias) { - eprintln!("Alias: '{}' already exists", alias); - return; - } - - let mut commands = self.commands.write(); - if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) { - debug!("Adding alias: {} for cmd: {}", alias, command.name); - self.aliases - .write() - .insert(alias.to_string(), name.to_string()); - } else { - eprintln!("Command: '{}' was not found", name); - } - } - - fn execute_command(&self, mut name: String, args: Option>) -> anyhow::Result<()> { - let commands = self.commands.borrow(); - if self.aliases.read().contains_key(&name) { - name = self - .aliases - .read() - .get_key_value(&name) - .context("Failed to get alias")? - .1 - .to_string(); - - debug!("changed to {}", &name); - } - if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { - match (command.arg_count, args.as_ref()) { - (expected, Some(args_vec)) if args_vec.len() != expected as usize => { - eprintln!( - "Command: '{}' expected {} arguments but received {}", - name, - expected, - args_vec.len() - ); - Ok(()) - } - (expected, None) => { - eprintln!( - "Command: '{}' expected {} arguments but received none", - name, expected - ); - Ok(()) - } - (_, _) => command.execute(args), - } - } else { - eprintln!("Command: '{}' was not found", name.red().italic()); - - let most_similar = check_similarity( - &name, - &self - .commands - .read() - .iter() - .map(|cmd| cmd.name.to_string()) - .collect::>(), - ); - match most_similar { - Some(similar) => { - eprintln!("Did you mean: '{}'?", similar.green().italic().bold()); - Ok(()) - } - None => { - println!("Type 'help' for a list of commands"); - Ok(()) - } - } - } - } +pub fn setup() { + commands!(HelpCommand,ClearCommand,ExitCommand,ExecFile,CounterCommand); } diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs new file mode 100644 index 0000000..7381a46 --- /dev/null +++ b/engine/src/core/splash.rs @@ -0,0 +1,29 @@ +use colored::Colorize; + +pub fn print_splash() { + println!( + "{}", + format!( + r#" + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ +โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ +โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ +โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ +โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ +โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“โ–“ + + Version: {} + "#, + env!("CARGO_PKG_VERSION").green() + ) + .bright_yellow() + ); +} \ No newline at end of file diff --git a/engine/src/main.rs b/engine/src/main.rs index 767db02..3beed07 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,36 +1,19 @@ -#![deny(clippy::unwrap_in_result)] -use anyhow::Result; -use log::LevelFilter; -use plugin_api::plugin_imports::*; -use plugin_api::{get_plugin, PluginManager}; +use core::{repl::{handler::COMMAND_MANAGER, input::handle_repl, setup}, splash}; + +use anyhow::Ok; + pub mod core; -pub mod utils; - -use utils::{logger::LOGGER, splash::print_splash}; #[tokio::main] -async fn main() -> Result<()> { - // Load all plugins +async fn main() -> anyhow::Result<()> { + setup(); + splash::print_splash(); + COMMAND_MANAGER.read().execute("help", None)?; + let t = tokio::spawn(handle_repl()); - log::set_logger(&*LOGGER).ok(); - log::set_max_level(LevelFilter::Off); - - print_splash(); - let mut plugin_manager = PluginManager::new(); - let plugins = plugin_manager.load_all(); - println!("Plugins loaded: {:?}", plugins); - - // Get the player plugin - let player_lib = get_plugin!(player_lib, plugins); - player_lib.test(); - - LOGGER.write_to_stdout(); - - let shell_thread = tokio::task::spawn(async { core::repl::exec::handle_repl().await }); - - core::init_renderer()?; - shell_thread.await??; + t.await??; Ok(()) + } diff --git a/engine/src/utils/mathi.rs b/engine/src/utils/mathi.rs deleted file mode 100644 index 8b13789..0000000 --- a/engine/src/utils/mathi.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/engine/src/utils/mod.rs b/engine/src/utils/mod.rs deleted file mode 100644 index df309c5..0000000 --- a/engine/src/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod logger; -pub mod mathi; -pub mod splash; diff --git a/engine/src/utils/splash.rs b/engine/src/utils/splash.rs deleted file mode 100644 index 051d11e..0000000 --- a/engine/src/utils/splash.rs +++ /dev/null @@ -1,29 +0,0 @@ -use colored::Colorize; - -pub fn print_splash() { - println!( - "{}", - format!( - r#" - &&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&&&&&& - && &&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&& -&&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&&& &&&&&&&&&&&&& -&&&&&&&&&&& &&&&&&&&&&&& - &&&&&&&&& && - &&&&&&&&& && - &&&&&&&&&&&&&&&&&&&&& - &&&&&&&&&&&&&&&&& - &&&&&&&&&&& - - Version: {} - "#, - env!("CARGO_PKG_VERSION").color("yellow") - ) - .bright_black() - ); -} diff --git a/engine/test.zensh b/engine/test.zensh new file mode 100644 index 0000000..5149af1 --- /dev/null +++ b/engine/test.zensh @@ -0,0 +1,7 @@ +count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count + +count +help + + +exit 102 \ No newline at end of file diff --git a/plugin_api/Cargo.toml b/plugin_api/Cargo.toml deleted file mode 100644 index f702587..0000000 --- a/plugin_api/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "plugin_api" -version = "0.3.0" -authors = ["Tristan Poland "] -description = "Horizon Plugins API" -license = "MIT" -edition = "2021" - -[build-dependencies] -toml_edit = "0.22.22" -pathdiff = "0.2.3" - -[dependencies] -async-trait = "0.1.83" -tokio = { version = "1.42.0", features = ["rt", "net", "rt-multi-thread"] } -uuid = "1.11.0" -socketioxide = "0.15.0" -horizon-plugin-api = "0.1.11" -# -# -# -# -###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ###### -player_lib = { path = "../plugins/player_lib", version = "0.1.0" } -###### END AUTO-GENERATED PLUGIN DEPENDENCIES ###### diff --git a/plugin_api/build.rs b/plugin_api/build.rs deleted file mode 100644 index 801cf3a..0000000 --- a/plugin_api/build.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::Path; - -fn main() { - // Get the path to the plugins directory - let plugins_dir = Path::new("..").join("plugins"); - - println!("cargo:warning=Looking for plugins in: {:?}", plugins_dir); - - // Ensure the plugins directory exists - if !plugins_dir.exists() { - println!( - "cargo:warning=Plugins directory not found at {:?}", - plugins_dir - ); - return; - } - - // Find all valid plugin directories - let plugin_paths = discover_plugins(&plugins_dir); - println!("cargo:warning=Found {} plugins", plugin_paths.len()); - - // Update Cargo.toml with plugin dependencies - if let Err(e) = update_cargo_toml(&plugin_paths) { - println!("cargo:warning=Failed to update Cargo.toml: {}", e); - std::process::exit(1); - } - - // Generate the plugin macro and imports files - if let Err(e) = generate_plugin_files(&plugin_paths) { - println!("cargo:warning=Failed to generate plugin files: {}", e); - std::process::exit(1); - } - - // Tell Cargo to rerun this script if the plugins directory or Cargo.toml - // changes - println!("cargo:rerun-if-changed=../plugins"); - println!("cargo:rerun-if-changed=Cargo.toml"); -} - -fn discover_plugins(plugins_dir: &Path) -> Vec<(String, String, String)> { - let mut valid_plugins = Vec::new(); - - if let Ok(entries) = fs::read_dir(plugins_dir) { - for entry in entries.flatten() { - let path = entry.path(); - - // Check if this is a directory - if !path.is_dir() { - continue; - } - - let plugin_name = path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or("") - .to_string(); - - // Skip if empty plugin name - if plugin_name.is_empty() { - continue; - } - - // Check for required files/directories - let cargo_toml = path.join("Cargo.toml"); - let src_dir = path.join("src"); - let lib_rs = path.join("src").join("lib.rs"); - - if cargo_toml.exists() && src_dir.exists() && lib_rs.exists() { - // Read the Cargo.toml to get the package name and version - if let Ok(mut file) = File::open(&cargo_toml) { - let mut contents = String::new(); - if file.read_to_string(&mut contents).is_ok() { - // Simple parsing for package name and version - let mut name = None; - let mut version = None; - - for line in contents.lines() { - let line = line.trim(); - if line.starts_with("name") { - name = line - .split('=') - .nth(1) - .map(|s| s.trim().trim_matches('"').to_string()); - } else if line.starts_with("version") { - version = line - .split('=') - .nth(1) - .map(|s| s.trim().trim_matches('"').to_string()); - } - } - - if let (Some(name), Some(version)) = (name, version) { - println!( - "cargo:warning=Found plugin: {} v{} in {}", - name, version, plugin_name - ); - valid_plugins.push((name, version, plugin_name)); - } - } - } - } - } - } - - valid_plugins -} - -const AUTO_GENERATED_START: &str = - "###### BEGIN AUTO-GENERATED PLUGIN DEPENDENCIES - DO NOT EDIT THIS SECTION ######"; -const AUTO_GENERATED_END: &str = "###### END AUTO-GENERATED PLUGIN DEPENDENCIES ######"; - -fn update_cargo_toml(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { - let cargo_path = "Cargo.toml"; - let mut contents = String::new(); - File::open(cargo_path)?.read_to_string(&mut contents)?; - - // Normalize line endings to \n for consistent processing - contents = contents.replace("\r\n", "\n"); - - // Find the boundaries of the auto-generated section - let start_idx = contents.find(AUTO_GENERATED_START); - let end_idx = contents.find(AUTO_GENERATED_END); - - let base_contents = match (start_idx, end_idx) { - (Some(start), Some(end)) => { - // If an existing section is found, take everything before it - contents[..start].trim_end().to_string() - } - _ => { - // If no section exists, use all current contents - contents.trim_end().to_string() - } - }; - - // Generate the new dependencies section - let mut new_section = String::new(); - new_section.push('\n'); // Add a newline before the section - new_section.push_str(AUTO_GENERATED_START); - new_section.push('\n'); // Add newline after start marker - - // Sort plugins by name for consistent output - let mut sorted_plugins = plugin_paths.to_vec(); - sorted_plugins.sort_by(|a, b| a.0.cmp(&b.0)); - - for (name, version, plugin_dir) in sorted_plugins { - new_section.push_str(&format!( - "{} = {{ path = \"../plugins/{}\", version = \"{}\" }}\n", - name, plugin_dir, version - )); - } - - new_section.push_str(AUTO_GENERATED_END); - - // Combine the base contents with the new section - let mut final_contents = base_contents; - final_contents.push_str(&new_section); - - // Ensure file ends with a single newline - if !final_contents.ends_with('\n') { - final_contents.push('\n'); - } - - // Write the updated Cargo.toml - fs::write(cargo_path, final_contents)?; - - Ok(()) -} - -fn generate_plugin_files(plugin_paths: &[(String, String, String)]) -> std::io::Result<()> { - // Create the output directory if it doesn't exist - let out_dir = Path::new("src"); - fs::create_dir_all(out_dir)?; - - // Then generate the imports file that uses the macro - generate_imports_file(plugin_paths, out_dir)?; - - Ok(()) -} - -fn generate_imports_file( - plugin_paths: &[(String, String, String)], - out_dir: &Path, -) -> std::io::Result<()> { - let mut file = fs::File::create(out_dir.join("plugin_imports.rs"))?; - - // Write the header - writeln!(file, "// This file is automatically generated by build.rs")?; - writeln!(file, "// Do not edit this file manually!\n")?; - writeln!( - file, - "use horizon_plugin_api::{{Pluginstate, LoadedPlugin, Plugin}};" - )?; - writeln!(file, "use std::collections::HashMap;\n")?; - for (i, (name, _, _)) in plugin_paths.iter().enumerate() { - write!(file, "pub use {};\n", name)?; - write!(file, "pub use {}::*;\n", name)?; - write!(file, "pub use {}::Plugin as {}_plugin;\n", name, name)?; - } - writeln!(file, "\n"); - - // Use the macro with discovered plugins - writeln!(file, "// Invoke the macro with all discovered plugins")?; - writeln!( - file, - "pub fn load_plugins() -> HashMap {{" - )?; - write!(file, " let plugins = crate::load_plugins!(")?; - - // Add each plugin to the macro invocation - for (i, (name, _, _)) in plugin_paths.iter().enumerate() { - if i > 0 { - write!(file, ",")?; - } - write!(file, "\n {}", name)?; - } - - writeln!(file, "\n );")?; - writeln!(file, " plugins")?; - writeln!(file, "}}")?; - - Ok(()) -} diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs deleted file mode 100644 index 426ed2e..0000000 --- a/plugin_api/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::collections::HashMap; - -pub use horizon_plugin_api::{get_plugin, LoadedPlugin, Plugin, Pluginstate, Version}; - -pub mod plugin_imports; - -// Define the current plugin version -const PLUGIN_API_VERSION: Version = Version { - major: 0, - minor: 1, - hotfix: 0, -}; - -#[derive(Clone)] -pub struct PluginManager { - plugins: HashMap, -} - -#[macro_export] -macro_rules! load_plugins { - ($($plugin:ident),* $(,)?) => { - { - let mut plugins = HashMap::new(); - $( - plugins.insert( - stringify!($plugin).to_string(), - (Pluginstate::ACTIVE, <$plugin::Plugin as $plugin::PluginConstruct>::new(plugins.clone())), - ); - )* - - plugins - } - }; -} - -impl PluginManager { - /// Allow instantiation of the ``PluginManager`` struct - pub fn new() -> PluginManager { - let new_manager = PluginManager { - plugins: HashMap::new(), - }; - - new_manager - } - - pub fn load_plugin(mut self, name: String, plugin: Plugin) { - self.plugins.insert(name, (Pluginstate::ACTIVE, plugin)); - } - - pub fn unload_plugin(mut self, name: String) { - self.plugins.remove(&name); - } - - pub fn get_plugins(self) -> HashMap { - self.plugins - } - - pub fn load_all(&mut self) -> HashMap { - self.plugins = plugin_imports::load_plugins(); - - //let my_test_plugin = get_plugin!(test_plugin, plugins); - //let result = my_test_plugin.thing(); - - let mut loaded_plugins = HashMap::new(); - for (name, (state, plugin)) in &self.plugins { - if *state == Pluginstate::ACTIVE { - loaded_plugins.insert( - name.clone(), - LoadedPlugin { - instance: plugin.clone(), - }, - ); - } - } - loaded_plugins - } -} diff --git a/plugin_api/src/plugin_imports.rs b/plugin_api/src/plugin_imports.rs deleted file mode 100644 index 7875142..0000000 --- a/plugin_api/src/plugin_imports.rs +++ /dev/null @@ -1,18 +0,0 @@ -// This file is automatically generated by build.rs -// Do not edit this file manually! - -use horizon_plugin_api::{Pluginstate, LoadedPlugin, Plugin}; -use std::collections::HashMap; - -pub use player_lib; -pub use player_lib::*; -pub use player_lib::Plugin as player_lib_plugin; - - -// Invoke the macro with all discovered plugins -pub fn load_plugins() -> HashMap { - let plugins = crate::load_plugins!( - player_lib - ); - plugins -} diff --git a/plugins/player_lib/Cargo.toml b/plugins/player_lib/Cargo.toml deleted file mode 100644 index 567b135..0000000 --- a/plugins/player_lib/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "player_lib" -version = "0.1.0" -edition = "2021" - -[dependencies] -PebbleVault = "0.6.1" -horizon-plugin-api = "0.1.13" -horizon_data_types = "0.4.0" -socketioxide = "0.15.1" -parking_lot = "0.12.3" diff --git a/plugins/player_lib/src/lib.rs b/plugins/player_lib/src/lib.rs deleted file mode 100644 index 9fbadb7..0000000 --- a/plugins/player_lib/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::collections::HashMap; - -pub use horizon_plugin_api::{LoadedPlugin, Plugin, Pluginstate}; - -// Define the trait properly -pub trait PluginAPI { - fn test(&self); -} - -pub trait PluginConstruct { - fn get_structs(&self) -> Vec<&str>; - // If you want default implementations, mark them with 'default' - fn new(plugins: HashMap) -> Plugin; -} - -// Implement constructor separately -impl PluginConstruct for Plugin { - fn new(plugins: HashMap) -> Plugin { - Plugin {} - } - - fn get_structs(&self) -> Vec<&str> { - vec!["MyPlayer"] - } -} - -// Implement the trait for Plugin -impl PluginAPI for Plugin { - fn test(&self) { - println!("test"); - } -} - -//----------------------------------------------------------------------------- -// Plugin Implementation -//----------------------------------------------------------------------------- diff --git a/subcrates/zen_core/Cargo.toml b/subcrates/zen_core/Cargo.toml new file mode 100644 index 0000000..0604d37 --- /dev/null +++ b/subcrates/zen_core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zen_core" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.94" +thiserror = "2.0.8" diff --git a/subcrates/zen_core/src/lib.rs b/subcrates/zen_core/src/lib.rs new file mode 100644 index 0000000..9b8cf04 --- /dev/null +++ b/subcrates/zen_core/src/lib.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + + +#[derive(Debug,Error)] +enum ZError { + #[error(transparent)] + Unknown(#[from] anyhow::Error) + +} \ No newline at end of file diff --git a/subcrates/zephyr/Cargo.toml b/subcrates/zephyr/Cargo.toml deleted file mode 100644 index 739c027..0000000 --- a/subcrates/zephyr/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "zephyr" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/subcrates/zephyr/README.md b/subcrates/zephyr/README.md deleted file mode 100644 index ee6059c..0000000 --- a/subcrates/zephyr/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Zephyr ECS
- - -๐Ÿšง **Work In Progress** ๐Ÿšง - -This README is currently under construction. Please check back later for more detailed information about the project. - -
\ No newline at end of file diff --git a/subcrates/zephyr/src/lib.rs b/subcrates/zephyr/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/subcrates/zephyr/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - From 82c8b4ad26e0fc923e7b4f98fb05f93f18f83339 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Thu, 19 Dec 2024 21:07:08 -0500 Subject: [PATCH 048/324] windows ARM workflow (#12) --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b0a72d0..5e06e29 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -62,7 +62,6 @@ jobs: - os: windows-latest arch: x86_64 target: x86_64-pc-windows-msvc - exclude: - os: windows-latest arch: aarch64 runs-on: ${{ matrix.os }} @@ -99,7 +98,8 @@ jobs: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - name: ๐Ÿงช Run tests - uses: actions-rs/cargo@v1 + if: matrix.target != 'aarch64-pc-windows-msvc' + uses: actions-rs/cargo@v1 with: command: test args: --target ${{ matrix.target }} From 9c20dccb068b71d5bcd972657c88e07d233aad23 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Thu, 19 Dec 2024 21:21:39 -0500 Subject: [PATCH 049/324] specify windows arm target (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ * make workflow use nightly * remove code quality checks temporarily * fix check version i think * build for windowsARM * forgot to specify target --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5e06e29..3febcbc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -64,6 +64,7 @@ jobs: target: x86_64-pc-windows-msvc - os: windows-latest arch: aarch64 + target: aarch64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: From a737369d111431e32dd7adb5821ff5d94362a71c Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Thu, 19 Dec 2024 21:29:33 -0500 Subject: [PATCH 050/324] change cargo.toml name (#14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ * make workflow use nightly * remove code quality checks temporarily * fix check version i think * build for windowsARM * forgot to specify target * change cargo.toml to zenyx to fix workflow From f76245835d4a632ed31b60636b3d2e96272b0f55 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Sat, 21 Dec 2024 14:35:55 -0500 Subject: [PATCH 051/324] polish repl (#17) * add less daunting panic message on release builds --- Cargo.toml | 10 +- engine/Cargo.toml | 11 ++- engine/src/core/ecs/mod.rs | 3 + engine/src/core/mod.rs | 7 +- engine/src/core/panic.rs | 152 +++++++++++++++++++++++++++++++ engine/src/core/repl/commands.rs | 140 ++++++++++++++++++---------- engine/src/core/repl/handler.rs | 76 +++++++++++++--- engine/src/core/repl/input.rs | 8 +- engine/src/core/repl/mod.rs | 19 +++- engine/src/core/splash.rs | 2 +- engine/src/core/workspace/mod.rs | 17 ++++ engine/src/main.rs | 33 +++++-- engine/test.zensh | 8 +- main.zensh | 1 + subcrates/zen_core/Cargo.toml | 7 ++ subcrates/zen_core/src/lib.rs | 6 +- test.zensh | 11 --- 17 files changed, 405 insertions(+), 106 deletions(-) create mode 100644 engine/src/core/ecs/mod.rs create mode 100644 engine/src/core/panic.rs create mode 100644 engine/src/core/workspace/mod.rs create mode 100644 main.zensh delete mode 100644 test.zensh diff --git a/Cargo.toml b/Cargo.toml index dd8f14c..cb0f4ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = ["engine","subcrates/zen_core"] [profile.dev] + rpath = false panic = "abort" lto = "off" @@ -12,8 +13,9 @@ overflow-checks = false incremental = true codegen-units = 512 + strip = "symbols" -debug-assertions = false +debug-assertions = true [profile.dev.package."*"] opt-level = 0 @@ -22,8 +24,9 @@ overflow-checks = false incremental = true codegen-units = 512 + strip = "symbols" -debug-assertions = false +debug-assertions = true [profile.dev.build-override] opt-level = 0 debug = false @@ -34,3 +37,6 @@ codegen-units = 512 [workspace.dependencies] lazy_static = "1.5.0" parking_lot = "0.12.3" + +[profile.release] +debug-assertions = false diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 230f3b7..82574fa 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -2,11 +2,14 @@ name = "engine" version = "0.1.0" edition = "2024" - +repository = "https://github.com/Zenyx-Engine/Zenyx" [dependencies] anyhow = "1.0.94" +backtrace = "0.3.74" chrono = "0.4.39" colored = "2.2.0" +crashreport = "1.0.1" +dirs-next = "2.0.0" lazy_static.workspace = true log = "0.4.22" @@ -15,3 +18,9 @@ parking_lot.workspace = true regex = "1.11.1" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } tokio = { version = "1.42.0", features = ["macros", "parking_lot", "rt", "rt-multi-thread"] } + +[profile.dev] +debug-assertions = true + +[profile.release] +debug-assertions = false diff --git a/engine/src/core/ecs/mod.rs b/engine/src/core/ecs/mod.rs new file mode 100644 index 0000000..f810534 --- /dev/null +++ b/engine/src/core/ecs/mod.rs @@ -0,0 +1,3 @@ +// struct ComponentRegistry { +// components +// } diff --git a/engine/src/core/mod.rs b/engine/src/core/mod.rs index a831946..7d8a407 100644 --- a/engine/src/core/mod.rs +++ b/engine/src/core/mod.rs @@ -1,3 +1,6 @@ -pub mod repl; +pub mod ecs; pub mod logger; -pub mod splash; \ No newline at end of file +pub mod panic; +pub mod repl; +pub mod splash; +pub mod workspace; diff --git a/engine/src/core/panic.rs b/engine/src/core/panic.rs new file mode 100644 index 0000000..9a14611 --- /dev/null +++ b/engine/src/core/panic.rs @@ -0,0 +1,152 @@ +use std::fmt::Write as FmtWrite; +use std::mem; + +use backtrace::Backtrace; +use parking_lot::Once; +use regex::Regex; + +static INIT: parking_lot::Once = Once::new(); + +//#[cfg(not(debug_assertions))] +pub fn set_panic_hook() { + use std::io::Write; + + use colored::Colorize; + + use crate::workspace; + INIT.call_once(|| { + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + let log_path = workspace::get_working_dir().unwrap_or_else(|_| { + default_hook(info); + std::process::exit(0); + }); + if !log_path.exists() { + std::fs::create_dir_all(&log_path).unwrap_or_else(|_| { + default_hook(info); + std::process::exit(0); + }) + } + let log_path = log_path.join("panic.log"); + + // human_panic::print_msg::(Some(log_path), &human_panic::Metadata::new("Zenyx", env!("CARGO_PKG_VERSION")) + // .support("https://github.com/Zenyx-Engine/Zenyx/issues") + // .authors("Zenyx community ")).unwrap(); + // // Call the default hook for any additional actions + + let mut file = std::fs::File::create(&log_path).unwrap_or_else(|_| { + default_hook(info); + std::process::exit(0); + }); + writeln!(file, "{}", info.payload_as_str().unwrap_or_else(|| { + default_hook(info); + std::process::exit(0); + })).unwrap_or_else(|_| { + default_hook(info); + std::process::exit(0); + }); + writeln!(file, "{}", render_backtrace().sanitize_path()).unwrap_or_else(|_| { + default_hook(info); + std::process::exit(0); + }); + let panic_msg = format!( +"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report. + +We have generated a report file at \"{}\". Submit an issue or email with the subject of \"Zenyx Crash Report\" and include the report as an attachment. + +To submit the crash report: + +https://github.com/Zenyx-Engine/Zenyx/issues + +We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports. + +Thank you kindly!",log_path.display()); + println!("{}",panic_msg.red().bold()); + println!("\nFor future reference, the error summary is as follows:\n{}",info.payload_as_str().unwrap_or_else(||{ + default_hook(info); + std::process::exit(0); + }).red().bold()); + std::process::exit(0); // There is nothing to be done at this point, it looks cleaner to exit instead of doing a natural panic + })); + }); +} +// THIS SNIPPET IS LICENSED UNDER THE APACHE LICENSE, VERSION 2.0 +// https://github.com/rust-cli/human-panic +// No changes were made to the original snippet +fn render_backtrace() -> String { + //We take padding for address and extra two letters + //to pad after index. + #[allow(unused_qualifications)] // needed for pre-1.80 MSRV + const HEX_WIDTH: usize = mem::size_of::() * 2 + 2; + //Padding for next lines after frame's address + const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6; + + let mut backtrace = String::new(); + + //Here we iterate over backtrace frames + //(each corresponds to function's stack) + //We need to print its address + //and symbol(e.g. function name), + //if it is available + let bt = Backtrace::new(); + let symbols = bt + .frames() + .iter() + .flat_map(|frame| { + let symbols = frame.symbols(); + if symbols.is_empty() { + vec![(frame, None, "".to_owned())] + } else { + symbols + .iter() + .map(|s| { + ( + frame, + Some(s), + s.name() + .map(|n| n.to_string()) + .unwrap_or_else(|| "".to_owned()), + ) + }) + .collect::>() + } + }) + .collect::>(); + let begin_unwind = "rust_begin_unwind"; + let begin_unwind_start = symbols + .iter() + .position(|(_, _, n)| n == begin_unwind) + .unwrap_or(0); + for (entry_idx, (frame, symbol, name)) in symbols.iter().skip(begin_unwind_start).enumerate() { + let ip = frame.ip(); + let _ = writeln!(backtrace, "{entry_idx:4}: {ip:HEX_WIDTH$?} - {name}"); + if let Some(symbol) = symbol { + //See if there is debug information with file name and line + if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) { + let _ = writeln!( + backtrace, + "{:3$}at {}:{}", + "", + file.display(), + line, + NEXT_SYMBOL_PADDING + ); + } + } + } + + backtrace +} + +trait Sanitize { + fn sanitize_path(&self) -> String; +} + +impl Sanitize for str { + fn sanitize_path(&self) -> String { + let username_pattern = r"(?i)(/home/|/Users/|\\Users\\)([^/\\]+)"; + let re = Regex::new(username_pattern).expect("Failed to sanitize path, aborting operation"); + + re.replace_all(self, "${1}").to_string() + } +} diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 0b1a38e..4708302 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -4,9 +4,8 @@ use anyhow::anyhow; use parking_lot::RwLock; use regex::Regex; -use crate::core::repl::handler::COMMAND_MANAGER; - use super::{handler::Command, input::tokenize}; +use crate::core::repl::handler::COMMAND_MANAGER; #[derive(Default)] pub struct HelpCommand; @@ -19,7 +18,7 @@ impl Command for HelpCommand { for (_, command) in manager.get_commands() { println!( "Command: {}\n\tDescription: {}\n\tParameters: {}\n\tHelp: {}\n", - command.get_name(), + command.get_name().to_lowercase(), command.get_description(), command.get_params(), command.get_help() @@ -62,7 +61,9 @@ impl Command for ClearCommand { fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { println!("Clearing screen..., running command"); let _result = if cfg!(target_os = "windows") { - std::process::Command::new("cmd").args(["/c", "cls"]).spawn() + std::process::Command::new("cmd") + .args(["/c", "cls"]) + .spawn() } else { std::process::Command::new("clear").spawn() }; @@ -97,14 +98,13 @@ impl Command for ExitCommand { fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { match args { Some(args) => { - let exit_code = args[0].parse()?; std::process::exit(exit_code); // Ok(()) - }, + } None => { std::process::exit(0); - }, + } } } @@ -136,27 +136,33 @@ impl Command for ExitCommand { pub struct ExecFile; impl Command for ExecFile { - fn execute(&self, args: Option>) -> Result<(),anyhow::Error> { + fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { match args { Some(args) => { - let file_path = PathBuf::from_str(&args[0])?; if file_path.extension().is_some() && file_path.extension().unwrap() != "zensh" { return Err(anyhow!("Selected file was not a zensh file")); } else { let zscript = fs::read_to_string(file_path)?; if let Ok(command) = eval(zscript) { - println!("{:#?}",command); - for (cmd_name,cmd_args) in command { - COMMAND_MANAGER.read().execute(&cmd_name, cmd_args)? + println!("{:#?}", command); + for (cmd_name, cmd_args) in command { + match COMMAND_MANAGER.read().execute(&cmd_name, cmd_args) { + Ok(_) => (), + Err(e) => { + println!( + "Error executing command returned an error: {}. Aborting script", + e + ); + break; + } + } } } } Ok(()) - }, - None => { - Err(anyhow!("Not enough argumentss")) - }, + } + None => Err(anyhow!("Not enough argumentss")), } } @@ -181,37 +187,6 @@ impl Command for ExecFile { } } -fn eval(input: String) -> Result>)>, anyhow::Error> { - if input.trim().is_empty() { - return Err(anyhow!("Input was empty")); - } - - let pattern = Regex::new(r"[;|\n]").unwrap(); - let commands: Vec<&str> = pattern.split(&input).collect(); - let mut evaluted = vec![]; - - for command in commands { - let command = command.trim(); - if command.is_empty() { - println!("Empty command, skipping."); - continue; - } - - let tokens = tokenize(command); - if tokens.is_empty() { - println!("Empty command, skipping."); - continue; - } - let cmd_name = &tokens[0]; - let args: Option> = if tokens.len() > 1 { - Some(tokens[1..].iter().map(|s| s.to_string()).collect()) - } else { - None - }; - evaluted.push((cmd_name.to_owned(),args)); - } - Ok(evaluted) -} #[derive(Default)] pub struct CounterCommand { @@ -250,4 +225,73 @@ impl Command for CounterCommand { fn get_name(&self) -> String { String::from("count") } -} \ No newline at end of file +} +#[derive(Default)] +pub struct PanicCommmand; +impl Command for PanicCommmand { + fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { + if args.is_some() { + let panic_msg = &args.unwrap()[0]; + panic!("{}", panic_msg) + } + let option: Option = None; + println!("Unwrapping None: {}", option.unwrap()); + panic!("Panic command was called") + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("causes a panic with your provided message") + } + + fn get_name(&self) -> String { + String::from("panic") + } + + fn get_help(&self) -> String { + String::from("") + } + + fn get_params(&self) -> String { + String::from("optional: panic msg") + } +} + + + + +fn eval(input: String) -> Result>)>, anyhow::Error> { + if input.trim().is_empty() { + return Err(anyhow!("Input was empty")); + } + + let pattern = Regex::new(r"[;|\n]").unwrap(); + let commands: Vec<&str> = pattern.split(&input).collect(); + let mut evaluted = vec![]; + + for command in commands { + let command = command.trim(); + if command.is_empty() { + println!("Empty command, skipping."); + continue; + } + + let tokens = tokenize(command); + if tokens.is_empty() { + println!("Empty command, skipping."); + continue; + } + let cmd_name = &tokens[0]; + let args: Option> = if tokens.len() > 1 { + Some(tokens[1..].iter().map(|s| s.to_string()).collect()) + } else { + None + }; + evaluted.push((cmd_name.to_owned(),args)); + } + Ok(evaluted) +} + diff --git a/engine/src/core/repl/handler.rs b/engine/src/core/repl/handler.rs index 563368d..4b7a340 100644 --- a/engine/src/core/repl/handler.rs +++ b/engine/src/core/repl/handler.rs @@ -29,7 +29,6 @@ macro_rules! alias { }; } - fn hamming_distance(a: &str, b: &str) -> Option { if a.len() != b.len() { return None; @@ -71,7 +70,7 @@ fn check_similarity(target: &str) -> Option { let mut best_match: Option = None; let mut best_distance = usize::MAX; - for (cmd_name,_) in COMMAND_MANAGER.read().get_commands() { + for (cmd_name, _) in COMMAND_MANAGER.read().get_commands() { if let Some(hamming_dist) = hamming_distance(target, cmd_name) { if hamming_dist <= max_hamming_distance && hamming_dist < best_distance { best_distance = hamming_dist; @@ -92,6 +91,7 @@ fn check_similarity(target: &str) -> Option { pub struct CommandManager { pub commands: HashMap>, pub aliases: HashMap, + pub categories: HashMap, } impl CommandManager { @@ -99,53 +99,99 @@ impl CommandManager { CommandManager { commands: HashMap::new(), aliases: HashMap::new(), + categories: HashMap::new(), } } + + pub fn add_category(&mut self, category: Category) { + self.categories.insert(category.name.clone(), category); + } + pub fn get_commands(&self) -> std::collections::hash_map::Iter<'_, String, Box> { self.commands.iter() } - pub fn execute_command(&self,command: &str,args: Option>) -> Result<(),anyhow::Error> { + + pub fn execute_command( + &self, + command: &str, + args: Option>, + ) -> Result<(), anyhow::Error> { if let Some(command) = self.commands.get(command) { command.execute(args)?; Ok(()) } else { - println!("Command '{}' not found.", command); let corrected_cmd = check_similarity(command); if corrected_cmd.is_some() { println!("Command: {} was not found. Did you mean {}?",command.red().bold(),corrected_cmd .expect("A command was editied during execution, something has gone seriously wrong").green().bold().italic()); - return Ok(()); } - Ok(()) + Err(anyhow::anyhow!("Command '{}' not found.", command)) } } - pub fn execute(&self, command: &str,args: Option>) -> Result<(), anyhow::Error> { + pub fn execute(&self, command: &str, args: Option>) -> Result<(), anyhow::Error> { match self.aliases.get(command) { - Some(command) => self.execute(command,args), + Some(command) => self.execute(command, args), // check to see if we are using an alias or the command just doesnt exist None => { - self.execute_command(command,args)?; + self.execute_command(command, args)?; Ok(()) - }, + } } - } pub fn add_command(&mut self, command: Box) { - self.commands.insert(command.get_name().to_lowercase(), command); + self.commands + .insert(command.get_name().to_lowercase(), command); } + + pub fn add_command_with_category(&mut self, command: Box, category: Category) { + if self.categories.contains_key(&category.name) { + let mut cmd_name = command.get_name().to_lowercase(); + cmd_name.insert_str(0, &format!("{}_", &&category.uid.to_lowercase())); + println!("{}", cmd_name); + self.commands.insert(cmd_name, command); + } else { + panic!("Category {} does not exist", category.name); + } + } + pub fn add_alias(&mut self, alias: &str, command: &str) { - self.aliases.insert(alias.to_string(), command.to_string()); + self.aliases.insert( + alias.to_string().to_lowercase(), + command.to_string().to_lowercase(), + ); + } +} +#[derive(Debug, Clone)] +pub struct Category { + // eg: Zenyx -> Z + // eg: core -> cr + // eg: exitcmd -> cr_exit + // eg: echo -> z_echo + pub uid: String, + // eg: Zenyx + pub name: String, + // eg: Zenyx internal commands + pub description: String, +} + +impl Category { + pub fn new(uid: &str, name: &str, description: &str) -> Self { + Self { + uid: uid.to_string(), + name: name.to_string(), + description: description.to_string(), + } } } pub trait Command: Send + Sync { - fn execute(&self, args: Option>) -> Result<(),anyhow::Error>; + fn execute(&self, args: Option>) -> Result<(), anyhow::Error>; fn undo(&self); fn redo(&self); fn get_description(&self) -> String; fn get_name(&self) -> String; fn get_help(&self) -> String; fn get_params(&self) -> String; -} \ No newline at end of file +} diff --git a/engine/src/core/repl/input.rs b/engine/src/core/repl/input.rs index 7a5a52e..33995e9 100644 --- a/engine/src/core/repl/input.rs +++ b/engine/src/core/repl/input.rs @@ -161,10 +161,10 @@ pub fn evaluate_command(input: &str) -> anyhow::Result<()> { } else { None }; - match COMMAND_MANAGER.read().execute(cmd_name, args) { - Ok(_) => continue, - Err(e) => return Err(e) - } + match COMMAND_MANAGER.read().execute(cmd_name, args) { + Ok(_) => continue, + Err(e) => return Err(e), + } } Ok(()) } diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index a5b0613..9e1dd63 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,12 +1,23 @@ -use commands::{ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand}; +use commands::{ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand, PanicCommmand}; +use handler::{COMMAND_MANAGER, Category}; use crate::commands; pub mod commands; -pub mod input; pub mod handler; - +pub mod input; pub fn setup() { - commands!(HelpCommand,ClearCommand,ExitCommand,ExecFile,CounterCommand); + commands!( + HelpCommand, + ClearCommand, + ExitCommand, + CounterCommand, + PanicCommmand + ); + let cat = Category::new("cr", "Core", "Core commands"); + COMMAND_MANAGER.write().add_category(cat.clone()); + COMMAND_MANAGER + .write() + .add_command_with_category(Box::new(ExecFile), cat.clone()); } diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs index 7381a46..94f4b5b 100644 --- a/engine/src/core/splash.rs +++ b/engine/src/core/splash.rs @@ -26,4 +26,4 @@ pub fn print_splash() { ) .bright_yellow() ); -} \ No newline at end of file +} diff --git a/engine/src/core/workspace/mod.rs b/engine/src/core/workspace/mod.rs new file mode 100644 index 0000000..d98bf5a --- /dev/null +++ b/engine/src/core/workspace/mod.rs @@ -0,0 +1,17 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result, anyhow}; + +pub fn get_working_dir() -> Result { + let mut dir = dirs_next::data_dir() + .ok_or(anyhow!("Expected working directory, found: None")) + .context("Could not fetch working dir")?; + dir.push("Zenyx"); + Ok(dir) +} + +pub fn get_data_dir() -> Result { + let mut dir = get_working_dir().context("Failed to obtain working dir")?; + dir.push("data"); + Ok(dir) +} diff --git a/engine/src/main.rs b/engine/src/main.rs index 3beed07..ccd41b9 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,18 +1,33 @@ -use core::{repl::{handler::COMMAND_MANAGER, input::handle_repl, setup}, splash}; +#![feature(panic_payload_as_str)] +use core::{ + panic::set_panic_hook, + repl::{handler::COMMAND_MANAGER, setup}, + splash, workspace, +}; use anyhow::Ok; - +use colored::Colorize; +use tokio::runtime; pub mod core; -#[tokio::main] -async fn main() -> anyhow::Result<()> { - setup(); - splash::print_splash(); - COMMAND_MANAGER.read().execute("help", None)?; - let t = tokio::spawn(handle_repl()); +fn main() -> anyhow::Result<()> { + if !cfg!(debug_assertions) { + println!("{}", "Debug mode disabled".bright_blue()); + set_panic_hook(); + } + let runtime = runtime::Builder::new_current_thread() + .enable_all() + .build()?; - t.await??; + runtime.block_on(async { + setup(); + splash::print_splash(); + COMMAND_MANAGER.read().execute("help", None)?; + let t = tokio::spawn(core::repl::input::handle_repl()); + t.await??; + Ok(()) + })?; Ok(()) diff --git a/engine/test.zensh b/engine/test.zensh index 5149af1..aa8771e 100644 --- a/engine/test.zensh +++ b/engine/test.zensh @@ -1,7 +1 @@ -count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count;count - -count -help - - -exit 102 \ No newline at end of file +cr_exec ./test.zensh diff --git a/main.zensh b/main.zensh new file mode 100644 index 0000000..95eee35 --- /dev/null +++ b/main.zensh @@ -0,0 +1 @@ +exit "test" \ No newline at end of file diff --git a/subcrates/zen_core/Cargo.toml b/subcrates/zen_core/Cargo.toml index 0604d37..d342c70 100644 --- a/subcrates/zen_core/Cargo.toml +++ b/subcrates/zen_core/Cargo.toml @@ -6,3 +6,10 @@ edition = "2024" [dependencies] anyhow = "1.0.94" thiserror = "2.0.8" +parking_lot.workspace = true + +[profile.dev] +debug-assertions = true + +[profile.release] +debug-assertions = false diff --git a/subcrates/zen_core/src/lib.rs b/subcrates/zen_core/src/lib.rs index 9b8cf04..1293081 100644 --- a/subcrates/zen_core/src/lib.rs +++ b/subcrates/zen_core/src/lib.rs @@ -1,9 +1,11 @@ -use thiserror::Error; +use thiserror::Error; + #[derive(Debug,Error)] enum ZError { #[error(transparent)] Unknown(#[from] anyhow::Error) -} \ No newline at end of file +} + diff --git a/test.zensh b/test.zensh deleted file mode 100644 index f7725fc..0000000 --- a/test.zensh +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - -echo "Hello World" -echo "hello world"; hello \ No newline at end of file From d064d027c6ccb7e57d3b20b1675d428d9096dd95 Mon Sep 17 00:00:00 2001 From: Cazdotsys Date: Sat, 21 Dec 2024 16:28:32 -0500 Subject: [PATCH 052/324] ZLUA REPL!!!!! (#18) --- engine/Cargo.toml | 1 + engine/src/core/logger/mod.rs | 2 +- engine/src/core/repl/commands.rs | 103 ++++++++++++++++++++++++++++--- engine/src/core/repl/handler.rs | 1 + engine/src/core/repl/mod.rs | 7 ++- engine/src/main.rs | 5 +- 6 files changed, 106 insertions(+), 13 deletions(-) diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 82574fa..e5a9ad6 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -13,6 +13,7 @@ dirs-next = "2.0.0" lazy_static.workspace = true log = "0.4.22" +mlua = { version = "0.10.2", features = ["anyhow", "lua54", "vendored"] } once_cell = "1.20.2" parking_lot.workspace = true regex = "1.11.1" diff --git a/engine/src/core/logger/mod.rs b/engine/src/core/logger/mod.rs index 7cd8814..765f0e8 100644 --- a/engine/src/core/logger/mod.rs +++ b/engine/src/core/logger/mod.rs @@ -81,4 +81,4 @@ impl Log for DynamicLogger { let mut writer = self.writer.lock(); writer.flush().unwrap(); } -} \ No newline at end of file +} diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index 4708302..f21e58f 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -1,8 +1,11 @@ use std::{fs, path::PathBuf, str::FromStr}; - +use colored::Colorize; +use mlua::prelude::*; use anyhow::anyhow; +use mlua::{Lua, MultiValue}; use parking_lot::RwLock; use regex::Regex; +use rustyline::{error::ReadlineError, DefaultEditor}; use super::{handler::Command, input::tokenize}; use crate::core::repl::handler::COMMAND_MANAGER; @@ -187,7 +190,6 @@ impl Command for ExecFile { } } - #[derive(Default)] pub struct CounterCommand { counter: RwLock, @@ -260,10 +262,7 @@ impl Command for PanicCommmand { } } - - - -fn eval(input: String) -> Result>)>, anyhow::Error> { +fn eval(input: String) -> Result>)>, anyhow::Error> { if input.trim().is_empty() { return Err(anyhow!("Input was empty")); } @@ -290,8 +289,98 @@ fn eval(input: String) -> Result>)>, anyhow::Erro } else { None }; - evaluted.push((cmd_name.to_owned(),args)); + evaluted.push((cmd_name.to_owned(), args)); } Ok(evaluted) } +#[derive(Default)] +pub struct ZLua; + +impl Command for ZLua { + fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { + let time = chrono::Local::now().format("%H:%M:%S.%3f").to_string(); + let prompt = format!("[{}/{}] {}", time, "ZLUA", ">>\t"); + let lua = Lua::new(); + let globals = lua.globals(); + let sum = lua.create_function(|_, (list1, list2): (i32, i32)| { + // This function just checks whether two string lists are equal, and in an inefficient way. + // Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return + // turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned. + Ok(list1 == list2) + })?; + globals.set("sum", sum)?; + let log = lua.create_function(|_, (msg,): (String,)| { + println!("{}", msg); + Ok(()) + })?; + globals.set("log", log)?; + let mut editor = DefaultEditor::new().expect("Failed to create editor"); + + loop { + let mut prompt = &prompt; + let mut line = String::new(); + + loop { + match editor.readline(prompt) { + Ok(input) => line.push_str(&input), + Err(ReadlineError::Interrupted) => { + println!("Exiting ZLUA shell..."); + return Ok(()); + } + Err(_) => {} + } + + match lua.load(&line).eval::() { + Ok(values) => { + editor.add_history_entry(line).unwrap(); + println!( + "{}", + values + .iter() + .map(|value| format!("{:#?}", value)) + .collect::>() + .join("\t") + ); + break; + } + Err(mlua::Error::SyntaxError { + incomplete_input: true, + .. + }) => { + // continue reading input and append it to `line` + line.push_str("\n"); // separate input lines + prompt = prompt; + } + + Err(e) => { + eprintln!("error: {}", e); + break; + } + } + } + } + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("Runs the ZLua interpreter") + } + + fn get_name(&self) -> String { + String::from("zlua") + } + + fn get_help(&self) -> String { + String::from("zlua") + } + + fn get_params(&self) -> String { + String::from("No parameters required.") + } +} + + diff --git a/engine/src/core/repl/handler.rs b/engine/src/core/repl/handler.rs index 4b7a340..d3892f7 100644 --- a/engine/src/core/repl/handler.rs +++ b/engine/src/core/repl/handler.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; + use colored::Colorize; use lazy_static::lazy_static; use parking_lot::RwLock; diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index 9e1dd63..a1d85e0 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,4 +1,6 @@ -use commands::{ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand, PanicCommmand}; +use commands::{ + ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand, PanicCommmand, ZLua, +}; use handler::{COMMAND_MANAGER, Category}; use crate::commands; @@ -13,7 +15,8 @@ pub fn setup() { ClearCommand, ExitCommand, CounterCommand, - PanicCommmand + PanicCommmand, + ZLua ); let cat = Category::new("cr", "Core", "Core commands"); COMMAND_MANAGER.write().add_category(cat.clone()); diff --git a/engine/src/main.rs b/engine/src/main.rs index ccd41b9..80dbecd 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -5,8 +5,8 @@ use core::{ splash, workspace, }; -use anyhow::Ok; use colored::Colorize; +use mlua::Lua; use tokio::runtime; pub mod core; @@ -26,9 +26,8 @@ fn main() -> anyhow::Result<()> { COMMAND_MANAGER.read().execute("help", None)?; let t = tokio::spawn(core::repl::input::handle_repl()); t.await??; - Ok(()) + Ok::<(), anyhow::Error>(()) })?; Ok(()) - } From 73aea4b08671572361c4b1df03b646b54a62cc2c Mon Sep 17 00:00:00 2001 From: GhostedGaming Date: Mon, 23 Dec 2024 23:14:11 -0500 Subject: [PATCH 053/324] Added a file for zlua and made a new function --- engine/src/core/repl/commands.rs | 91 ----------------------------- engine/src/core/repl/mod.rs | 6 +- engine/src/core/repl/zlua.rs | 98 ++++++++++++++++++++++++++++++++ engine/src/core/splash.rs | 4 ++ 4 files changed, 106 insertions(+), 93 deletions(-) create mode 100644 engine/src/core/repl/zlua.rs diff --git a/engine/src/core/repl/commands.rs b/engine/src/core/repl/commands.rs index f21e58f..55b6447 100644 --- a/engine/src/core/repl/commands.rs +++ b/engine/src/core/repl/commands.rs @@ -293,94 +293,3 @@ fn eval(input: String) -> Result>)>, anyhow::Err } Ok(evaluted) } -#[derive(Default)] -pub struct ZLua; - -impl Command for ZLua { - fn execute(&self, args: Option>) -> Result<(), anyhow::Error> { - let time = chrono::Local::now().format("%H:%M:%S.%3f").to_string(); - let prompt = format!("[{}/{}] {}", time, "ZLUA", ">>\t"); - let lua = Lua::new(); - let globals = lua.globals(); - let sum = lua.create_function(|_, (list1, list2): (i32, i32)| { - // This function just checks whether two string lists are equal, and in an inefficient way. - // Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return - // turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned. - Ok(list1 == list2) - })?; - globals.set("sum", sum)?; - let log = lua.create_function(|_, (msg,): (String,)| { - println!("{}", msg); - Ok(()) - })?; - globals.set("log", log)?; - let mut editor = DefaultEditor::new().expect("Failed to create editor"); - - loop { - let mut prompt = &prompt; - let mut line = String::new(); - - loop { - match editor.readline(prompt) { - Ok(input) => line.push_str(&input), - Err(ReadlineError::Interrupted) => { - println!("Exiting ZLUA shell..."); - return Ok(()); - } - Err(_) => {} - } - - match lua.load(&line).eval::() { - Ok(values) => { - editor.add_history_entry(line).unwrap(); - println!( - "{}", - values - .iter() - .map(|value| format!("{:#?}", value)) - .collect::>() - .join("\t") - ); - break; - } - Err(mlua::Error::SyntaxError { - incomplete_input: true, - .. - }) => { - // continue reading input and append it to `line` - line.push_str("\n"); // separate input lines - prompt = prompt; - } - - Err(e) => { - eprintln!("error: {}", e); - break; - } - } - } - } - } - - fn undo(&self) {} - - fn redo(&self) {} - - fn get_description(&self) -> String { - String::from("Runs the ZLua interpreter") - } - - fn get_name(&self) -> String { - String::from("zlua") - } - - fn get_help(&self) -> String { - String::from("zlua") - } - - fn get_params(&self) -> String { - String::from("No parameters required.") - } -} - - - diff --git a/engine/src/core/repl/mod.rs b/engine/src/core/repl/mod.rs index a1d85e0..d41fe90 100644 --- a/engine/src/core/repl/mod.rs +++ b/engine/src/core/repl/mod.rs @@ -1,13 +1,15 @@ use commands::{ - ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand, PanicCommmand, ZLua, + ClearCommand, CounterCommand, ExecFile, ExitCommand, HelpCommand, PanicCommmand }; use handler::{COMMAND_MANAGER, Category}; +use zlua::ZLua; use crate::commands; pub mod commands; pub mod handler; pub mod input; +pub mod zlua; pub fn setup() { commands!( @@ -16,7 +18,7 @@ pub fn setup() { ExitCommand, CounterCommand, PanicCommmand, - ZLua + zlua::ZLua ); let cat = Category::new("cr", "Core", "Core commands"); COMMAND_MANAGER.write().add_category(cat.clone()); diff --git a/engine/src/core/repl/zlua.rs b/engine/src/core/repl/zlua.rs new file mode 100644 index 0000000..ff911fa --- /dev/null +++ b/engine/src/core/repl/zlua.rs @@ -0,0 +1,98 @@ +use mlua::{Lua, MultiValue, Number}; +use rustyline::{error::ReadlineError, DefaultEditor}; +use crate::core::repl::handler::Command; + +#[derive(Default)] +pub struct ZLua; + +impl Command for ZLua { + fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { + let time = chrono::Local::now().format("%H:%M:%S.%3f").to_string(); + let prompt = format!("[{}/{}] {}", time, "ZLUA", ">>\t"); + let lua = Lua::new(); + let globals = lua.globals(); + let add = lua.create_function(|_, (number1,number2):(i32,i32)|{ + let result = number1 + number2; + println!("{result}"); + Ok(()) + })?; + let is_equal = lua.create_function(|_, (list1, list2): (i32, i32)| { + // This function just checks whether two string lists are equal, and in an inefficient way. + // Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return + // turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned. + Ok(list1 == list2) + })?; + globals.set("isequal", is_equal)?; + let log = lua.create_function(|_, (msg,): (String,)| { + println!("{}", msg); + Ok(()) + })?; + globals.set("log", log)?; + let mut editor = DefaultEditor::new().expect("Failed to create editor"); + globals.set("add", add)?; + + loop { + let mut prompt = &prompt; + let mut line = String::new(); + + loop { + match editor.readline(prompt) { + Ok(input) => line.push_str(&input), + Err(ReadlineError::Interrupted) => { + println!("Exiting ZLUA shell..."); + return Ok(()); + } + Err(_) => {} + } + + match lua.load(&line).eval::() { + Ok(values) => { + editor.add_history_entry(line).unwrap(); + println!( + "{}", + values + .iter() + .map(|value| format!("{:#?}", value)) + .collect::>() + .join("\t") + ); + break; + } + Err(mlua::Error::SyntaxError { + incomplete_input: true, + .. + }) => { + // continue reading input and append it to `line` + line.push_str("\n"); // separate input lines + prompt = prompt; + } + + Err(e) => { + eprintln!("error: {}", e); + break; + } + } + } + } + } + + fn undo(&self) {} + + fn redo(&self) {} + + fn get_description(&self) -> String { + String::from("Runs the ZLua interpreter") + } + + fn get_name(&self) -> String { + String::from("zlua") + } + + fn get_help(&self) -> String { + String::from("zlua") + } + + fn get_params(&self) -> String { + String::from("No parameters required.") + } +} diff --git a/engine/src/core/splash.rs b/engine/src/core/splash.rs index 94f4b5b..d882c87 100644 --- a/engine/src/core/splash.rs +++ b/engine/src/core/splash.rs @@ -1,6 +1,10 @@ use colored::Colorize; pub fn print_splash() { + println! +("# # +# Welcome to the Zenyx terminal # +# #"); println!( "{}", format!( From 9514c125bdaee751d926798fb9c21542ce94fd18 Mon Sep 17 00:00:00 2001 From: GhostedGaming Date: Tue, 24 Dec 2024 03:35:41 -0500 Subject: [PATCH 054/324] Working if statement partially --- engine/Cargo.toml | 2 ++ engine/src/core/repl/zlua.rs | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/engine/Cargo.toml b/engine/Cargo.toml index e5a9ad6..e52a077 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -20,6 +20,8 @@ regex = "1.11.1" rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } tokio = { version = "1.42.0", features = ["macros", "parking_lot", "rt", "rt-multi-thread"] } +rand = "0.8.5" + [profile.dev] debug-assertions = true diff --git a/engine/src/core/repl/zlua.rs b/engine/src/core/repl/zlua.rs index ff911fa..da3770e 100644 --- a/engine/src/core/repl/zlua.rs +++ b/engine/src/core/repl/zlua.rs @@ -1,4 +1,5 @@ -use mlua::{Lua, MultiValue, Number}; +use rand::Rng; +use mlua::{Function, Lua, MultiValue, Number, Value::Nil}; use rustyline::{error::ReadlineError, DefaultEditor}; use crate::core::repl::handler::Command; @@ -11,25 +12,40 @@ impl Command for ZLua { let prompt = format!("[{}/{}] {}", time, "ZLUA", ">>\t"); let lua = Lua::new(); let globals = lua.globals(); + //This just adds 2 numbers together let add = lua.create_function(|_, (number1,number2):(i32,i32)|{ let result = number1 + number2; println!("{result}"); Ok(()) })?; + globals.set("add", add)?; + let is_equal = lua.create_function(|_, (list1, list2): (i32, i32)| { // This function just checks whether two string lists are equal, and in an inefficient way. // Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return // turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned. Ok(list1 == list2) })?; - globals.set("isequal", is_equal)?; + globals.set("is_equal", is_equal)?; + + //This is just true or false let log = lua.create_function(|_, (msg,): (String,)| { println!("{}", msg); Ok(()) })?; globals.set("log", log)?; + + let if_statement = lua.create_function(|_, (condition, then_value, else_value): (bool, String, String)| { + if condition { + println!("{}", then_value); + } else { + println!("{}", else_value); + } + Ok(()) + })?; + globals.set("if_then", if_statement)?; + let mut editor = DefaultEditor::new().expect("Failed to create editor"); - globals.set("add", add)?; loop { let mut prompt = &prompt; From a4d6faa800d647fe5a542522b909ccb8e6d64bd5 Mon Sep 17 00:00:00 2001 From: GhostedGaming Date: Tue, 24 Dec 2024 13:09:20 -0500 Subject: [PATCH 055/324] Imported the whole library and now we have alot more functtions (sorry caz) --- engine/src/core/repl/zlua.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/engine/src/core/repl/zlua.rs b/engine/src/core/repl/zlua.rs index da3770e..c5c483b 100644 --- a/engine/src/core/repl/zlua.rs +++ b/engine/src/core/repl/zlua.rs @@ -1,5 +1,4 @@ -use rand::Rng; -use mlua::{Function, Lua, MultiValue, Number, Value::Nil}; +use mlua::{Function, Lua, LuaOptions, MultiValue, Number, Value::Nil}; use rustyline::{error::ReadlineError, DefaultEditor}; use crate::core::repl::handler::Command; @@ -10,7 +9,10 @@ impl Command for ZLua { fn execute(&self, _args: Option>) -> Result<(), anyhow::Error> { let time = chrono::Local::now().format("%H:%M:%S.%3f").to_string(); let prompt = format!("[{}/{}] {}", time, "ZLUA", ">>\t"); - let lua = Lua::new(); + let lua = Lua::new_with( + mlua::StdLib::ALL_SAFE, + LuaOptions::default() + )?; let globals = lua.globals(); //This just adds 2 numbers together let add = lua.create_function(|_, (number1,number2):(i32,i32)|{ @@ -28,22 +30,12 @@ impl Command for ZLua { })?; globals.set("is_equal", is_equal)?; - //This is just true or false let log = lua.create_function(|_, (msg,): (String,)| { - println!("{}", msg); - Ok(()) + println!("{}", msg); + Ok(()) })?; globals.set("log", log)?; - let if_statement = lua.create_function(|_, (condition, then_value, else_value): (bool, String, String)| { - if condition { - println!("{}", then_value); - } else { - println!("{}", else_value); - } - Ok(()) - })?; - globals.set("if_then", if_statement)?; let mut editor = DefaultEditor::new().expect("Failed to create editor"); From 915b638fa7c250bb0128c2817f52a90e5f7b04b9 Mon Sep 17 00:00:00 2001 From: GhostedGaming Date: Tue, 24 Dec 2024 13:40:14 -0500 Subject: [PATCH 056/324] Added better syntax --- engine/src/core/repl/zlua.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/engine/src/core/repl/zlua.rs b/engine/src/core/repl/zlua.rs index c5c483b..90203c5 100644 --- a/engine/src/core/repl/zlua.rs +++ b/engine/src/core/repl/zlua.rs @@ -23,12 +23,12 @@ impl Command for ZLua { globals.set("add", add)?; let is_equal = lua.create_function(|_, (list1, list2): (i32, i32)| { - // This function just checks whether two string lists are equal, and in an inefficient way. - // Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return - // turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned. + if list1 == 0 || list2 == 0 { + return Err(mlua::Error::RuntimeError("Zero values not allowed".to_string())); + } Ok(list1 == list2) })?; - globals.set("is_equal", is_equal)?; + globals.set("isEqual", is_equal)?; let log = lua.create_function(|_, (msg,): (String,)| { println!("{}", msg); @@ -36,7 +36,11 @@ impl Command for ZLua { })?; globals.set("log", log)?; - + let fail_safe = lua.create_function(|_,()|{ + println!("Failed"); + Ok(()) + })?; + globals.set("failSafe",fail_safe)?; let mut editor = DefaultEditor::new().expect("Failed to create editor"); loop { @@ -55,6 +59,15 @@ impl Command for ZLua { match lua.load(&line).eval::() { Ok(values) => { + for value in &values { + match value { + mlua::Value::Nil => println!("Got nil value"), + mlua::Value::Number(n) => println!("Got number: {}", n), + mlua::Value::String(s) => println!("Got string: {}", s.to_str()?), + mlua::Value::Boolean(b) => println!("Got boolean: {}", b), + _ => eprintln!("Got unexpected type: {:#?}", value) + } + } editor.add_history_entry(line).unwrap(); println!( "{}", @@ -76,9 +89,11 @@ impl Command for ZLua { } Err(e) => { - eprintln!("error: {}", e); + eprintln!("Error: {} at line {}", e, line.lines().count()); + eprintln!("Input that caused error: {}", line); break; } + } } } From 06ced36fad24f1713faa52dba5afb156cdab584d Mon Sep 17 00:00:00 2001 From: GhostedGaming Date: Tue, 24 Dec 2024 21:51:56 -0500 Subject: [PATCH 057/324] add rudimentary Text editor --- engine/Cargo.toml | 3 + engine/src/core/repl/Re-exports.rs | 9 ++ engine/text_editor/CodeEditorWindow.js | 27 ++++ engine/text_editor/Landing.js | 152 ++++++++++++++++++ engine/text_editor/LanguageDropdown.js | 18 +++ .../text_editor/constants/languageOptions.js | 20 +++ 6 files changed, 229 insertions(+) create mode 100644 engine/src/core/repl/Re-exports.rs create mode 100644 engine/text_editor/CodeEditorWindow.js create mode 100644 engine/text_editor/Landing.js create mode 100644 engine/text_editor/LanguageDropdown.js create mode 100644 engine/text_editor/constants/languageOptions.js diff --git a/engine/Cargo.toml b/engine/Cargo.toml index e52a077..c1369b9 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -22,6 +22,9 @@ tokio = { version = "1.42.0", features = ["macros", "parking_lot", "rt", "rt-mul rand = "0.8.5" +three-d = "0.18.0" +window = "0.5.0" + [profile.dev] debug-assertions = true diff --git a/engine/src/core/repl/Re-exports.rs b/engine/src/core/repl/Re-exports.rs new file mode 100644 index 0000000..8d7f394 --- /dev/null +++ b/engine/src/core/repl/Re-exports.rs @@ -0,0 +1,9 @@ +pub use renderer::*; +pub use window::*; +pub use crate::core::*; +pub use material::*; +pub use effect::*; +pub use light::*; +pub use geometry::*; +pub use object::*; +pub use control::*; \ No newline at end of file diff --git a/engine/text_editor/CodeEditorWindow.js b/engine/text_editor/CodeEditorWindow.js new file mode 100644 index 0000000..370d592 --- /dev/null +++ b/engine/text_editor/CodeEditorWindow.js @@ -0,0 +1,27 @@ +import React, { useState } from "react"; + +import Editor from "@monaco-editor/react"; + +const CodeEditorWindow = ({ onChange, language, code, theme }) => { + const [value, setValue] = useState(code || ""); + + const handleEditorChange = (value) => { + setValue(value); + onChange("code", value); + }; + + return ( +
+ +
+ ); +}; +export default CodeEditorWindow; \ No newline at end of file diff --git a/engine/text_editor/Landing.js b/engine/text_editor/Landing.js new file mode 100644 index 0000000..6a69985 --- /dev/null +++ b/engine/text_editor/Landing.js @@ -0,0 +1,152 @@ +import React, { useEffect, useState } from "react"; +import CodeEditorWindow from "./CodeEditorWindow"; +import axios from "axios"; +import { classnames } from "../utils/general"; +import { languageOptions } from "../constants/languageOptions"; + +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + +import { defineTheme } from "../lib/defineTheme"; +import useKeyPress from "../hooks/useKeyPress"; +import Footer from "./Footer"; +import OutputWindow from "./OutputWindow"; +import CustomInput from "./CustomInput"; +import OutputDetails from "./OutputDetails"; +import ThemeDropdown from "./ThemeDropdown"; +import LanguagesDropdown from "./LanguagesDropdown"; + +const javascriptDefault = `// some comment`; + +const Landing = () => { + const [code, setCode] = useState(javascriptDefault); + const [customInput, setCustomInput] = useState(""); + const [outputDetails, setOutputDetails] = useState(null); + const [processing, setProcessing] = useState(null); + const [theme, setTheme] = useState("cobalt"); + const [language, setLanguage] = useState(languageOptions[0]); + + const enterPress = useKeyPress("Enter"); + const ctrlPress = useKeyPress("Control"); + + const onSelectChange = (sl) => { + console.log("selected Option...", sl); + setLanguage(sl); + }; + + useEffect(() => { + if (enterPress && ctrlPress) { + console.log("enterPress", enterPress); + console.log("ctrlPress", ctrlPress); + handleCompile(); + } + }, [ctrlPress, enterPress]); + const onChange = (action, data) => { + switch (action) { + case "code": { + setCode(data); + break; + } + default: { + console.warn("case not handled!", action, data); + } + } + }; + const handleCompile = () => { + // We will come to the implementation later in the code + }; + + const checkStatus = async (token) => { + // We will come to the implementation later in the code + }; + + function handleThemeChange(th) { + // We will come to the implementation later in the code + } + useEffect(() => { + defineTheme("oceanic-next").then((_) => + setTheme({ value: "oceanic-next", label: "Oceanic Next" }) + ); + }, []); + + const showSuccessToast = (msg) => { + toast.success(msg || `Compiled Successfully!`, { + position: "top-right", + autoClose: 1000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + }; + const showErrorToast = (msg) => { + toast.error(msg || `Something went wrong! Please try again.`, { + position: "top-right", + autoClose: 1000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + }; + + return ( + <> + +
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+ + +
+ {outputDetails && } +
+
+