diff --git a/Cargo.lock b/Cargo.lock index 317748f57e..081adc58da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3428,9 +3428,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.3" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" dependencies = [ "ring", "untrusted", @@ -3598,9 +3598,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] diff --git a/README.md b/README.md index 0f0c6f9ca2..249eb2f953 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,22 @@ [![Documentation](https://img.shields.io/badge/documentation-fishfolk.org-green.svg?labelColor=1e1c24&color=f3ee7a)](https://fishfolk.org/bones/overview/introduction/) [![Crates.io](https://img.shields.io/crates/v/bones_lib?labelColor=1e1c24)](https://crates.io/crates/bones_lib) -[![docs.rs](https://img.shields.io/docsrs/bones_lib?label=API%20Docs&labelColor=1e1c24)](https://docs.rs/bones_lib) +[![docs.rs](https://img.shields.io/docsrs/bones_framework?label=API%20Docs&labelColor=1e1c24)](https://docs.rs/bones_framework) +[![Main Branch Docs](https://img.shields.io/badge/API_Docs-Main_Branch-blue?labelColor=1e1c24&color=red)](https://fishfolk.github.io/bones/rustdoc/bones_framework/index.html) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg?label=license&labelColor=1e1c24&color=34925e)](./LICENSE) -[![Discord](https://img.shields.io/badge/chat-on%20discord-green.svg?logo=discord&logoColor=fff&labelColor=1e1c24&color=8d5b3f)](https://discord.gg/4smxjcheE5) +[![Discord](https://img.shields.io/badge/chat-on%20discord-green.svg?logo=discord&logoColor=fff&labelColor=1e1c24&color=8d5b3f)][Discord] -A work-in-progress, opinionated game meta-engine built on [Bevy]. +A work-in-progress, simple, and easy-to-use game engine that can be rendered with [Bevy]. -Under development for future use in the [Jumpy] game, and possibly other FishFolk games. +Used in the [Jumpy] game, and will possibly be used in other FishFolk games in the future. Check out [Fishfolk.org] for more documentation and tutorials. [fishfolk.org]: https://fishfolk.org [bevy]: https://bevyengine.org [jumpy]: https://github.com/fishfolk/jumpy +[discord]: https://discord.gg/4smxjcheE5 +[revolt]: https://weird.dev/invite/ZagXxrS4 ## Overview @@ -24,31 +27,47 @@ Bones is designed around a simple, custom Entity Component System ( ECS ), desig - **Determinism:** Bones ECS is deterministic by default, making it easier to get a re-producible and predictable gameplay. - **Snapshot/Restore:** The Bones ECS world can be trivially snapshot and restored. -- **Modding/Scripting ( future ):** Bones ECS is simple enough that we can feasibly provide a C API for integration with other languages and scripting. +- **Modding/Scripting ( work-in-progress ):** Bones ECS is built on our [`bones_schema`] system, which allows for runtime reflection and the ability to interact with data types defined outside of Rust. + +[`bones_schema`]: https://fishfolk.github.io/bones/rustdoc/bones_schema/index.html Determinism and Snapshot/Restore are also key features for getting excellent **network play** with the rollback networking model, while requiring no changes to the core game loop implementation. -### Game Core +### Bones Lib + +The [`bones_lib`] contains the [`bones_ecs`] and the [`bones_asset`] system. It defines the concept of a [`Game`] which contains all of your game logic and data in a collection of [`Session`]s that each have their own ECS [`World`]. + +Bones lib has no rendering components or even math types, it is only concerned with organizing your game logic and assets. + +[`bones_lib`]: https://fishfolk.github.io/bones/rustdoc/bones_lib/index.html +[`bones_ecs`]: https://fishfolk.github.io/bones/rustdoc/bones_ecs/index.html +[`bones_asset`]: https://fishfolk.github.io/bones/rustdoc/bones_asset/index.html +[`Game`]: https://fishfolk.github.io/bones/rustdoc/bones_lib/struct.Game.html +[`Session`]: https://fishfolk.github.io/bones/rustdoc/bones_lib/struct.Session.html +[`World`]: https://fishfolk.github.io/bones/rustdoc/bones_lib/ecs/struct.World.html + +### Bones Framework -Using `bones_lib`, which includes `bones_ecs` and other useful utilities, you write your "game core". +On top of [`bones_lib`] there is the [`bones_framework`], which defines the rendering components and math types. Right now [`bones_framework`] is focused only on 2D rendering. 3D is not a priority for us now, but there is no technical limitation preventing community developed 3D rendering components either on top of [`bones_lib`] directly or as an extension to the [`bones_framework`]. -This game core is mostly isolated from anything outside of the ECS world. This is important so that the entire game core can be snapshot/restored. +[`bones_framework`]: https://fishfolk.github.io/bones/rustdoc/bones_framework/index.html -This means that to collect input, you must read those inputs from an ECS resource, and to render sprites, map tiles, etc., you must create entities with specific components that tell Bones how to render them. +### Bones Bevy Renderer -### Bevy Renderer +A game created with the [`bones_framework`] is renderer agnostic. This allows us to create rendering integrations with other engines. Our official integration is with the [Bevy] engine. The [`bones_bevy_renderer`] allows you to create a Bevy `App` for rendering a [`bones_framework`] game. -Once you have your game core, you can render the core in a Bevy game using the `bones_bevy_renderer` crate. +This also allows you to create custom extensions to the Bevy renderer, if you need a bones integration with a feature not supported out-of-the-box in the [`bones_framework`]. -This lets you utilize the power and plugin ecosystem of Bevy to interact with rendering, input, etc. while keeping your core game deterministic, snapshot-able, and moddable. +[`bones_bevy_renderer`]: https://fishfolk.github.io/bones/rustdoc/bones_bevy_renderer/index.html -You are also free to create your own rendering components that you synchronize with Bevy to support any custom rendering/audio use-cases, etc. +## Contributing -> **Note:** Bones ECS as well as `bones_lib` can technically be used without Bevy. Right now we are focusing on Bevy, and support for alternative uses may not be well polished/complete yet, but it's still within the realm of possibility to render Bones cores with any framework you want. +If you would like to contribute, feel free to reach out on our [Discord] or [Revolt] server to ask questions! -### Bones App ( Future ) +We also use [TODO Issue][tdi] to automatically create issues from all of our `TODO` comments in code. You can check out the [todo issue list][tdil] to see if there's any thing you'd like to take a hack at. -As we get a feel for how things fit together with the use of Bones in [Jumpy], we hope to be able to create a standardized game runner around Bones game cores. This would allow the Bones app to handle common things like localization, asset loading, input mapping, settings menu, networking, etc., allowing you to focus on writing what makes your game unique. +[tdi]: https://github.com/DerJuulsn/todo-issue +[tdil]: https://github.com/fishfolk/bones/issues?q=is%3Aissue+is%3Aopen+label%3Acode%3Atodo ## Similar Projects diff --git a/framework_crates/bones_asset/src/io.rs b/framework_crates/bones_asset/src/io.rs index b9063b2357..ace5433c49 100644 --- a/framework_crates/bones_asset/src/io.rs +++ b/framework_crates/bones_asset/src/io.rs @@ -76,9 +76,9 @@ impl FileAssetIo { } _ => (), }, - // TODO: Log `bones_asset` errors with tracing. - // - // Also see the unwrap_or_else line below, which needs an error log. + // TODO: Log asset errors with tracing. + // We should use the [`tracing`](https://docs.rs/tracing/latest/tracing/) crate + // to log an error message here instead of using `eprintln!()`. Err(e) => eprintln!("watch error: {e:?}"), } }) @@ -96,7 +96,7 @@ impl FileAssetIo { }) .map_err(|e| { eprintln!("watch error: {e:?}"); - // See todo above: log error message. + // TODO: Log asset errors with tracing. }) .ok(); } diff --git a/framework_crates/bones_asset/src/server.rs b/framework_crates/bones_asset/src/server.rs index 26e8bdf9fe..1cbc3c9837 100644 --- a/framework_crates/bones_asset/src/server.rs +++ b/framework_crates/bones_asset/src/server.rs @@ -210,7 +210,11 @@ impl AssetServer { id: meta.id, version: meta.version, game_version: meta.game_version, - // TODO: load & import schemas that are defined in asset packs. + // TODO: Load & import schemas that are defined in asset packs. + // This is medium-sized effort. The idea is that asset packs shoudl be able to + // create YAML files that describe a `SchemaData`. This schema data should get + // registered with a file extension name, just like a Rust metadata asset does + // so that that schema can be used to load assets. schemas: default(), import_schemas: default(), root: root_handle, @@ -230,7 +234,7 @@ impl AssetServer { match self.load_asset_forced(loc.as_ref()) { Ok(handle) => handle_change(self, handle), Err(_) => { - // TODO: Handle/log asset error. + // TODO: Log asset errors with tracing. continue; } }; @@ -742,7 +746,10 @@ mod metadata { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - // TODO: write a really nice error message for this. + // TODO: Write very verbose error messages for metadata asset deserializers. + // The schema describes the type that we are trying to deserialize, so we should + // use that information to format a nice-to-read error message that documents + // what data it's expecting. write!( formatter, "asset metadata matching the schema: {:#?}", @@ -820,7 +827,7 @@ mod metadata { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - // TODO: write a really nice error message for this. + // TODO: Write very verbose error messages for metadata asset deserializers. write!( formatter, "asset metadata matching the schema: {:#?}", @@ -863,7 +870,7 @@ mod metadata { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - // TODO: write a really nice error message for this. + // TODO: Write very verbose error messages for metadata asset deserializers. write!( formatter, "asset metadata matching the schema: {:#?}", diff --git a/framework_crates/bones_bevy_renderer/src/lib.rs b/framework_crates/bones_bevy_renderer/src/lib.rs index e29c2eaca5..127f365831 100644 --- a/framework_crates/bones_bevy_renderer/src/lib.rs +++ b/framework_crates/bones_bevy_renderer/src/lib.rs @@ -108,6 +108,8 @@ pub struct BonesData { impl BonesBevyRenderer { // TODO: Create a better builder pattern struct for `BonesBevyRenderer`. + // We want to use a nice builder-pattern struct for `BonesBevyRenderer` so that it is easier + // to set options like the `pixel_art` flag or the `game_version`. /// Create a new [`BonesBevyRenderer`] for the provided game. pub fn new(game: bones::Game) -> Self { BonesBevyRenderer { diff --git a/framework_crates/bones_ecs/src/components/typed.rs b/framework_crates/bones_ecs/src/components/typed.rs index 8cf464e3c8..8b56535e40 100644 --- a/framework_crates/bones_ecs/src/components/typed.rs +++ b/framework_crates/bones_ecs/src/components/typed.rs @@ -51,7 +51,6 @@ impl ComponentStore { } // TODO: Replace ComponentStore functions with non-validating ones. - // // Right now functions like `insert`, `get`, and `get_mut` use the checked and panicing versions // of the `untyped` functions. These functions do an extra check to see that the schema matches, // but we've already validated that in the construction of the `ComponentStore`, so we should diff --git a/framework_crates/bones_ecs/src/components/untyped.rs b/framework_crates/bones_ecs/src/components/untyped.rs index 6bd3bac945..65e1965866 100644 --- a/framework_crates/bones_ecs/src/components/untyped.rs +++ b/framework_crates/bones_ecs/src/components/untyped.rs @@ -222,7 +222,14 @@ impl UntypedComponentStore { fn allocate_enough(&mut self, until: usize) { if self.storage.capacity() <= until { self.storage - // TODO: Determine a better policy for resizing component storage. + // TODO: Determine a better policy for resizing and pre-allocating component storage. + // Right now we double the size of the storage every time we run out. It seems like we + // might be able to come up with a smarter policy. On top of that we should + // be able to create a type data for components ( see + // `bones_framework::metadata_asset()` for example ) that lets you customize the resize + // and also pre-allocation strategy for the component. Right now we don't pre-allocate + // any memory, but that could be useful for components that know there will be a lot of + // them, such as bullets. .resize((until + 1) * 2) .unwrap(); } diff --git a/framework_crates/bones_ecs/src/entities.rs b/framework_crates/bones_ecs/src/entities.rs index 9103c0cfee..19f2c03d76 100644 --- a/framework_crates/bones_ecs/src/entities.rs +++ b/framework_crates/bones_ecs/src/entities.rs @@ -87,7 +87,9 @@ pub trait QueryItem { } // TODO: Implement optional component query iterators. - +// We don't have the ability to do `entities.iter_with(Option<&my_comp>)`, but we should +// be able to implement a `QueryItem` for that so that you can iterate over entities that +// _might_ have a component. impl<'a, 'q, T: HasSchema> QueryItem for &'a Comp<'q, T> { type Iter = ComponentBitsetIterator<'a, T>; fn apply_bitset(&self, bitset: &mut BitSetVec) { diff --git a/framework_crates/bones_ecs/src/error.rs b/framework_crates/bones_ecs/src/error.rs index b684d3a2f0..d8382a76d1 100644 --- a/framework_crates/bones_ecs/src/error.rs +++ b/framework_crates/bones_ecs/src/error.rs @@ -2,7 +2,11 @@ use std::error::Error; /// The types of errors used throughout the ECS. // TODO: Re-evaluate `EcsError` variants. -// Some of them may not be used anymore. +// Some these error variants may not be used anymore. Also, I think most of the times +// that we return `EcsError`, there is only one possible error that could occur for that function. +// If that is the case in all situations, we should consider breaking each error type into it's +// own struct, so that we aren't returning an enum with a bunch of errors that will never happen +// for each function call. #[derive(Debug, thiserror::Error)] pub enum EcsError { /// A resource was not initialized in the [`World`][crate::World] but the diff --git a/framework_crates/bones_ecs/src/stage.rs b/framework_crates/bones_ecs/src/stage.rs index abcfd89e3e..65ca9ec872 100644 --- a/framework_crates/bones_ecs/src/stage.rs +++ b/framework_crates/bones_ecs/src/stage.rs @@ -16,7 +16,9 @@ pub struct SystemStages { impl std::fmt::Debug for SystemStages { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SystemStages") - // TODO: add list of stages to the debug render for `SystemStages`. + // TODO: Add list of stages to the debug render for `SystemStages`. + // We can at least list the names of each stage for `SystemStages` debug + // implementation. .finish() } } diff --git a/framework_crates/bones_framework/src/input/gamepad.rs b/framework_crates/bones_framework/src/input/gamepad.rs index 2ea206e0a3..d98a1236b0 100644 --- a/framework_crates/bones_framework/src/input/gamepad.rs +++ b/framework_crates/bones_framework/src/input/gamepad.rs @@ -1,2 +1,5 @@ //! Gamepad input resource. // TODO: implement gamepad input in bones framework. +// We don't have any gamepad imput implementation right now. We should probably +// copy Bevy's strategy for the most part, just like we did with the keyboard and +// mouse modules. diff --git a/framework_crates/bones_framework/src/localization.rs b/framework_crates/bones_framework/src/localization.rs index dc3a49deb6..14d327d303 100644 --- a/framework_crates/bones_framework/src/localization.rs +++ b/framework_crates/bones_framework/src/localization.rs @@ -65,7 +65,6 @@ impl LocalizationAsset { }; // TODO: Log localization formatting errors. - // // We need to find a way to log the errors without allocating every time we format: // https://github.com/projectfluent/fluent-rs/issues/323. b.format_pattern(value, None, &mut vec![]) diff --git a/framework_crates/bones_framework/src/render/camera.rs b/framework_crates/bones_framework/src/render/camera.rs index 64d695f5f0..4464d6ec68 100644 --- a/framework_crates/bones_framework/src/render/camera.rs +++ b/framework_crates/bones_framework/src/render/camera.rs @@ -6,7 +6,10 @@ use crate::prelude::*; /// /// The entity must also have a [`Transform`] component for the camera to render anything. #[derive(Clone, Copy, Debug, HasSchema)] -#[schema(opaque)] // TODO: make repr(C) when `Option`s are supported. +#[schema(opaque)] +// TODO: make repr(C) when `Option`s are supported. +// We don't have `Option` support in `bones_schema` right now. +// Once we do, we can make this type `#[repr(C)]` instead of `#[schema(opaque)]`. pub struct Camera { /// The height of the camera in in-game pixels. /// diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index 8d3adbce51..0e6c23d07b 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -165,6 +165,20 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { default_fn: #default_fn, drop_fn: Some(::raw_drop), // TODO: Allow deriving `hash_fn` and `eq_fn` for `HasSchema`. + // We need to come up with a strategy for deriving the `Hash` and `Eq` + // functions. There are a couple ways of thinking about this. If the + // struct is #[repr(C)] and all of it's fields implement hash and eq, + // then we can automatically implement hash and EQ using all of the + // field's implementations, without having to have the rust struct + // actually implement either. On the other hand, we could have the rust + // struct be required to derive hash and/or eq, and then we just use the + // `RawHash` and `RawEq` traits to get the functions. I think that's + // probably the right direction, but we need to come up with an + // attribute syntax to tell the derive macro to use the Hash and Eq + // rust implmentations. Bevy used `#[reflect(Hash)]`, and we already + // have `#[schema(no_default)]` so maybe we just add a couple flags like + // `#[schema(hash, eq)]` if you want to use the Rust structs + // `Hash` and `Eq` impls. eq_fn: None, hash_fn: None, kind: #schema_mod::SchemaKind::Primitive(#schema_mod::Primitive::Opaque { diff --git a/framework_crates/bones_schema/src/schema.rs b/framework_crates/bones_schema/src/schema.rs index 7501074fce..f98bd6da91 100644 --- a/framework_crates/bones_schema/src/schema.rs +++ b/framework_crates/bones_schema/src/schema.rs @@ -122,8 +122,9 @@ impl Schema { #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] // TODO: Add name fields to `SchemaData`. -// -// We may want to to have both "full" names and "short" names. +// We may want to to have both "full" names and "short" names. We have to think about whether or not +// we want to have some sort of a module path type or just make the full name include the module +// path in whatever way it wants to. pub struct SchemaData { /// The kind of schema. pub kind: SchemaKind, @@ -262,9 +263,10 @@ pub struct StructFieldInfo { /// The schema of the field. pub schema: &'static Schema, // TODO: Investigate adding attribute info to `StructFieldInfo`. - // - // This would allow custom type datas to read and respond to custom attributes on fields as - // well. + // It could be very useful if the derive macro could capture custom attribute data for struct + // fields and put it into the schema data. This would allow type data implementations that + // implement `FromType` to have access to custom attributes that could be used to modify various + // behavior, without requiring a new macro. } /// A type of primitive. diff --git a/framework_crates/bones_schema/src/std_impls.rs b/framework_crates/bones_schema/src/std_impls.rs index 9a9aad5562..373b65cf3c 100644 --- a/framework_crates/bones_schema/src/std_impls.rs +++ b/framework_crates/bones_schema/src/std_impls.rs @@ -130,7 +130,11 @@ mod impl_glam { clone_fn: Some(::raw_clone), drop_fn: Some(::raw_drop), default_fn: Some(::raw_default), - // TODO: Implement hash and eq for `Quat`. + // TODO: Get the schema `hash_fn` and `eq_fn` for the `Quat` type. + // Quats don't implement hash and eq by default because of floating point number + // issues, so we'll have to use a workaround like `CustomRawFns` below to create + // valid implementations of Hash and Eq over the floating points inside the + // Quat. hash_fn: None, eq_fn: None, type_data: Default::default(), @@ -190,6 +194,8 @@ mod impl_glam { schema_impl_glam_vecs!(F64, f64, DVec); // TODO: Implement `HasSchema` for glam matrix types. + // We need to implement `HasSchema` for the matrix types, just like we did with the vector + // types. macro_rules! custom_fns_impl_bvec { ($ty:ident) => { diff --git a/framework_crates/bones_utils/src/key_mod.rs b/framework_crates/bones_utils/src/key_mod.rs index 141331c16e..7c09a80df4 100644 --- a/framework_crates/bones_utils/src/key_mod.rs +++ b/framework_crates/bones_utils/src/key_mod.rs @@ -1,4 +1,6 @@ -// TODO: Replace `Key` with `fstr` crate. +// TODO: Replace `Key` with an inline string crate of some sort. +// There are existing solutions such as `fstr` or `smol_str`, which bevy uses, +// which we shold use instead of rolling our own. /// A small ascii byte array stored on the stack and used similarly to a string to represent things /// like animation keys, etc, without requring a heap allocation. diff --git a/other_crates/bones_matchmaker/src/proxy.rs b/other_crates/bones_matchmaker/src/proxy.rs index af64a6bbea..6c9e72f031 100644 --- a/other_crates/bones_matchmaker/src/proxy.rs +++ b/other_crates/bones_matchmaker/src/proxy.rs @@ -83,6 +83,8 @@ async fn impl_proxy(match_info: MatchInfo, clients: Vec) -> anyhow:: // Spawn task for handling the client's unreliable messages // TODO: De-duplicate matchmaker proxy code if possible. + // These two task pool spawns have very similar code. It could be good to de-duplicate + // it if possible. task_pool .spawn(async move { let result = async {