High performance Rust ECS library

Related tags

rust ecs
Overview

Legion ECS

Build Status Crates.io Docs.rs

Legion aims to be a feature rich high performance Entity component system (ECS) library for Rust game projects with minimal boilerplate.

Getting Started

Worlds

Worlds are collections of entities, where each entity can have an arbitrary collection of components attached.

use legion::*;
let world = World::default();

Entities can be inserted via either push (for a single entity) or extend (for a collection of entities with the same component types). The world will create a unique ID for each entity upon insertion that you can use to refer to that entity later.

// a component is any type that is 'static, sized, send and sync
#[derive(Clone, Copy, Debug, PartialEq)]
struct Position {
    x: f32,
    y: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Velocity {
    dx: f32,
    dy: f32,
}

// push a component tuple into the world to create an entity
let entity: Entity = world.push((Position { x: 0.0, y: 0.0 }, Velocity { dx: 0.0, dy: 0.0 }));

// or extend via an IntoIterator of tuples to add many at once (this is faster)
let entities: &[Entity] = world.extend(vec![
    (Position { x: 0.0, y: 0.0 }, Velocity { dx: 0.0, dy: 0.0 }),
    (Position { x: 1.0, y: 1.0 }, Velocity { dx: 0.0, dy: 0.0 }),
    (Position { x: 2.0, y: 2.0 }, Velocity { dx: 0.0, dy: 0.0 }),
]);

You can access entities via entries. Entries allow you to query an entity to find out what types of components are attached to it, to get component references, or to add and remove components.

// entries return `None` if the entity does not exist
if let Some(mut entry) = world.entry(entity) {
    // access information about the entity's archetype
    println!("{:?} has {:?}", entity, entry.archetype().layout().component_types());

    // add an extra component
    entry.add_component(12f32);

    // access the entity's components, returns `None` if the entity does not have the component
    assert_eq!(entry.get_component::<f32>().unwrap(), &12f32);
}

Queries

Entries are not the most convenient or performant way to search or bulk-access a world. Queries allow for high performance and expressive iteration through the entities in a world.

// you define a query be declaring what components you want to find, and how you will access them
let mut query = <&Position>::query();

// you can then iterate through the components found in the world
for position in query.iter(&world) {
    println!("{:?}", position);
}

You can search for entities which have all of a set of components.

// construct a query from a "view tuple"
let mut query = <(&Velocity, &mut Position)>::query();

// this time we have &Velocity and &mut Position
for (velocity, position) in query.iter_mut(&mut world) {
    position.x += velocity.x;
    position.y += velocity.y;
}

You can augment a basic query with additional filters. For example, you can choose to exclude entities which also have a certain component, or only include entities for which a certain component has changed since the last time the query ran (this filtering is conservative and coarse-grained)

// you can use boolean expressions when adding filters
let mut query = <(&Velocity, &mut Position)>::query()
    .filter(!component::<Ignore>() & maybe_changed::<Position>());

for (velocity, position) in query.iter_mut(&mut world) {
    position.x += velocity.dx;
    position.y += velocity.dy;
}

There is much more than can be done with queries. See the module documentation for more information.

Systems

You may have noticed that when we wanted to write to a component, we needed to use iter_mut to iterate through our query. But perhaps your application wants to be able to process different components on different entities, perhaps even at the same time in parallel? While it is possible to do this manually (see World::split), this is very difficult to do when the different pieces of the application don't know what components each other need, or might or might not even have conflicting access requirements.

Systems and the Schedule automates this process, and can even schedule work at a more granular level than you can otherwise do manually. A system is a unit of work. Each system is defined as a function which is provided access to queries and shared resources. These systems can then be appended to a schedule, which is a linear sequence of systems, ordered by when side effects (such as writes to components) should be observed. The schedule will automatically parallelize the execution of all systems whilst maintaining the apparent order of execution from the perspective of each system.

// a system fn which loops through Position and Velocity components, and reads the Time shared resource
// the #[system] macro generates a fn called update_positions_system() which will construct our system
#[system(for_each)]
fn update_positions(pos: &mut Position, vel: &Velocity, #[resource] time: &Time) {
    pos.x += vel.dx * time.elapsed_seconds;
    pos.y += vel.dy * time.elapsed_seconds;
}

// construct a schedule (you should do this on init)
let mut schedule = Schedule::builder()
    .add_system(update_positions_system())
    .build();

// run our schedule (you should do this each update)
schedule.execute(&mut world, &mut resources);

See the systems module and the system proc macro for more information.

Feature Flags

Legion provides a few feature flags:

  • parallel - Enables parallel iterators and parallel schedule execution via the rayon library. Enabled by default.
  • extended-tuple-impls - Extends the maximum size of view and component tuples from 8 to 24, at the cost of increased compile times. Off by default.
  • serialize - Enables the serde serialization module and associated functionality. Enabled by default.
  • crossbeam-events - Implements the EventSender trait for crossbeam Sender channels, allowing them to be used for event subscriptions. Enabled by default.

WASM

Legion runs with parallelism on by default, which is not currently supported by Web Assembly as it runs single-threaded. Therefore, to build for WASM, ensure you set default-features = false in Cargo.toml. Additionally, if you want to use the serialize feature, you must enable either the stdweb or wasm-bindgen features, which will be proxied through to the uuid crate. See the uuid crate for more information.

legion = { version = "*", default-features = false, features = ["wasm-bindgen"] }
Issues
  • Experimental refactor in legion_experiment crate

    Experimental refactor in legion_experiment crate

    This started as a simple refactor of legion into smaller sub-modules, as some of the module files were well over 1k LoC and were getting unmaintainable. However, while moving things around, I found some soundness issues and saw some opportunity to simplify things. Those in turn caused more changes downstream, and the simplifications made other improvements that I had been planning fairly trivial to implement. This ended up being a much larger and more impactful refactor of the entire legion_core crate than I had originally planned. This work is not yet complete, but it is wide reaching enough that I want to give everyone the opportunity to see what I've been doing and collect some feedback.

    The general changes are:

    • Much shorter module files, each with a more clearly defined scope.
    • Simpler code in general. I don't have a line count yet, but I expect this to have shaved a few hundred lines off the library overall.
    • Removed many uses of unsafe.

    Changes to World (which now more closely resembles a std collection):

    • Added len to retrieve entity count.
    • Renamed insert to extend.
    • Added push as sugar for inserting a single entity.
    • Renamed delete to remove.
    • Renamed is_alive to contains.
    • Added entity(Entity) -> Entry and entity_mut(Entity) -> EntryMut functions. Entries contain the APIs for interacting with a single entity. e.g. get_component, add_component and layout (which in turn offers has_component etc).
    • iter and iter_mut for iterating through all entity entries.
    • You can now insert with a tuple of component vecs, in addition to an iterator of component tuples. This is about 50% faster.

    Changes to storage:

    • Much clearer distinction between structural changes to storage (e.g. adding entities) and the interior mutability involved in accessing components.
    • Chunk metadata has been pulled out into a LayoutIndex.
    • The layout index is optimised for search and provides a .search(EntityFilter) -> impl Iterator<Item = ArchetypeIndex> function.
    • Renamed "archetype" to "layout".
    • Renamed "chunk set" to "archetype".
    • Storage now derefs into [Archetype] and can be indexed by ArchetypeIndex.
    • Chunks no longer free their internal memory when empty. Defrag now deletes empty chunks.
    • Event subscribers can ask to recieve initial creation/insertion events for pre-existing archetypes and entities when they subscribe.

    Changes to filters:

    • No longer implemented as iterators. This was a pain without GATs (it is the canonical streaming iterator problem).
    • Filters now only provide their matching logic.
    • Distinction made between stateless layout/archetype filters and stateful chunk filters.
    • Filters can now cache their matches and incrementally update queries. System queries should do this by default in the future.

    It is likely that SystemQuery won't be needed anymore, too.

    I don't forsee any more major API changes beyond completing this rework, and legion v1.0.0 will likely closely resemble this, after which things should stabilise and breaking changes will hopefully be rare.

    opened by TomGillen 36
  • Improved public API soundess

    Improved public API soundess

    Changed World and Query APIs to require &mut World, such that it is statically impossible to write safe code with unsound borrowing. New unsafe _unchecked alternatives have been added with the previous behaviour.

    This required changing the PreparedQuery APIs to also require they be given a PreparedWorld reference, in order to control whether a system can execute two of its queries at the same time.

    PreparedWorld now provides more of the API that is available on World. PreparedWorld allows random access to components and archetypes that a system's queries declare access to. SystemBuilder::[read/write]_component can now be used to mark the system as accessing all archetypes.

    opened by TomGillen 18
  • "query attempted to access archetype unavailable via sub world": Bug or just me?

    Here is some code to reproduce (not sure if this is minimal)

    use legion::prelude::*;
    use tracing::{info, Level};
    use tracing_subscriber;
    
    #[derive(Debug)]
    struct Money(f64);
    #[derive(Debug)]
    struct Health(f64);
    struct Food(f64);
    
    
    fn main() {
        let subscriber = tracing_subscriber::fmt()
            .with_max_level(Level::TRACE)
            .init();
    
        let universe = Universe::new();
        let mut world = universe.create_world();
    
        world.insert((),
            vec![
                (Money(5.0), Food(5.0)),
            ]
        );
        
        world.insert((),
            vec![
                (Money(4.0), Health(3.0)),
                (Money(4.0), Health(3.0)),
                (Money(4.0), Health(3.0)),
            ]
        );
    
        let show_me_the_money = SystemBuilder::new("money_show")
            .with_query(<(Read<Money>, Read<Food>)>::query())
            .build(|_, world, _, query| {
                for (money, food) in query.iter(world) {
                    info!("Look at my money {:?}", money);
                }
            });
    
        let health_conscious = SystemBuilder::new("healthy")
            .with_query(<(Read<Money>, Read<Health>)>::query())
            .build(|_, world, _, query| {
                for (money, health) in query.iter(world) {
                    info!("So healthy {:?}", health);
                }
            });
    
        let mut schedule = Schedule::builder()
            .add_system(show_me_the_money)
            .flush()
            .add_system(health_conscious)
            .flush()
            .build();
    
        let mut resources = Resources::default();
        schedule.execute(&mut world, &mut resources);
    }
    

    If you comment out either of the world.insert... statements, it runs without errors. Am I doing something wrong here?

    opened by travis-leith 13
  • Random access APIs should be marked as unsafe

    Random access APIs should be marked as unsafe

    Random access APIs, such as World.get_component cannot have their safety expressed statically in Rust's type system. Instead, they are runtime borrow checked.

    However, this runtime borrow checking is only there as a diagnostic aid; it will provide more useful fail-fast behaviour and stack traces, rather than the state corruption that would otherwise have to be debugged.

    The user must still carefully consider how these APIs are used and ensure that they are not breaking any borrowing rules. Therefore, these functions should still be marked as unsafe, to more clearly communicate this responsibility.

    type: bug 
    opened by TomGillen 10
  • Track component changes like resource changes

    Track component changes like resource changes

    • Track reads as well as writes
    • Writes are dependent on reads to avoid simultanious execution
    • Reads handled after writes to prevent overriting last read
    opened by Guvante 9
  • What is the conceptual difference between Executor and Builder?

    What is the conceptual difference between Executor and Builder?

    A Builder can be used to compose a bunch of Systems which are all Schedulable. It seems that (I may be wrong on this one because it is not yet explicitly documented as far as I can tell) that Systems can be grouped between flush calls and that Systems within such a group are executed in parallel (by default) and the groups themselves are executed sequentially.

    Have I got that right?

    An Executor is something that "Executes a sequence of systems, potentially in parallel, and then commits their command buffers."

    These seem to fulfill the same purpose to me. Is one being deprecated or do they have different use cases?

    Also, I am happy to take a stab at expanding the readme.md and/or the hello_world example with something that clarifies this, once I understand it.

    opened by travis-leith 9
  • Make `Entity` (de)serializable

    Make `Entity` (de)serializable

    Hi,

    For a networking game I'd like to use Entity as an identification for players (a player client would send to the server an Entity along other data).
    If this is sound, would it be possible to make Entity (de)serializable to be able to send it in network packets ?

    opened by bestouff 8
  • Cannot access additional components while iterating query

    Cannot access additional components while iterating query

    Before 3ee0bb9b75 change the following code (edited for brevity) used to work:

        SystemBuilder::new("monster_ai")
            .with_query(<(Write<Viewshed>, Write<Position>)>::query().filter(tag::<Monster>()))
            .write_component::<Confusion>()
            .build(
                |command_buffer, world, _, query| {
                    for (entity, (mut viewshed, mut pos)) in query.iter_entities_mut(world) {
                        let mut can_act = true;
    
                        if let Some(mut confused) = world.get_component_mut::<Confusion>(entity) {
                            confused.turns -= 1;
                            can_act = false;
                        }
    
                        if can_act {
    

    After 3ee0bb9b75 it fails to compile:

    error[E0499]: cannot borrow `*world` as mutable more than once at a time
      --> src/monster_ai_system.rs:28:49
       |
    25 |                 for (entity, (mut viewshed, mut pos)) in query.iter_entities_mut(world) {
       |                                                          ------------------------------
       |                                                          |                       |
       |                                                          |                       first mutable borrow occurs here
       |                                                          first borrow later used here
    ...
    28 |                     if let Some(mut confused) = world.get_component_mut::<Confusion>(entity) {
       |                                                 ^^^^^ second mutable borrow occurs here
    

    Is this access pattern not supported by Legion?

    If so, what is the designed use of read/write_component() function? Any tips on changing my code?

    opened by smokku 7
  • allow `system` macro to use a local module for legion behind a feature instead of the crate

    allow `system` macro to use a local module for legion behind a feature instead of the crate

    fixes #178

    This is due to all paths to legion in the macro starting with :: which means that legion has to be a crate and not a module.

    I found this issue when playing with Amethyst where the system macro doesn't work with the following error:

    error[E0432]: unresolved import `legion`
      --> examples/pong_tutorial_03/systems/paddle.rs:34:1
       |
    34 | #[system]
       | ^^^^^^^^^ help: a similar path exists: `amethyst_core::legion`
       |
       = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
    
    error[E0433]: failed to resolve: could not find `legion` in `{{root}}`
      --> examples/pong_tutorial_03/systems/paddle.rs:34:1
       |
    34 | #[system]
       | ^^^^^^^^^ could not find `legion` in `{{root}}`
       |
       = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
    
    error[E0433]: failed to resolve: could not find `legion` in `{{root}}`
      --> examples/pong_tutorial_03/systems/paddle.rs:34:1
       |
    34 | #[system]
       | ^^^^^^^^^ not found in `legion::systems`
       |
       = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
    help: consider importing one of these items
       |
    1  | use amethyst::ecs::SystemBuilder;
       |
    1  | use amethyst_core::ecs::SystemBuilder;
       |
    1  | use crate::SystemBuilder;
       |
    

    If I add legion as a direct dependency, it works, but it's not ideal because it means everyone using amethyst with legion would always need to keep both in sync.

    I added a feature reexport that, when enabled, will switch the path prefix from :: to self::, which means I can now use a re-exported legion module

    opened by mockersf 7
  • Adding a component trigger `changed` filter for other components

    Adding a component trigger `changed` filter for other components

    Here is a simple example:

    use legion::prelude::*;
    
    #[derive(Debug)]
    struct Foo(pub i32);
    
    #[derive(Debug)]
    struct Bar(pub i32);
    
    fn main() {
        let universe = Universe::new();
        let mut world = universe.create_world();
    
        let foo_system = SystemBuilder::new("test")
            .with_query(<Read<Foo>>::query().filter(changed::<Foo>()))
            .build(|_, world, _, query| {
                for foo in query.iter(world) {
                    println!("{:?} changed", foo);
                }
            });
    
        let mut schedule = Schedule::builder()
            .add_system(foo_system)
            .build();
        
        let entity = world.insert((), vec![(Foo(1),)])[0];
    
        println!("pass #1");
        schedule.execute(&mut world);
    
        // Adding Bar component will mark entity as if Foo has changed
        world.add_component::<Bar>(entity, Bar(0));
    
        println!("pass #2");
        schedule.execute(&mut world);
    
        println!("pass #3");
        schedule.execute(&mut world);
    }
    

    output

    pass #1
    Ref { borrow: Shared { state: 2 }, value: Foo(1) } changed
    pass #2
    Ref { borrow: Shared { state: 2 }, value: Foo(1) } changed
    pass #3
    

    It's not clear to me if it is the intended behavior, but typing .filter(changed::<Foo>()) strongly suggest it will pull only entities with modified Foo components.

    Tested against 0.2.1 and master.

    opened by IcanDivideBy0 7
  • How to apply Filter in #[system] with Query<(Entity, &Component)>

    How to apply Filter in #[system] with Query<(Entity, &Component)>

    I know that I can do .filter on a query to add filter arguments, and I know that in a #[system(for_each)] something like #[filter(maybe_changed::<Position>())] can be added as described in system proc macro doc

    My question is how to do it in this specific configuration:

    #[system]
    fn beam_array_damage_update(
    	targets: &mut Query<(Entity, &Hull, &mut Shield)>, 
    	sender: &mut Query<(&BeamArray, &TargetingConsole)>,
    	world: &mut SubWorld,
    	#[resource] dt: &DeltaTime
    ) {
    	...
    }
    

    where parameter targets should have the filter attached to. I know that Query has a second generic argument F which should probably be used, but how? Thanks in advance

    opened by nyvs 3
  • Make `KnownLength` public

    Make `KnownLength` public

    I would like to create an internal method that wraps legion, but since many of the bounds used internally (such as KnownLength, Aos, etc.) are private, most things I am trying to do, such as creating a bound for the types I can pass into CommandBuffer::push, are not possible.

    opened by SOF3 0
  • Include license file in legion_codegen crate

    Include license file in legion_codegen crate

    That would help with fedora packaging.

    opened by remilauzier 0
  • Update dependency and fix some warnings

    Update dependency and fix some warnings

    Description

    Motivation and Context

    How Has This Been Tested?

    Checklist:

    • [x] Acknowledged that by making this pull request I release this code under an MIT/Apache 2.0 dual licensing scheme.
    • [x] My code follows the code style of this project.
    • [ ] If my change required a change to the documentation I have updated the documentation accordingly.
    • [ ] I have updated the content of the book if this PR would make the book outdated.
    • [ ] I have added tests to cover my changes.
    • [ ] My code is used in an example.
    opened by remilauzier 0
  • Failed World::clone_from(), because of missing Duplicate::register_clone(), asserts

    Failed World::clone_from(), because of missing Duplicate::register_clone(), asserts

    This code tries to implement the request from #269 – to deserialize into an existing World and to find out what exactly we have deserialized.

    I didn't realize one needs Duplicate::register_clone() or Duplicate::register_copy() in order to copy/clone anything between Worlds.

    As a result, the following crashes:

    use std::{fs, fmt, io};
    use std::ops::Range;
    use std::path::Path;
    use std::error::Error;
    use std::fmt::{Display, Formatter};
    
    use serde::de::DeserializeSeed;
    
    use legion::{Entity, World};
    use legion::serialize::Canon;
    use legion::world::{Merger, Duplicate, Allocate};
    
    use crate::item::{Item, Headwear};
    use crate::components::Render;
    use crate::prelude::storage::{Archetype, Components, ArchetypeWriter};
    
    
    impl Registry {
        
        pub fn new() -> Self {
            let mut result = Self { registry: legion::Registry::<String>::default() };
    
            result.registry.register::<Render>("render".to_string());
            result.registry.register::<Item>("item".to_string());
            result.registry.register::<Headwear>("headwear".to_string());
    
            result
        }
        
        fn deser(&self, item_name: &str) -> Result<World, PrefabError> {
            let path = Path::new("data/items").join(item_name).with_extension("json");
            let json = fs::read_to_string(path)?;
            let json_val: serde_json::Value = serde_json::from_str(&json)?;
    
            let entity_serializer = Canon::default();
    
            let w = self.registry
                .as_deserialize(&entity_serializer)
                .deserialize(json_val)?;
    
            Ok(w)
        }
    
        pub fn load(&self, item_name: &str, world: &mut World) -> Result<Vec<Entity>, PrefabError> {
            struct PrefabMerger {
                pub dup: Duplicate,
                pub entities: Vec<Entity>,
            }
            
            impl Merger for PrefabMerger {
                fn assign_id(&mut self, existing: Entity, allocator: &mut Allocate) -> Entity {
                    let id = self.dup.assign_id(existing, allocator);
                    self.entities.push(id);
                    id
                }
    
                fn merge_archetype(&mut self, src_entity_range: Range<usize>, src_arch: &Archetype, src_components: &Components, dst: &mut ArchetypeWriter) {
                    self.dup.merge_archetype(src_entity_range, src_arch, src_components, dst)
                }
            }
            
            impl PrefabMerger {
                pub fn new() -> Self { 
                    Self { dup: Duplicate::default(), entities: vec![] } 
                }
            }
    
            let mut mirage_world = self.deser(item_name)?;
            let mut merger = PrefabMerger::new();
            world.clone_from(&mut mirage_world, &legion::any(), &mut merger);
    
            Ok(merger.entities)
        }
    }
    
    
    #[cfg(test)]
    mod tests {    
        #[test] 
        fn cask() -> Result<(), PrefabError> {
            let mut world = World::default();
            let reg = Registry::new();
            let entities = reg.load("one_cask", &mut world)?;
            assert_eq!(1, entities.len());
            
            Ok(())
        }
    }
    

    It fails with the following stack:

       3: core::panicking::assert_failed
                 at /Users/vic/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/panicking.rs:143:5
       4: <legion::internals::insert::ArchetypeWriter as core::ops::drop::Drop>::drop
                 at /Users/vic/.cargo/registry/src/github.com-1ecc6299db9ec823/legion-0.4.0/src/internals/insert.rs:113:9
       5: core::ptr::drop_in_place<legion::internals::insert::ArchetypeWriter>
                 at /Users/vic/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:192:1
       6: legion::internals::world::World::clone_from
                 at /Users/vic/.cargo/registry/src/github.com-1ecc6299db9ec823/legion-0.4.0/src/internals/world.rs:691:9
       7: rifrl::item::prefabs::Registry::load
                 at ./src/item/prefabs.rs:103:9
       8: rifrl::item::prefabs::tests::cask
                 at ./src/item/prefabs.rs:183:24
    

    in impl Drop for ArchetypeWriter::drop():

            assert_eq!(
                self.claimed.count_ones() as usize,
                self.archetype.layout().component_types().len()
            );
    

    I guess this should not crash, but instead just clone nothing. Or, maybe, it should, but with a more clear error message.

    opened by singalen 0
  • Should maybe_changed always match at least once?

    Should maybe_changed always match at least once?

    I have a system that is using #[filter(maybe_changed::<Foo>())] to update lazily. However, I'd like to run this same function once to set the initial state correctly. Is there a filter I can combine to always execute on first encountering a given component, or a way to set the dirty flag on the component during initialisation?

    opened by swiftcoder 0
  • as_deserialize_into_world(): provide a way to read a deserialized entities' IDs

    as_deserialize_into_world(): provide a way to read a deserialized entities' IDs

    I'm implementing prefabs, and I feel like as_deserialize() would result in at least one excess copy of the new instance. as_deserialize_into_world() looks just right, but currently I see no way to know - which exactly Entities were deserialized? It would be nice to have a callback or to return a collection of ids.

    opened by singalen 1
  • Trying to deserialize from JSON file

    Trying to deserialize from JSON file

    Hi! I'm trying to follow the rust rogueliketoolkit tutorial and trying to convert it to legion.

    I'm trying to deserialize a savegame.json file, but I'm getting errors. The deserializer isn't well documented, but I've tried my best:

    pub fn load_game(world: &mut World, resources: &mut Resources) {
        /*{
            let mut cmd = CommandBuffer::new(world);
            let mut to_delete = Vec::new();
            for e in <Entity>::query().iter_mut(world) {
                to_delete.push(*e);
            }
            for del in to_delete.iter() {
                cmd.remove(*del);
            }
            cmd.flush(world, resources);
         } */
    
        let mut registry = Registry::<String>::default();
    
        register_components!(registry, BlocksTile, CombatStats, Player, Position, Monster, Name, Item, Renderable, SufferDamage, Viewshed, WantsToMelee, InBackpack, WantsToPickupItem, WantsToUseItem, WantsToDropItem, Consumable, ProvidesHealing, Ranged, InflictsDamage, AreaOfEffect, Confusion, SerializationHelper);
    
    
        let data = fs::read_to_string("./savegame.json").unwrap();
        let entity_serializer = Canon::default();
        let new_world: World = registry.as_deserialize(&entity_serializer).deserialize(data).expect("Unable to deserialize world");
        //let new_world: World = registry.as_deserialize(&entity_serializer).deserialize(data).unwrap();
    
        *world = new_world;
    
        let mut deleteme: Option<Entity> = None;
        {
            for (entity, helper) in <(Entity, &SerializationHelper)>::query().iter(world) {
                resources.insert(helper.map.clone());
                let mut map = resources.get_mut::<Map>().unwrap();
                map.tile_content = vec![Vec::new(); super::map::MAP_COUNT];
                deleteme = Some(*entity);
            }
    
            for (entity, _player, position) in <(Entity, &Player, &Position)>::query().iter(world) {
                resources.insert(rltk::Point::new(position.x, position.y));
                resources.insert(PlayerEntity { entity: *entity })
            }
        }
        let mut cmd = CommandBuffer::new(world);
        cmd.remove(deleteme.unwrap());
        cmd.flush(world, resources);
    }
    

    The register_components! macro is just a way to iterate over all my components and register them with a name of their lowercase struct name

    error[E0277]: the trait bound `std::string::String: components::_::_serde::Deserializer<'_>` is not satisfied
      --> src\saveload_system.rs:58:84
       |
    58 |     let new_world: World = registry.as_deserialize(&entity_serializer).deserialize(data).expect("Unable to deserialize world");
       |                                                                                    ^^^^ the trait `components::_::_serde::Deserializer<'_>` is not implemented for `std::string::String`
    

    sorry for such a basic question, but I'm really having problems and I don't know how to fix it.

    opened by dyedquartz 3
  • Re-export types from `atomic_refcell`

    Re-export types from `atomic_refcell`

    Some types from atomic_refcell are exposed through legion, (eg., though Resources::get). Since legion doesn't re-export these, in order to build on them, you have to manually add atomic_refcell as a dependency and make sure its version is compatible.

    opened by skysch 0
  • get state of executed system.

    get state of executed system.

    It is possible to get the state of an executed system back out? Something like this?

    #[system]
    fn example(#[state] s: &mut u32) {}
    
    fn test(){
        let sys = example_system(0);
        let mut schedule = Schedule::builder()
            .add_system(&mut sys)
            .build();
       schedule.execute(world, resources);
       let s = sys.states().s();
    }
    

    Specifics can change, it is about the general concept.

    opened by N4tus 0
Releases(v0.4.0)
  • v0.4.0(Feb 25, 2021)

    What's Changed

    πŸš€ Features and Improvements

    • [breaking] Some rearranging of serialize/deserialize code (#230) @Rua
    • [breaking] Extract canon from registry (#221) @alyjak
    • [breaking] Add Resources to CommandBuffer::exec_mut and ::flush (#214) @Rua
    • Allow Queries to be requested as parameters in #[system] (#237) @TomGillen
    • Use NonZeroU64 to store entity IDs (#238) @Rua
    • Removing derivative from dependencies (#232) @lain-dono
    • Fetching a nonexistent resource prints the name of a type (#231) @LokiVKlokeNaAndoke
    • Implement Debug for Schedule, Executor and other subtypes (#213) @Rua
    • adds EntityFilterTuple to the public API (#222) @alyjak
    • Adds CustomEntitySerializer to the public API (#220) @alyjak
    • Re-export FetchMut from systems (#209) @Veykril
    • A few minor optimizations to LocationMap (#206) @mjhostet
    • Better error message when fetching nonexistent resources (#210) @Rua
    • Show appropriate error position on invalid macro usage (#208) @tyfkda

    πŸ› Bug Fixes

    • Fix parallel chunk iterator using entire index instead of split slice (#240) @TomGillen
    • Fix broken links in docs & readme.md (#236) @FluffyCreature
    • allow read in both subworlds within split in system (#235) @ezpuzz
    • Use a drop guard to prevent leaving a dangling reference in internals::serialize::id::run_as_context (#203) @autumnontape

    🧰 Maintenance

    • synchronize CI functions with Amethyst (#233) @ezpuzz
    • Use write_u64 to special-case hashing 64 bit values (#204) @mjhostet
    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Sep 28, 2020)

    Fixes:

    • QueryResult::split_at splitting at the concorrect indexes. This fixes a critical bug which would cause the incorrect components to be returned from parallel iterators, potentially resulting in mutable aliases.
    • SystemBuilder requiring Send be implemented for its resource tuple type.
    • Various broken documentation links.
    Source code(tar.gz)
    Source code(zip)
Owner
Amethyst Engine
Data-oriented game engine written in Rust
Amethyst Engine
High performance Rust ECS library

Legion aims to be a feature rich high performance Entity component system (ECS) library for Rust game projects with minimal boilerplate. Getting Start

Amethyst Engine 1.2k Sep 16, 2021
A quick and dirty Space Invaders type game in Bevy, with attached tutorial.

This article is in-development and will be released in full form soon. It'll appear on Medium (my publisher likes that), with this as a the accompanyi

Herbert 11 Aug 21, 2021
A curated list of wgpu code and resources.

Awesome wgpu A curated list of wgpu code and resources. PRs welcome. About wgpu https://github.com/gfx-rs/wgpu-rs matrix chat https://matrix.to/#/#wgp

Roman FroΕ‚ow 188 Sep 10, 2021
A tilemap rendering crate for bevy which is more ECS friendly.

bevy_ecs_tilemap A tilemap rendering plugin for bevy which is more ECS friendly by having an entity per tile. Features A tile per entity Fast renderin

John 87 Sep 12, 2021
Rust Game engine 3D (and 2D)

A feature-rich, production-ready, general purpose 2D/3D game engine written in Rust with a scene editor.

rg3d engine 2.7k Sep 13, 2021
A feature-rich, production-ready, general purpose 2D/3D game engine written in Rust with a scene editor.

A feature-rich, production-ready, general purpose 2D/3D game engine written in Rust with a scene editor.

rg3d engine 2.8k Sep 16, 2021
RTS game/engine in Rust and WebGPU

What is this? A real time strategy game/engine written with Rust and WebGPU. Eventually it will be able to run in a web browser thanks to WebGPU. This

Thomas SIMON 227 Sep 6, 2021
SDL2 bindings for Rust

Rust-SDL2 Bindings for SDL2 in Rust Changelog for 0.34.2 Overview Rust-SDL2 is a library for talking to the new SDL2.0 libraries from Rust. Low-level

null 1.7k Sep 13, 2021
Rust bindings for libtcod 1.6.3 (the Doryen library/roguelike toolkit)

Warning: Not Maintained This project is no longer actively developed or maintained. Please accept our apologies. Open pull requests may still get merg

Tomas Sedovic 222 Sep 3, 2021
πŸ‚ A Rust-based simulated DOM (browser-independent replacement for web_sys)

DOM in Rust without a browser Hello and welcome. This library provides server-side or browserless simulation of a DOM. Example Usage use std::sync::Ar

null 37 Sep 13, 2021
A CLI tool to manage your godot-rust projects

ftw A CLI tool to manage your godot-rust project! Table of contents General Information Setup Usage Contact General Information This is a tool to help

Michael Angelo Calimlim 50 Sep 1, 2021
Rust library to create a Good Game Easily

ggez What is this? ggez is a Rust library to create a Good Game Easily. The current version is 0.6.0-rc0. This is a RELEASE CANDIDATE version, which m

null 3k Sep 8, 2021
Sci-fi run 'n' gun action RPG created in Rust, using Macroquad

Capstone - Twilight of the Archons This is an action/run 'n' gun RPG, created in Rust using macroquad. The future holds huge boss fights, and a mind-n

Ole A. Sjo Fasting 35 Sep 19, 2021
Unofficial game launcher for UnityStation

RSHub Unofficial game launcher for UnityStation. RSHub is a terminal application written in Rust for listing and connecting to UnityStation game serve

Eugene 3 Sep 12, 2021
Tiny cross-platform webview library for C/C++/Golang. Uses WebKit (Gtk/Cocoa) and Edge (Windows)

webview A tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. Also, there are Rust bindings, Python bindings, Ni

webview 8.9k Sep 12, 2021
A modern 3D/2D game engine that uses wgpu.

Harmony A modern 3D/2D game engine that uses wgpu and is designed to work out of the box with minimal effort. It uses legion for handling game/renderi

John 147 Aug 20, 2021
A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇.

Hebi ?? A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇(へび). Configuration One of the

Elnu 56 Sep 9, 2021
Walleye is a chess engine written completely in rust.

Walleye is a UCI-compatible engine written using the classical alpha-beta style AI. It supports loading board positions from arbitrary FEN strings, Unicode pretty printing to the console, and UCI communication logs to help with debugging.

Mitchel Paulin 61 Sep 13, 2021
An egui backend for godot-rust

Godot Egui An egui backend for godot-rust. Rationale Godot has a perfectly valid GUI system, so why egui? Here are my personal reasons: Simplicity: No

null 55 Sep 15, 2021