diff --git a/.forgejo/dependabot.yml b/.forgejo/dependabot.yml deleted file mode 100644 index 5251d0f..0000000 --- a/.forgejo/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# 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" diff --git a/.forgejo/workflows/rust.yml b/.forgejo/workflows/rust.yml index 7c4df6f..6adc72d 100644 --- a/.forgejo/workflows/rust.yml +++ b/.forgejo/workflows/rust.yml @@ -1,101 +1,101 @@ -name: Build Zenyx ⚡ - -on: - push: - pull_request: - -jobs: - build: - name: Build ${{ matrix.target }} - runs-on: ubuntu-latest - - services: - docker: - image: docker:dind - options: >- - --privileged - --env DOCKER_TLS_CERTDIR="" - ports: - - 2375:2375 - - strategy: - fail-fast: false - matrix: - target: - - x86_64-unknown-linux-gnu - - aarch64-unknown-linux-gnu - - powerpc64le-unknown-linux-gnu - - s390x-unknown-linux-gnu - - riscv64gc-unknown-linux-gnu - - x86_64-pc-windows-gnu - - x86_64-pc-windows-msvc - include: - - target: x86_64-unknown-linux-gnu - binary_name: zenyx-x86_64-linux - ext: "" - - target: aarch64-unknown-linux-gnu - binary_name: zenyx-aarch64-linux - ext: "" - - target: powerpc64le-unknown-linux-gnu - binary_name: zenyx-powerpc64le-linux - ext: "" - - target: s390x-unknown-linux-gnu - binary_name: zenyx-s390x-linux - ext: "" - - target: riscv64gc-unknown-linux-gnu - binary_name: zenyx-riscv64-linux - ext: "" - - target: x86_64-pc-windows-gnu - binary_name: zenyx-x86_64-windows-gnu.exe - ext: ".exe" - - target: x86_64-pc-windows-msvc - binary_name: zenyx-x86_64-windows-msvc.exe - ext: ".exe" - - steps: - - name: 📥 Checkout source - uses: https://github.com/actions/checkout@v4 - - - name: 🐳 Set up Docker CLI - uses: https://github.com/docker/setup-docker-action@v3 - with: - docker-binaries: true - - - name: 🛠️ Install Rust toolchain - uses: https://github.com/actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.target }} - override: true - components: rust-src - - - name: 🧩 Restore Cargo cache - uses: https://github.com/actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ matrix.target }}- - - - name: 🧰 Install Cross - run: cargo install cross --locked - - - name: 🚀 Build with cross - env: - DOCKER_HOST: tcp://localhost:2375 - CROSS_CONTAINER_IN_CONTAINER: true - run: cross build --target ${{ matrix.target }} --release - - - name: 📦 Package artifact - run: | - mkdir -p artifacts - cp target/${{ matrix.target }}/release/zenyx${{ matrix.ext }} artifacts/${{ matrix.binary_name }} - - - name: ⬆️ Upload artifact - uses: https://code.forgejo.org/forgejo/upload-artifact@v4 - with: - name: ${{ matrix.binary_name }} - path: artifacts/${{ matrix.binary_name }} +name: Build Zenyx ⚡ + +on: + push: + pull_request: + +jobs: + build: + name: Build ${{ matrix.target }} + runs-on: ubuntu-latest + + services: + docker: + image: docker:dind + options: >- + --privileged + --env DOCKER_TLS_CERTDIR="" + ports: + - 2375:2375 + + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + - powerpc64le-unknown-linux-gnu + - s390x-unknown-linux-gnu + - riscv64gc-unknown-linux-gnu + - x86_64-pc-windows-gnu + - x86_64-pc-windows-msvc + include: + - target: x86_64-unknown-linux-gnu + binary_name: zenyx-x86_64-linux + ext: "" + - target: aarch64-unknown-linux-gnu + binary_name: zenyx-aarch64-linux + ext: "" + - target: powerpc64le-unknown-linux-gnu + binary_name: zenyx-powerpc64le-linux + ext: "" + - target: s390x-unknown-linux-gnu + binary_name: zenyx-s390x-linux + ext: "" + - target: riscv64gc-unknown-linux-gnu + binary_name: zenyx-riscv64-linux + ext: "" + - target: x86_64-pc-windows-gnu + binary_name: zenyx-x86_64-windows-gnu.exe + ext: ".exe" + - target: x86_64-pc-windows-msvc + binary_name: zenyx-x86_64-windows-msvc.exe + ext: ".exe" + + steps: + - name: 📥 Checkout source + uses: https://github.com/actions/checkout@v4 + + - name: 🐳 Set up Docker CLI + uses: https://github.com/docker/setup-docker-action@v3 + with: + docker-binaries: true + + - name: 🛠️ Install Rust toolchain + uses: https://github.com/actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.target }} + override: true + components: rust-src + + - name: 🧩 Restore Cargo cache + uses: https://github.com/actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ matrix.target }}- + + - name: 🧰 Install Cross + run: cargo install cross --locked + + - name: 🚀 Build with cross + env: + DOCKER_HOST: tcp://localhost:2375 + CROSS_CONTAINER_IN_CONTAINER: true + run: cross build --target ${{ matrix.target }} --release + + - name: 📦 Package artifact + run: | + mkdir -p artifacts + cp target/${{ matrix.target }}/release/zenyx${{ matrix.ext }} artifacts/${{ matrix.binary_name }} + + - name: ⬆️ Upload artifact + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: ${{ matrix.binary_name }} + path: artifacts/${{ matrix.binary_name }} diff --git a/.gitignore b/.gitignore index 6219808..5938ea6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -/target -.idea -# Cargo.lock -*.log -.direnv +/target +.idea +# Cargo.lock +*.log +.direnv **/result \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0f90262..4904d2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,47 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -438,6 +479,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + [[package]] name = "indexmap" version = "2.9.0" @@ -2155,6 +2202,8 @@ checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" name = "zenyx" version = "0.1.0" dependencies = [ + "crossbeam", + "humantime", "terminator", "thiserror 2.0.12", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 2859fc4..df13c8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,19 @@ split-debuginfo = "off" [profile.dev] debug = 0 +[profile.mini] +inherits = "release" +opt-level = "z" +debug = false +strip = true +lto = true +codegen-units = 1 +incremental = false +panic = "abort" + [dependencies] +crossbeam = "0.8.4" +humantime = "2.2.0" terminator = "0.3.2" thiserror = "2.0.12" tracing = "0.1.41" diff --git a/LICENSE.md b/LICENSE.md index c918216..001fd3d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +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.** +# 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 index ce50650..8a4ed2f 100644 --- a/README.md +++ b/README.md @@ -1,175 +1,175 @@ -
- -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 - -## Star history ⭐ - - - - - - Star History Chart - - - -## 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 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 | -| 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 - -## 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: - - git clone https://github.com/Caznix/Zenyx.git - -**2**. cd into the project directory - - cd ./Zenyx - -**3**. Build the project - - cargo run - -## 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]() - -## 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 - -Zenyx Typeface - -## Colored badges - -Zenyx Logo - - +
+ +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 + +## Star history ⭐ + + + + + + Star History Chart + + + +## 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 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 | +| 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 + +## 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: + + git clone https://github.com/Caznix/Zenyx.git + +**2**. cd into the project directory + + cd ./Zenyx + +**3**. Build the project + + cargo run + +## 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]() + +## 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 + +Zenyx Typeface + +## Colored badges + +Zenyx Logo + + diff --git a/assets/Badge.svg b/assets/Badge.svg index babda1a..714305d 100644 --- a/assets/Badge.svg +++ b/assets/Badge.svg @@ -1,9 +1,9 @@ - - - - - - - - - + + + + + + + + + diff --git a/assets/Logo.svg b/assets/Logo.svg index ef382b7..7730d19 100644 --- a/assets/Logo.svg +++ b/assets/Logo.svg @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/default.nix b/default.nix index 1ad47e5..1aa9adf 100644 --- a/default.nix +++ b/default.nix @@ -1,75 +1,75 @@ -{ lib, - rustPlatform, - nix-gitignore, - bash, - makeWrapper, - dav1d, - vulkan-loader, - libGL, - wayland, - pkg-config, - libxkbcommon, - pkgs, - stdenv, - targetPackages ? pkgs, -}: let - version = (builtins.fromTOML (builtins.readFile ./engine/Cargo.toml)).package.version; - src = nix-gitignore.gitignoreSource [] ./.; -in -rustPlatform.buildRustPackage rec { - pname = "zenyx"; - inherit src version; - cargoLock.lockFile = ./Cargo.lock; - - nativeBuildInputs = [ - pkg-config - ] ++ lib.optionals stdenv.targetPlatform.isDarwin [ - targetPackages.darwin.apple_sdk.frameworks.CoreServices - ]; - - buildInputs = with targetPackages; [ - dav1d - ] ++ lib.optionals (stdenv.targetPlatform.isLinux || stdenv.targetPlatform.isWindows) [ - vulkan-loader - ] ++ lib.optionals stdenv.targetPlatform.isLinux [ - makeWrapper - wayland - libxkbcommon - libGL - xorg.libXcursor - xorg.libXrandr - xorg.libXi - xorg.libX11 - xorg.libxcb - bash - ] ++ lib.optionals stdenv.targetPlatform.isDarwin [ - makeWrapper - darwin.apple_sdk.frameworks.Cocoa - darwin.apple_sdk.frameworks.Metal - darwin.apple_sdk.frameworks.CoreVideo - darwin.apple_sdk.frameworks.QuartzCore - ]; - - CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = - lib.optionalString stdenv.targetPlatform.isWindows "${stdenv.cc.targetPrefix}gcc"; - - NIX_LDFLAGS = lib.optionalString stdenv.targetPlatform.isDarwin "-framework CoreFoundation"; - - postInstall = lib.optionalString stdenv.targetPlatform.isLinux '' - wrapProgram $out/bin/${pname} \ - --prefix PATH : ${lib.makeBinPath [ bash ]} \ - --set LD_LIBRARY_PATH ${lib.makeLibraryPath buildInputs} - '' + lib.optionalString stdenv.targetPlatform.isWindows '' - mkdir -p $out/bin - cp ${targetPackages.vulkan-loader}/bin/vulkan-1.dll $out/bin/ - ''; - - doCheck = false; - - meta = { - description = "Cross-platform WSYWIG Game Engine"; - license = lib.licenses.mit; - platforms = lib.platforms.all; - mainProgram = "zenyx"; - }; +{ lib, + rustPlatform, + nix-gitignore, + bash, + makeWrapper, + dav1d, + vulkan-loader, + libGL, + wayland, + pkg-config, + libxkbcommon, + pkgs, + stdenv, + targetPackages ? pkgs, +}: let + version = (builtins.fromTOML (builtins.readFile ./engine/Cargo.toml)).package.version; + src = nix-gitignore.gitignoreSource [] ./.; +in +rustPlatform.buildRustPackage rec { + pname = "zenyx"; + inherit src version; + cargoLock.lockFile = ./Cargo.lock; + + nativeBuildInputs = [ + pkg-config + ] ++ lib.optionals stdenv.targetPlatform.isDarwin [ + targetPackages.darwin.apple_sdk.frameworks.CoreServices + ]; + + buildInputs = with targetPackages; [ + dav1d + ] ++ lib.optionals (stdenv.targetPlatform.isLinux || stdenv.targetPlatform.isWindows) [ + vulkan-loader + ] ++ lib.optionals stdenv.targetPlatform.isLinux [ + makeWrapper + wayland + libxkbcommon + libGL + xorg.libXcursor + xorg.libXrandr + xorg.libXi + xorg.libX11 + xorg.libxcb + bash + ] ++ lib.optionals stdenv.targetPlatform.isDarwin [ + makeWrapper + darwin.apple_sdk.frameworks.Cocoa + darwin.apple_sdk.frameworks.Metal + darwin.apple_sdk.frameworks.CoreVideo + darwin.apple_sdk.frameworks.QuartzCore + ]; + + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER = + lib.optionalString stdenv.targetPlatform.isWindows "${stdenv.cc.targetPrefix}gcc"; + + NIX_LDFLAGS = lib.optionalString stdenv.targetPlatform.isDarwin "-framework CoreFoundation"; + + postInstall = lib.optionalString stdenv.targetPlatform.isLinux '' + wrapProgram $out/bin/${pname} \ + --prefix PATH : ${lib.makeBinPath [ bash ]} \ + --set LD_LIBRARY_PATH ${lib.makeLibraryPath buildInputs} + '' + lib.optionalString stdenv.targetPlatform.isWindows '' + mkdir -p $out/bin + cp ${targetPackages.vulkan-loader}/bin/vulkan-1.dll $out/bin/ + ''; + + doCheck = false; + + meta = { + description = "Cross-platform WSYWIG Game Engine"; + license = lib.licenses.mit; + platforms = lib.platforms.all; + mainProgram = "zenyx"; + }; } \ No newline at end of file diff --git a/flake.lock b/flake.lock index 1c67c8f..61da03c 100644 --- a/flake.lock +++ b/flake.lock @@ -1,190 +1,190 @@ -{ - "nodes": { - "alejandra": { - "inputs": { - "fenix": "fenix", - "flakeCompat": "flakeCompat", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1730688725, - "narHash": "sha256-g0SSfTWZ5mtMOpQic+eqq9sXMy1E/7yKxxfupZd9V4A=", - "owner": "kamadorueda", - "repo": "alejandra", - "rev": "2bb91e309ca99656addff5c74545acbf5813636d", - "type": "github" - }, - "original": { - "owner": "kamadorueda", - "ref": "3.1.0", - "repo": "alejandra", - "type": "github" - } - }, - "fenix": { - "inputs": { - "nixpkgs": [ - "alejandra", - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1730615655, - "narHash": "sha256-2HBR3zLn57LXKNRtxBb+O+uDqHM4n0pz51rPayMl4cg=", - "owner": "nix-community", - "repo": "fenix", - "rev": "efeb50e2535b17ffd4a135e6e3e5fd60a525180c", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, - "flake-compat": { - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "revCount": 69, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flakeCompat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1743095683, - "narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1736320768, - "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "alejandra": "alejandra", - "flake-compat": "flake-compat", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay", - "utils": "utils" - } - }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1730555913, - "narHash": "sha256-KNHZUlqsEibg3YtfUyOFQSofP8hp1HKoY+laoesBxRM=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "f17a5bbfd0969ba2e63a74505a80e55ecb174ed9", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1743215516, - "narHash": "sha256-52qbrkG65U1hyrQWltgHTgH4nm0SJL+9TWv2UDCEPNI=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "524463199fdee49338006b049bc376b965a2cfed", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} +{ + "nodes": { + "alejandra": { + "inputs": { + "fenix": "fenix", + "flakeCompat": "flakeCompat", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730688725, + "narHash": "sha256-g0SSfTWZ5mtMOpQic+eqq9sXMy1E/7yKxxfupZd9V4A=", + "owner": "kamadorueda", + "repo": "alejandra", + "rev": "2bb91e309ca99656addff5c74545acbf5813636d", + "type": "github" + }, + "original": { + "owner": "kamadorueda", + "ref": "3.1.0", + "repo": "alejandra", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "alejandra", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1730615655, + "narHash": "sha256-2HBR3zLn57LXKNRtxBb+O+uDqHM4n0pz51rPayMl4cg=", + "owner": "nix-community", + "repo": "fenix", + "rev": "efeb50e2535b17ffd4a135e6e3e5fd60a525180c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "revCount": 69, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.1.0/01948eb7-9cba-704f-bbf3-3fa956735b52/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" + } + }, + "flakeCompat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1743095683, + "narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1736320768, + "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "alejandra": "alejandra", + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "utils": "utils" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1730555913, + "narHash": "sha256-KNHZUlqsEibg3YtfUyOFQSofP8hp1HKoY+laoesBxRM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "f17a5bbfd0969ba2e63a74505a80e55ecb174ed9", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1743215516, + "narHash": "sha256-52qbrkG65U1hyrQWltgHTgH4nm0SJL+9TWv2UDCEPNI=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "524463199fdee49338006b049bc376b965a2cfed", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix index 2b71e5f..65256e3 100644 --- a/flake.nix +++ b/flake.nix @@ -1,78 +1,84 @@ -{ - description = "Zenyx - A WSYWIG game engine written in rust "; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - utils.url = "github:numtide/flake-utils"; - rust-overlay.url = "github:oxalica/rust-overlay"; - flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"; - alejandra.url = "github:kamadorueda/alejandra/3.1.0"; - alejandra.inputs.nixpkgs.follows = "nixpkgs"; - }; - - outputs = { - self, - nixpkgs, - utils, - rust-overlay, - alejandra, - ... - }: - { - overlays.default = final: prev: { - zenyx = final.callPackage ./default.nix {}; - }; - } - // utils.lib.eachDefaultSystem ( - system: let - pkgs = import nixpkgs { - inherit system; - overlays = [ - self.overlays.default - (import rust-overlay) - ]; - }; - buildInputs = with pkgs; [ - vulkan-loader - libGL - wayland - libxkbcommon - xorg.libXcursor - xorg.libXrandr - xorg.libXi - xorg.libX11 - xorg.libxcb - pkg-config - kdePackages.kdialog - ]; - in { - packages = { - inherit (pkgs) zenyx; - default = pkgs.zenyx; - windows = let - pkgsCross = import nixpkgs { - system = "x86_64-linux"; - crossSystem = nixpkgs.lib.systems.examples.mingwW64; - }; - in pkgsCross.callPackage ./default.nix {}; - }; - - devShells.default = pkgs.mkShell { - name = "zenyx"; - nativeBuildInputs = with pkgs; [ - (rust-bin.stable.latest.default.override { - extensions = ["rust-src" "cargo" "rustfmt" "clippy"]; - }) - pkg-config - ]; - buildInputs = buildInputs; - shellHook = '' - export PATH="$HOME/.cargo/bin:$PATH" - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${builtins.toString (pkgs.lib.makeLibraryPath buildInputs)}" - echo "Rust version: $(rustc --version)" - ''; - }; - - formatter = alejandra.packages.${system}.default; - } - ); -} +{ + description = "Zenyx - A WSYWIG game engine written in rust "; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + utils.url = "github:numtide/flake-utils"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"; + alejandra.url = "github:kamadorueda/alejandra/3.1.0"; + alejandra.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { + self, + nixpkgs, + utils, + rust-overlay, + alejandra, + ... + }: + { + overlays.default = final: prev: { + zenyx = final.callPackage ./default.nix {}; + }; + } + // utils.lib.eachDefaultSystem ( + system: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + self.overlays.default + (import rust-overlay) + ]; + }; + buildInputs = with pkgs; [ + vulkan-loader + libGL + wayland + libxkbcommon + xorg.libXcursor + xorg.libXrandr + xorg.libXi + xorg.libX11 + xorg.libxcb + pkg-config + kdePackages.kdialog + cargo-xbuild + cargo-pgo + cargo-cross + cargo-xwin + cargo-wizard + wine64 + ]; + in { + packages = { + inherit (pkgs) zenyx; + default = pkgs.zenyx; + windows = let + pkgsCross = import nixpkgs { + system = "x86_64-linux"; + crossSystem = nixpkgs.lib.systems.examples.mingwW64; + }; + in pkgsCross.callPackage ./default.nix {}; + }; + + devShells.default = pkgs.mkShell { + name = "zenyx"; + nativeBuildInputs = with pkgs; [ + (rust-bin.stable.latest.default.override { + extensions = ["rust-src" "cargo" "rustfmt" "clippy"]; + targets = [ "x86_64-pc-windows-msvc" "x86_64-unknown-linux-gnu" ]; + + }) + pkg-config + ]; + buildInputs = buildInputs; + shellHook = '' + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${builtins.toString (pkgs.lib.makeLibraryPath buildInputs)}" + echo "Rust version: $(rustc --version)" + ''; + }; + formatter = alejandra.packages.${system}.default; + } + ); +} diff --git a/rustfmt.toml b/rustfmt.toml index c7302df..a99bc20 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,7 +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 +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 diff --git a/shell.nix b/shell.nix index 3899c24..64217fe 100644 --- a/shell.nix +++ b/shell.nix @@ -1,15 +1,15 @@ -( - import - ( - let - lock = builtins.fromJSON (builtins.readFile ./flake.lock); - nodeName = lock.nodes.root.inputs.flake-compat; - in - fetchTarball { - url = lock.nodes.${nodeName}.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz"; - sha256 = lock.nodes.${nodeName}.locked.narHash; - } - ) - {src = ./.;} -) -.shellNix +( + import + ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + nodeName = lock.nodes.root.inputs.flake-compat; + in + fetchTarball { + url = lock.nodes.${nodeName}.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz"; + sha256 = lock.nodes.${nodeName}.locked.narHash; + } + ) + {src = ./.;} +) +.shellNix diff --git a/src/main.rs b/src/main.rs index ec82a19..bc30864 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,85 @@ +use crossbeam::channel::{Sender, unbounded}; use std::{ - fs::OpenOptions, io::{self, BufWriter, Stdout, Write}, path::{Path, PathBuf}, str::FromStr, sync::{mpsc, Arc, RwLock} + fmt, + fs::OpenOptions, + io::{BufWriter, Write}, + path::{Path, PathBuf}, + str::FromStr, + sync::{Arc, RwLock}, + time::SystemTime, +}; +use tracing::{Event, Level, Subscriber, info}; +use tracing_subscriber::{ + EnvFilter, + layer::{Context, Layer, SubscriberExt}, + registry::LookupSpan, + util::SubscriberInitExt, }; - -use tracing::{error, info}; -use tracing_subscriber::fmt::format::FmtSpan; -use tracing_subscriber::{Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt}; -use winit::event_loop::EventLoop; #[derive(Debug, Default)] struct App; -struct BufferWriter { - buffer: Arc>>, +#[derive(Debug, Clone)] +pub struct LogEntry { + timestamp: SystemTime, + level: Level, + message: String, } -impl Write for BufferWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - if let Ok(mut guard) = self.buffer.write() { - guard.extend_from_slice(buf); - Ok(buf.len()) +struct BufferLayer { + log_entries: Arc>>, + sender: Sender, +} + +impl Layer for BufferLayer +where + S: Subscriber + for<'a> LookupSpan<'a>, +{ + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let metadata = event.metadata(); + let level = *metadata.level(); + let timestamp = SystemTime::now(); + let mut message = String::new(); + let mut visitor = LogVisitor::new(&mut message); + event.record(&mut visitor); + + let log_entry = LogEntry { + timestamp, + level, + message, + }; + + if let Ok(mut guard) = self.log_entries.write() { + guard.push(log_entry.clone()); + } + + let _ = self.sender.send(log_entry); + } +} + +struct LogVisitor<'msg> { + message: &'msg mut String, +} + +impl<'msg> LogVisitor<'msg> { + fn new(message: &'msg mut String) -> Self { + Self { message } + } +} + +impl<'msg> tracing::field::Visit for LogVisitor<'msg> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) { + use std::fmt::Write as _; + if field.name() == "message" { + write!(self.message, "{:?}", value).unwrap(); } else { - Err(io::Error::new( - io::ErrorKind::Other, - "Failed to acquire write lock on log buffer", - )) + write!(self.message, "{}={:?} ", field.name(), value).unwrap(); } } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] -enum LogLevel { +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] +pub enum LogLevel { Error, #[default] Info, @@ -41,9 +87,9 @@ enum LogLevel { Trace, } -impl std::fmt::Display for LogLevel { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f,"{self:?}") +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) } } @@ -54,44 +100,53 @@ impl FromStr for LogLevel { match s { "trace" => Ok(Self::Trace), "debug" => Ok(Self::Debug), - "info" => Ok(Self::Info), + "info" => Ok(Self::Info), "error" => Ok(Self::Error), _ => Err(()), } } } -#[derive(Debug)] +#[derive(Debug, Clone,PartialEq,Eq,PartialOrd)] pub struct LoggerConfig { pub log_level: LogLevel, pub log_to_file: bool, pub log_file_path: PathBuf, pub log_to_stdout: bool, pub stdout_color: bool, - pub log_to_buffer: bool, + pub stdout_include_time: bool, + pub file_include_time: bool, } impl LoggerConfig { - pub fn level(mut self,level: LogLevel) -> Self { + pub fn level(mut self, level: LogLevel) -> Self { self.log_level = level; self } - pub fn log_to_file(mut self,log_to_file: bool ) -> Self { + pub fn log_to_file(mut self, log_to_file: bool) -> Self { self.log_to_file = log_to_file; self } - pub fn colored_stdout(mut self,show_color: bool ) -> Self { + pub fn colored_stdout(mut self, show_color: bool) -> Self { self.stdout_color = show_color; self } - pub fn log_to_stdout(mut self,stdout: bool ) -> Self { + pub fn log_to_stdout(mut self, stdout: bool) -> Self { self.log_to_stdout = stdout; self } - pub fn log_path>(mut self,path: P ) -> Self { + pub fn log_path>(mut self, path: P) -> Self { self.log_file_path = path.as_ref().to_path_buf(); self } + pub fn stdout_include_time(mut self, include: bool) -> Self { + self.stdout_include_time = include; + self + } + pub fn file_include_time(mut self, include: bool) -> Self { + self.file_include_time = include; + self + } } impl Default for LoggerConfig { @@ -102,118 +157,128 @@ impl Default for LoggerConfig { log_file_path: "zenyx.log".into(), log_to_stdout: true, stdout_color: true, - log_to_buffer: true, + stdout_include_time: false, + file_include_time: false, } } } - +#[derive(Debug, Clone,Copy, PartialEq,Eq,PartialOrd)] +pub enum LogQuery { + All, + From(SystemTime), +} +#[derive(Debug, Clone)] pub struct Logger { config: LoggerConfig, - log_buffer: Arc>>, -} - -enum Log { - All, - From(std::time::SystemTime), + log_entries: Arc>>, + _sender: Sender, } impl Logger { pub fn new(config: LoggerConfig) -> Self { + let (sender, receiver) = unbounded(); + let log_entries = Arc::new(RwLock::new(Vec::new())); + + if config.log_to_stdout { + let stdout_receiver = receiver.clone(); + let stdout_color = config.stdout_color; + let stdout_include_time = config.stdout_include_time; + std::thread::spawn(move || { + for entry in stdout_receiver { + let line = format_entry(&entry, stdout_color, stdout_include_time); + println!("{}", line); + } + }); + } + + if config.log_to_file { + let file_receiver = receiver.clone(); + let log_file_path = config.log_file_path.clone(); + let file_include_time = config.file_include_time; + std::thread::spawn(move || { + let file = OpenOptions::new() + .append(true) + .create(true) + .open(&log_file_path) + .unwrap_or_else(|_| { + OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&log_file_path) + .expect("Failed to create log file") + }); + let mut writer = BufWriter::new(file); + for entry in file_receiver { + let line = format_entry(&entry, false, file_include_time); + writer + .write_all(format!("{}\n", line).as_bytes()) + .expect("Failed to write to log file"); + writer.flush().expect("Failed to flush log file"); + } + }); + } + + let buffer_layer = BufferLayer { + log_entries: log_entries.clone(), + sender: sender.clone(), + }; + + let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| { + format!("{}={}", env!("CARGO_CRATE_NAME"), config.log_level).into() + }); + + let subscriber = tracing_subscriber::registry() + .with(env_filter) + .with(buffer_layer); + subscriber.init(); + Self { config, - log_buffer: Arc::new(RwLock::new(Vec::new())), + log_entries, + _sender: sender, } } - pub fn init(&self) { - let env_filter = - tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { - format!("{}={}", env!("CARGO_CRATE_NAME"), self.config.log_level).into() - }); - - let mut layers = Vec::new(); - - if self.config.log_to_stdout { - let fmt_layer_stdout = tracing_subscriber::fmt::layer() - .with_level(true) - .compact() - .with_ansi(self.config.stdout_color) - .log_internal_errors(false) - .without_time() - .with_thread_names(true) - .with_span_events(FmtSpan::CLOSE) - .with_writer(std::io::stdout) - .boxed(); - layers.push(fmt_layer_stdout); - } - - if self.config.log_to_file { - let log_file_path = self.config.log_file_path.clone(); - let fmt_layer_file = tracing_subscriber::fmt::layer() - .with_level(true) - .compact() - .with_ansi(false) - .log_internal_errors(false) - .without_time() - .with_thread_names(true) - .with_span_events(FmtSpan::CLOSE) - .with_writer(move || { - let file = OpenOptions::new() - .append(true) - .open(&log_file_path) - .unwrap_or_else(|_| { - OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&log_file_path) - .expect("Failed to create log file") - }); - BufWriter::new(file) - }) - .boxed(); - layers.push(fmt_layer_file); - } - // let (tx,rx) = mpsc::channel(); - // std::thread::spawn(move || { - // for log in rx.iter() { - // println!("{log:#?}") - // } - // }); - - - if self.config.log_to_buffer { - let log_buffer = Arc::clone(&self.log_buffer); - let fmt_layer_buffer = tracing_subscriber::fmt::layer() - .with_level(true) - .compact() - .with_ansi(false) - .log_internal_errors(false) - .without_time() - .with_thread_names(true) - .with_span_events(FmtSpan::CLOSE) - .with_writer(move || BufferWriter { - buffer: log_buffer.clone(), - }) - .boxed(); - layers.push(fmt_layer_buffer); - } - - if !layers.is_empty() { - let subscriber = Registry::default().with(env_filter).with(layers); - subscriber.init(); + pub fn get_logs(&self, log_query: LogQuery) -> Vec { + let guard = self.log_entries.read().unwrap(); + match log_query { + LogQuery::All => guard.clone(), + LogQuery::From(time) => guard + .iter() + .filter(|e| e.timestamp >= time) + .cloned() + .collect(), } } +} - pub fn get_logs(&self) -> Option { - if !self.config.log_to_buffer { - return None; +fn format_entry(entry: &LogEntry, use_color: bool, include_time: bool) -> String { + let timestamp = if include_time { + format!( + "[{}] ", + humantime::format_rfc3339_seconds(entry.timestamp) + .to_string() + .trim_end_matches('Z') + .replace('T', " ") + ) + } else { + String::new() + }; + + let level = if use_color { + match entry.level { + Level::ERROR => format!("\x1b[31m{}\x1b[0m", entry.level), + Level::WARN => format!("\x1b[33m{}\x1b[0m", entry.level), + Level::INFO => format!("\x1b[32m{}\x1b[0m", entry.level), + Level::DEBUG => format!("\x1b[36m{}\x1b[0m", entry.level), + Level::TRACE => format!("\x1b[34m{}\x1b[0m", entry.level), } - self.log_buffer - .read() - .ok() - .map(|guard| String::from_utf8_lossy(&guard).into_owned()) - } + } else { + entry.level.to_string() + }; + + format!("{}{} {}", timestamp, level, entry.message) } fn main() -> Result<(), Box> { @@ -221,25 +286,32 @@ fn main() -> Result<(), Box> { .colored_stdout(true) .level(LogLevel::Debug) .log_to_file(true) - .log_path(format!("{}.log",env!("CARGO_PKG_NAME"))); - let logger = Logger::new(logger_config); - logger.init(); + .stdout_include_time(false) + .file_include_time(true) + .log_path(format!("{}.log", env!("CARGO_PKG_NAME"))); - let event_loop = EventLoop::new()?; - info!("Application initialized"); + let logger = Logger::new(logger_config); let app = App::default(); + tracing::info!("Application initialized"); + tracing::error!("Another log message with value: {}", 42); - info!("This is a test log message"); - error!("Another log message with value: {}", 42); - - if let Some(logs) = logger.get_logs() { - println!("\n--- Logs from Buffer ---"); - println!("{}", logs); - println!("------------------------"); - } else { - eprintln!("Failed to read logs from buffer or buffer logging is disabled"); + let logs = logger.get_logs(LogQuery::All); + println!("\n--- All Logs ---"); + for entry in logs { + println!("{}", format_entry(&entry, false, true)); } + let now = SystemTime::now(); + tracing::warn!("This is a warning after the initial logs"); + let new_logs = logger.get_logs(LogQuery::From(now)); + println!("\n--- Logs Since Now ---"); + for entry in new_logs { + println!("{}", format_entry(&entry, false, true)); + } + info!("Application finished"); + + // logger.get_logs(); + Ok(()) }