Remove Bytes, FromBytes, Labels, EntityLabels. Document rest of bevy_core and enable warning on missing docs. (#3521)
This PR is part of the issue #3492. # Objective - Clean up dead code in `bevy_core`. - Add and update the `bevy_core` documentation to achieve a 100% documentation coverage. - Add the #![warn(missing_docs)] lint to keep the documentation coverage for the future. # Solution - Remove unused `Bytes`, `FromBytes`, `Labels`, and `EntityLabels` types and associated systems. - Made several types private that really only have use as internal types, mostly pertaining to fixed timestep execution. - Add and update the bevy_core documentation. - Add the #![warn(missing_docs)] lint. # Open Questions Should more of the internal states of `FixedTimestep` be public? Seems mostly to be an implementation detail unless someone really needs that fixed timestep state.
This commit is contained in:
parent
363bdf78dc
commit
dc8fefe27f
@ -1,103 +0,0 @@
|
|||||||
pub use bevy_derive::Bytes;
|
|
||||||
|
|
||||||
// NOTE: we can reexport common traits and methods from bytemuck to avoid requiring dependency most of
|
|
||||||
// the time, but unfortunately we can't use derive macros that way due to hardcoded path in generated code.
|
|
||||||
pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
|
|
||||||
|
|
||||||
// FIXME: `Bytes` trait doesn't specify the expected encoding format,
|
|
||||||
// which means types that implement it have to know what format is expected
|
|
||||||
// and can only implement one encoding at a time.
|
|
||||||
// TODO: Remove `Bytes` and `FromBytes` in favour of `crevice` crate.
|
|
||||||
|
|
||||||
/// Converts the implementing type to bytes by writing them to a given buffer
|
|
||||||
pub trait Bytes {
|
|
||||||
/// Converts the implementing type to bytes by writing them to a given buffer
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]);
|
|
||||||
|
|
||||||
/// The number of bytes that will be written when calling `write_bytes`
|
|
||||||
fn byte_len(&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Bytes for T
|
|
||||||
where
|
|
||||||
T: Pod,
|
|
||||||
{
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
|
||||||
buffer[0..self.byte_len()].copy_from_slice(bytes_of(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
std::mem::size_of::<Self>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts a byte array to `Self`
|
|
||||||
pub trait FromBytes {
|
|
||||||
/// Converts a byte array to `Self`
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromBytes for T
|
|
||||||
where
|
|
||||||
T: Pod,
|
|
||||||
{
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Self {
|
|
||||||
assert_eq!(
|
|
||||||
bytes.len(),
|
|
||||||
std::mem::size_of::<T>(),
|
|
||||||
"Cannot convert byte slice `&[u8]` to type `{}`. They are not the same size.",
|
|
||||||
std::any::type_name::<T>()
|
|
||||||
);
|
|
||||||
unsafe { bytes.as_ptr().cast::<T>().read_unaligned() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use super::{Bytes, FromBytes};
|
|
||||||
use bevy_math::{Mat4, Vec2, Vec3, Vec4};
|
|
||||||
|
|
||||||
fn test_round_trip<T: Bytes + FromBytes + std::fmt::Debug + PartialEq>(value: T) {
|
|
||||||
let mut bytes = vec![0; value.byte_len()];
|
|
||||||
value.write_bytes(&mut bytes);
|
|
||||||
let result = T::from_bytes(&bytes);
|
|
||||||
assert_eq!(value, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_u32_bytes_round_trip() {
|
|
||||||
test_round_trip(123u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_f64_bytes_round_trip() {
|
|
||||||
test_round_trip(123f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_round_trip() {
|
|
||||||
test_round_trip(Vec2::new(1.0, 2.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_round_trip() {
|
|
||||||
test_round_trip(Vec3::new(1.0, 2.0, 3.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_round_trip() {
|
|
||||||
test_round_trip(Vec4::new(1.0, 2.0, 3.0, 4.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_round_trip() {
|
|
||||||
test_round_trip(Mat4::IDENTITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_array_round_trip() {
|
|
||||||
test_round_trip([-10i32; 1024]);
|
|
||||||
test_round_trip([Vec2::ZERO, Vec2::ONE, Vec2::Y, Vec2::X]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
use crate::bytes_of;
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
@ -42,12 +41,12 @@ impl Hash for FloatOrd {
|
|||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
if self.0.is_nan() {
|
if self.0.is_nan() {
|
||||||
// Ensure all NaN representations hash to the same value
|
// Ensure all NaN representations hash to the same value
|
||||||
state.write(bytes_of(&f32::NAN))
|
state.write(bytemuck::bytes_of(&f32::NAN))
|
||||||
} else if self.0 == 0.0 {
|
} else if self.0 == 0.0 {
|
||||||
// Ensure both zeroes hash to the same value
|
// Ensure both zeroes hash to the same value
|
||||||
state.write(bytes_of(&0.0f32))
|
state.write(bytemuck::bytes_of(&0.0f32))
|
||||||
} else {
|
} else {
|
||||||
state.write(bytes_of(&self.0));
|
state.write(bytemuck::bytes_of(&self.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,256 +0,0 @@
|
|||||||
use bevy_ecs::{
|
|
||||||
component::Component,
|
|
||||||
entity::Entity,
|
|
||||||
query::Changed,
|
|
||||||
reflect::ReflectComponent,
|
|
||||||
system::{Query, RemovedComponents, ResMut},
|
|
||||||
};
|
|
||||||
use bevy_reflect::Reflect;
|
|
||||||
use bevy_utils::{HashMap, HashSet};
|
|
||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
fmt::Debug,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A collection of labels
|
|
||||||
#[derive(Component, Default, Reflect)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct Labels {
|
|
||||||
labels: HashSet<Cow<'static, str>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Labels {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let mut list = f.debug_list();
|
|
||||||
for label in self.iter() {
|
|
||||||
list.entry(&label);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, L: Into<Cow<'static, str>>> From<T> for Labels
|
|
||||||
where
|
|
||||||
T: IntoIterator<Item = L>,
|
|
||||||
{
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
let mut labels = HashSet::default();
|
|
||||||
for label in value {
|
|
||||||
labels.insert(label.into());
|
|
||||||
}
|
|
||||||
Self { labels }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Labels {
|
|
||||||
pub fn contains<T: Into<Cow<'static, str>>>(&self, label: T) -> bool {
|
|
||||||
self.labels.contains(&label.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert<T: Into<Cow<'static, str>>>(&mut self, label: T) {
|
|
||||||
self.labels.insert(label.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, label: T) {
|
|
||||||
self.labels.remove(&label.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &str> {
|
|
||||||
self.labels.iter().map(|label| label.deref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maintains a mapping from [Entity](bevy_ecs::prelude::Entity) ids to entity labels and entity
|
|
||||||
/// labels to [Entities](bevy_ecs::prelude::Entity).
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct EntityLabels {
|
|
||||||
label_entities: HashMap<Cow<'static, str>, Vec<Entity>>,
|
|
||||||
entity_labels: HashMap<Entity, HashSet<Cow<'static, str>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntityLabels {
|
|
||||||
pub fn get(&self, label: &str) -> &[Entity] {
|
|
||||||
self.label_entities
|
|
||||||
.get(label)
|
|
||||||
.map(|entities| entities.as_slice())
|
|
||||||
.unwrap_or(&[])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn entity_labels_system(
|
|
||||||
mut entity_labels: ResMut<EntityLabels>,
|
|
||||||
removed_labels: RemovedComponents<Labels>,
|
|
||||||
query: Query<(Entity, &Labels), Changed<Labels>>,
|
|
||||||
) {
|
|
||||||
let entity_labels = entity_labels.deref_mut();
|
|
||||||
|
|
||||||
for entity in removed_labels.iter() {
|
|
||||||
if let Some(labels) = entity_labels.entity_labels.get(&entity) {
|
|
||||||
for label in labels.iter() {
|
|
||||||
if let Some(entities) = entity_labels.label_entities.get_mut(label) {
|
|
||||||
entities.retain(|e| *e != entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, labels) in query.iter() {
|
|
||||||
let current_labels = entity_labels
|
|
||||||
.entity_labels
|
|
||||||
.entry(entity)
|
|
||||||
.or_insert_with(HashSet::default);
|
|
||||||
|
|
||||||
for removed_label in current_labels.difference(&labels.labels) {
|
|
||||||
if let Some(entities) = entity_labels.label_entities.get_mut(removed_label) {
|
|
||||||
entities.retain(|e| *e != entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for added_label in labels.labels.difference(current_labels) {
|
|
||||||
entity_labels
|
|
||||||
.label_entities
|
|
||||||
.entry(added_label.clone())
|
|
||||||
.or_insert_with(Vec::new)
|
|
||||||
.push(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
*current_labels = labels.labels.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use bevy_ecs::{
|
|
||||||
schedule::{Schedule, Stage, SystemStage},
|
|
||||||
world::World,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn setup() -> (World, Schedule) {
|
|
||||||
let mut world = World::new();
|
|
||||||
world.insert_resource(EntityLabels::default());
|
|
||||||
let mut schedule = Schedule::default();
|
|
||||||
schedule.add_stage("test", SystemStage::single_threaded());
|
|
||||||
schedule.add_system_to_stage("test", entity_labels_system);
|
|
||||||
(world, schedule)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn holy_cow() -> Labels {
|
|
||||||
Labels::from(["holy", "cow"].iter().cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn holy_shamoni() -> Labels {
|
|
||||||
Labels::from(["holy", "shamoni"].iter().cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn adds_spawned_entity() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[e1], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[e1], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_labels() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
world.get_mut::<Labels>(e1).unwrap().insert("shalau");
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[e1], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[e1], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[e1], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_labels() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
world.get_mut::<Labels>(e1).unwrap().remove("holy");
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[e1], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn removes_despawned_entity() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
assert!(world.despawn(e1));
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn removes_labels_when_component_removed() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
world.entity_mut(e1).remove::<Labels>().unwrap();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn adds_another_spawned_entity() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let e2 = world.spawn().insert(holy_shamoni()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[e1, e2], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[e1], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shamoni"), &[e2], "shamoni");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn removes_despawned_entity_but_leaves_other() {
|
|
||||||
let (mut world, mut schedule) = setup();
|
|
||||||
let e1 = world.spawn().insert(holy_cow()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let e2 = world.spawn().insert(holy_shamoni()).id();
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
assert!(world.despawn(e1));
|
|
||||||
schedule.run(&mut world);
|
|
||||||
|
|
||||||
let entity_labels = world.get_resource::<EntityLabels>().unwrap();
|
|
||||||
assert_eq!(entity_labels.get("holy"), &[e2], "holy");
|
|
||||||
assert_eq!(entity_labels.get("cow"), &[], "cow");
|
|
||||||
assert_eq!(entity_labels.get("shamoni"), &[e2], "shamoni");
|
|
||||||
assert_eq!(entity_labels.get("shalau"), &[], "shalau");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,21 @@
|
|||||||
mod bytes;
|
#![warn(missing_docs)]
|
||||||
|
//! This crate provides core functionality for Bevy Engine.
|
||||||
|
|
||||||
mod float_ord;
|
mod float_ord;
|
||||||
mod label;
|
|
||||||
mod name;
|
mod name;
|
||||||
mod task_pool_options;
|
mod task_pool_options;
|
||||||
mod time;
|
mod time;
|
||||||
|
|
||||||
pub use bytes::*;
|
pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable};
|
||||||
pub use float_ord::*;
|
pub use float_ord::*;
|
||||||
pub use label::*;
|
|
||||||
pub use name::*;
|
pub use name::*;
|
||||||
pub use task_pool_options::DefaultTaskPoolOptions;
|
pub use task_pool_options::DefaultTaskPoolOptions;
|
||||||
pub use time::*;
|
pub use time::*;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
//! The Bevy Core Prelude.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use crate::{DefaultTaskPoolOptions, EntityLabels, Labels, Name, Time, Timer};
|
pub use crate::{DefaultTaskPoolOptions, Name, Time, Timer};
|
||||||
}
|
}
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
@ -30,6 +31,7 @@ use std::ops::Range;
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CorePlugin;
|
pub struct CorePlugin;
|
||||||
|
|
||||||
|
/// A `SystemLabel` enum for ordering systems relative to core Bevy systems.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
|
||||||
pub enum CoreSystem {
|
pub enum CoreSystem {
|
||||||
/// Updates the elapsed time. Any system that interacts with [Time] component should run after
|
/// Updates the elapsed time. Any system that interacts with [Time] component should run after
|
||||||
@ -47,13 +49,11 @@ impl Plugin for CorePlugin {
|
|||||||
.create_default_pools(&mut app.world);
|
.create_default_pools(&mut app.world);
|
||||||
|
|
||||||
app.init_resource::<Time>()
|
app.init_resource::<Time>()
|
||||||
.init_resource::<EntityLabels>()
|
|
||||||
.init_resource::<FixedTimesteps>()
|
.init_resource::<FixedTimesteps>()
|
||||||
.register_type::<HashSet<String>>()
|
.register_type::<HashSet<String>>()
|
||||||
.register_type::<Option<String>>()
|
.register_type::<Option<String>>()
|
||||||
.register_type::<Entity>()
|
.register_type::<Entity>()
|
||||||
.register_type::<Name>()
|
.register_type::<Name>()
|
||||||
.register_type::<Labels>()
|
|
||||||
.register_type::<Range<f32>>()
|
.register_type::<Range<f32>>()
|
||||||
.register_type::<Timer>()
|
.register_type::<Timer>()
|
||||||
// time system is added as an "exclusive system" to ensure it runs before other systems
|
// time system is added as an "exclusive system" to ensure it runs before other systems
|
||||||
@ -61,9 +61,7 @@ impl Plugin for CorePlugin {
|
|||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::First,
|
CoreStage::First,
|
||||||
time_system.exclusive_system().label(CoreSystem::Time),
|
time_system.exclusive_system().label(CoreSystem::Time),
|
||||||
)
|
);
|
||||||
.add_startup_system_to_stage(StartupStage::PostStartup, entity_labels_system)
|
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, entity_labels_system);
|
|
||||||
|
|
||||||
register_rust_types(app);
|
register_rust_types(app);
|
||||||
register_math_types(app);
|
register_math_types(app);
|
||||||
|
@ -8,6 +8,11 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Component used to identify an entity. Stores a hash for faster comparisons
|
/// Component used to identify an entity. Stores a hash for faster comparisons
|
||||||
|
/// The hash is eagerly re-computed upon each update to the name.
|
||||||
|
///
|
||||||
|
/// [`Name`] should not be treated as a globally unique identifier for entities,
|
||||||
|
/// as multiple entities can have the same name. [`bevy_ecs::entity::Entity`] should be
|
||||||
|
/// used instead as the default unique identifier.
|
||||||
#[derive(Component, Debug, Clone, Reflect)]
|
#[derive(Component, Debug, Clone, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Name {
|
pub struct Name {
|
||||||
@ -22,6 +27,9 @@ impl Default for Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Name {
|
impl Name {
|
||||||
|
/// Creates a new [`Name`] from any string-like type.
|
||||||
|
///
|
||||||
|
/// The internal hash will be computed immediately.
|
||||||
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
|
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let mut name = Name { name, hash: 0 };
|
let mut name = Name { name, hash: 0 };
|
||||||
@ -29,17 +37,25 @@ impl Name {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the entity's name.
|
||||||
|
///
|
||||||
|
/// The internal hash will be re-computed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
|
pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
|
||||||
*self = Name::new(name);
|
*self = Name::new(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the name of the entity in place.
|
||||||
|
///
|
||||||
|
/// This will allocate a new string if the name was previously
|
||||||
|
/// created from a borrow.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
||||||
f(self.name.to_mut());
|
f(self.name.to_mut());
|
||||||
self.update_hash();
|
self.update_hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the name of the entity as a `&str`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
|
@ -10,45 +10,66 @@ use bevy_ecs::{
|
|||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
/// The internal state of each [`FixedTimestep`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FixedTimestepState {
|
pub struct FixedTimestepState {
|
||||||
pub step: f64,
|
step: f64,
|
||||||
pub accumulator: f64,
|
accumulator: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FixedTimestepState {
|
impl FixedTimestepState {
|
||||||
/// The amount of time each step takes
|
/// The amount of time each step takes.
|
||||||
pub fn step(&self) -> f64 {
|
pub fn step(&self) -> f64 {
|
||||||
self.step
|
self.step
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of steps made in a second
|
/// The number of steps made in a second.
|
||||||
pub fn steps_per_second(&self) -> f64 {
|
pub fn steps_per_second(&self) -> f64 {
|
||||||
1.0 / self.step
|
1.0 / self.step
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The amount of time (in seconds) left over from the last step
|
/// The amount of time (in seconds) left over from the last step.
|
||||||
pub fn accumulator(&self) -> f64 {
|
pub fn accumulator(&self) -> f64 {
|
||||||
self.accumulator
|
self.accumulator
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The percentage of "step" stored inside the accumulator. Calculated as accumulator / step
|
/// The percentage of "step" stored inside the accumulator. Calculated as accumulator / step.
|
||||||
pub fn overstep_percentage(&self) -> f64 {
|
pub fn overstep_percentage(&self) -> f64 {
|
||||||
self.accumulator / self.step
|
self.accumulator / self.step
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A global resource that tracks the individual [`FixedTimestepState`]s
|
||||||
|
/// for every labeled [`FixedTimestep`].
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FixedTimesteps {
|
pub struct FixedTimesteps {
|
||||||
fixed_timesteps: HashMap<String, FixedTimestepState>,
|
fixed_timesteps: HashMap<String, FixedTimestepState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FixedTimesteps {
|
impl FixedTimesteps {
|
||||||
|
/// Gets the [`FixedTimestepState`] for a given label.
|
||||||
pub fn get(&self, name: &str) -> Option<&FixedTimestepState> {
|
pub fn get(&self, name: &str) -> Option<&FixedTimestepState> {
|
||||||
self.fixed_timesteps.get(name)
|
self.fixed_timesteps.get(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A system run criteria that enables systems or stages to run at a fixed timestep between executions.
|
||||||
|
///
|
||||||
|
/// This does not guarentee that the time elapsed between executions is exactly the provided
|
||||||
|
/// fixed timestep, but will guarentee that the execution will run multiple times per game tick
|
||||||
|
/// until the number of repetitions is as expected.
|
||||||
|
///
|
||||||
|
/// For example, a system with a fixed timestep run criteria of 120 times per second will run
|
||||||
|
/// two times during a ~16.667ms frame, once during a ~8.333ms frame, and once every two frames
|
||||||
|
/// with ~4.167ms frames. However, the same criteria may not result in exactly 8.333ms passing
|
||||||
|
/// between each execution.
|
||||||
|
///
|
||||||
|
/// When using this run criteria, it is advised not to rely on [`Time::delta`] or any of it's
|
||||||
|
/// variants for game simulation, but rather use the constant time delta used to initialize the
|
||||||
|
/// [`FixedTimestep`] instead.
|
||||||
|
///
|
||||||
|
/// For more fine tuned information about the execution status of a given fixed timestep,
|
||||||
|
/// use the [`FixedTimesteps`] resource.
|
||||||
pub struct FixedTimestep {
|
pub struct FixedTimestep {
|
||||||
state: LocalFixedTimestepState,
|
state: LocalFixedTimestepState,
|
||||||
internal_system: Box<dyn System<In = (), Out = ShouldRun>>,
|
internal_system: Box<dyn System<In = (), Out = ShouldRun>>,
|
||||||
@ -64,6 +85,7 @@ impl Default for FixedTimestep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FixedTimestep {
|
impl FixedTimestep {
|
||||||
|
/// Creates a [`FixedTimestep`] that ticks once every `step` seconds.
|
||||||
pub fn step(step: f64) -> Self {
|
pub fn step(step: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: LocalFixedTimestepState {
|
state: LocalFixedTimestepState {
|
||||||
@ -74,6 +96,7 @@ impl FixedTimestep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`FixedTimestep`] that ticks once every `rate` times per second.
|
||||||
pub fn steps_per_second(rate: f64) -> Self {
|
pub fn steps_per_second(rate: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: LocalFixedTimestepState {
|
state: LocalFixedTimestepState {
|
||||||
@ -84,6 +107,8 @@ impl FixedTimestep {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the label for the timestep. Setting a label allows a timestep
|
||||||
|
/// to be observed by the global [`FixedTimesteps`] resource.
|
||||||
pub fn with_label(mut self, label: &str) -> Self {
|
pub fn with_label(mut self, label: &str) -> Self {
|
||||||
self.state.label = Some(label.to_string());
|
self.state.label = Some(label.to_string());
|
||||||
self
|
self
|
||||||
@ -106,7 +131,7 @@ impl FixedTimestep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LocalFixedTimestepState {
|
struct LocalFixedTimestepState {
|
||||||
label: Option<String>, // TODO: consider making this a TypedLabel
|
label: Option<String>, // TODO: consider making this a TypedLabel
|
||||||
step: f64,
|
step: f64,
|
||||||
accumulator: f64,
|
accumulator: f64,
|
||||||
|
@ -55,11 +55,30 @@ impl Stopwatch {
|
|||||||
/// stopwatch.tick(Duration::from_secs(1));
|
/// stopwatch.tick(Duration::from_secs(1));
|
||||||
/// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
|
/// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// # See Also
|
||||||
|
///
|
||||||
|
/// [`elapsed_secs`](Stopwatch::elapsed) - if a `f32` value is desirable instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn elapsed(&self) -> Duration {
|
pub fn elapsed(&self) -> Duration {
|
||||||
self.elapsed
|
self.elapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
|
||||||
|
/// of the stopwatch, in seconds.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_core::*;
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// let mut stopwatch = Stopwatch::new();
|
||||||
|
/// stopwatch.tick(Duration::from_secs(1));
|
||||||
|
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # See Also
|
||||||
|
///
|
||||||
|
/// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn elapsed_secs(&self) -> f32 {
|
pub fn elapsed_secs(&self) -> f32 {
|
||||||
self.elapsed().as_secs_f32()
|
self.elapsed().as_secs_f32()
|
||||||
|
@ -28,9 +28,9 @@ impl Default for Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Time {
|
impl Time {
|
||||||
|
/// Updates the internal time measurements.
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
let now = Instant::now();
|
self.update_with_instant(Instant::now());
|
||||||
self.update_with_instant(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_with_instant(&mut self, instant: Instant) {
|
pub(crate) fn update_with_instant(&mut self, instant: Instant) {
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
use bevy_macro_utils::BevyManifest;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields};
|
|
||||||
|
|
||||||
pub fn derive_bytes(input: TokenStream) -> TokenStream {
|
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
let fields = match &ast.data {
|
|
||||||
Data::Struct(DataStruct {
|
|
||||||
fields: Fields::Named(fields),
|
|
||||||
..
|
|
||||||
}) => &fields.named,
|
|
||||||
_ => panic!("Expected a struct with named fields."),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bevy_core_path = BevyManifest::default().get_path(crate::modules::BEVY_CORE);
|
|
||||||
|
|
||||||
let fields = fields
|
|
||||||
.iter()
|
|
||||||
.map(|field| field.ident.as_ref().unwrap())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let struct_name = &ast.ident;
|
|
||||||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
impl #impl_generics #bevy_core_path::Bytes for #struct_name #ty_generics #where_clause {
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
|
||||||
let mut offset: usize = 0;
|
|
||||||
#(let byte_len = self.#fields.byte_len();
|
|
||||||
self.#fields.write_bytes(&mut buffer[offset..(offset + byte_len)]);
|
|
||||||
offset += byte_len;)*
|
|
||||||
}
|
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
let mut byte_len: usize = 0;
|
|
||||||
#(byte_len += self.#fields.byte_len();)*
|
|
||||||
byte_len
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
mod app_plugin;
|
mod app_plugin;
|
||||||
mod bevy_main;
|
mod bevy_main;
|
||||||
mod bytes;
|
|
||||||
mod enum_variant_meta;
|
mod enum_variant_meta;
|
||||||
mod modules;
|
mod modules;
|
||||||
|
|
||||||
@ -10,12 +9,6 @@ use bevy_macro_utils::{derive_label, BevyManifest};
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
|
|
||||||
/// Derives the Bytes trait. Each field must also implements Bytes or this will fail.
|
|
||||||
#[proc_macro_derive(Bytes)]
|
|
||||||
pub fn derive_bytes(input: TokenStream) -> TokenStream {
|
|
||||||
bytes::derive_bytes(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a dynamic plugin entry point function for the given `Plugin` type.
|
/// Generates a dynamic plugin entry point function for the given `Plugin` type.
|
||||||
#[proc_macro_derive(DynamicPlugin)]
|
#[proc_macro_derive(DynamicPlugin)]
|
||||||
pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
||||||
|
@ -1,2 +1 @@
|
|||||||
pub const BEVY_CORE: &str = "bevy_core";
|
|
||||||
pub const BEVY_UTILS: &str = "bevy_utils";
|
pub const BEVY_UTILS: &str = "bevy_utils";
|
||||||
|
@ -3,7 +3,6 @@ mod colorspace;
|
|||||||
pub use colorspace::*;
|
pub use colorspace::*;
|
||||||
|
|
||||||
use crate::color::{HslRepresentation, SrgbColorSpace};
|
use crate::color::{HslRepresentation, SrgbColorSpace};
|
||||||
use bevy_core::Bytes;
|
|
||||||
use bevy_math::{Vec3, Vec4};
|
use bevy_math::{Vec3, Vec4};
|
||||||
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
|
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -1035,58 +1034,6 @@ impl MulAssign<[f32; 3]> for Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bytes for Color {
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
|
||||||
match *self {
|
|
||||||
Color::Rgba {
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha,
|
|
||||||
} => {
|
|
||||||
red.nonlinear_to_linear_srgb().write_bytes(buffer);
|
|
||||||
green
|
|
||||||
.nonlinear_to_linear_srgb()
|
|
||||||
.write_bytes(&mut buffer[std::mem::size_of::<f32>()..]);
|
|
||||||
blue.nonlinear_to_linear_srgb()
|
|
||||||
.write_bytes(&mut buffer[2 * std::mem::size_of::<f32>()..]);
|
|
||||||
alpha.write_bytes(&mut buffer[3 * std::mem::size_of::<f32>()..]);
|
|
||||||
}
|
|
||||||
Color::RgbaLinear {
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
alpha,
|
|
||||||
} => {
|
|
||||||
red.write_bytes(buffer);
|
|
||||||
green.write_bytes(&mut buffer[std::mem::size_of::<f32>()..]);
|
|
||||||
blue.write_bytes(&mut buffer[2 * std::mem::size_of::<f32>()..]);
|
|
||||||
alpha.write_bytes(&mut buffer[3 * std::mem::size_of::<f32>()..]);
|
|
||||||
}
|
|
||||||
Color::Hsla {
|
|
||||||
hue,
|
|
||||||
saturation,
|
|
||||||
lightness,
|
|
||||||
alpha,
|
|
||||||
} => {
|
|
||||||
let [red, green, blue] =
|
|
||||||
HslRepresentation::hsl_to_nonlinear_srgb(hue, saturation, lightness);
|
|
||||||
red.nonlinear_to_linear_srgb().write_bytes(buffer);
|
|
||||||
green
|
|
||||||
.nonlinear_to_linear_srgb()
|
|
||||||
.write_bytes(&mut buffer[std::mem::size_of::<f32>()..]);
|
|
||||||
blue.nonlinear_to_linear_srgb()
|
|
||||||
.write_bytes(&mut buffer[std::mem::size_of::<f32>() * 2..]);
|
|
||||||
alpha.write_bytes(&mut buffer[std::mem::size_of::<f32>() * 3..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
std::mem::size_of::<f32>() * 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum HexColorError {
|
pub enum HexColorError {
|
||||||
Length,
|
Length,
|
||||||
|
Loading…
Reference in New Issue
Block a user