pub struct FlaggedStorage<C, T = DenseVecStorage<C>> { /* private fields */ }
Expand description

Wrapper storage that tracks modifications, insertions, and removals of components through an EventChannel.

Note: Joining over all components of a FlaggedStorage mutably will flag all components.

What you want to instead is to use restrict_mut() to first get the entities which contain the component and then conditionally modify the component after a call to get_mut_unchecked() or get_mut().

Examples

extern crate specs;

use specs::prelude::*;

pub struct Comp(u32);
impl Component for Comp {
    // `FlaggedStorage` acts as a wrapper around another storage.
    // You can put any store inside of here (e.g. HashMapStorage, VecStorage, etc.)
    //
    // It also works as `FlaggedStorage<Self>` and defaults to `DenseVecStorage<Self>`
    // for the inner storage.
    type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

pub struct CompSystem {
    // These keep track of where you left off in the event channel.
    reader_id: ReaderId<ComponentEvent>,

    // The bitsets you want to populate with modification/insertion events.
    modified: BitSet,
    inserted: BitSet,
}

impl<'a> System<'a> for CompSystem {
    type SystemData = (Entities<'a>, WriteStorage<'a, Comp>);
    fn run(&mut self, (entities, mut comps): Self::SystemData) {
        // We want to clear the bitset first so we don't have left over events
        // from the last frame.
        //
        // However, if you want to accumulate changes over a couple frames then you
        // can only clear it when necessary. (This might be useful if you have some
        // sort of "tick" system in your game and only want to do operations every
        // 1/4th of a second or something)
        //
        // It is not okay to only read the events in an interval though as that could
        // leave behind events which would end up growing the event ring buffer to
        // extreme sizes.
        self.modified.clear();
        self.inserted.clear();

        // Here we can populate the bitsets by iterating over the events.
        // You can also just iterate over the events without using a bitset which will
        // give you an ordered history of the events (which is good for caches and synchronizing
        // other storages, but this allows us to use them in joins with components.
        {
            let events = comps.channel()
                .read(&mut self.reader_id);
            for event in events {
                match event {
                    ComponentEvent::Modified(id) => { self.modified.add(*id); },
                    ComponentEvent::Inserted(id) => { self.inserted.add(*id); },
                    _ => { },
                };
            }
        }

        // Iterates over all components like normal.
        for comp in (&comps).join() {
            // ...
        }

        // **Never do this**
        // This will flag all components as modified regardless of whether the inner loop
        // actually modified the component.
        //
        // Only do this if you have other filters, like some other components to filter
        // out the ones you want to modify.
        for comp in (&mut comps).join() {
            // ...
        }

        // Instead you will want to restrict the amount of components iterated over, either through
        // other components in the join, or by using `RestrictedStorage` and only getting the component
        // mutably when you are sure you need to modify it.
        for (entity, mut comps) in (&entities, &mut comps.restrict_mut()).join() {
            if condition { // check whether this component should be modified.
                 let mut comp = comps.get_mut_unchecked();
                 // ...
            }
        }

        // To iterate over the modified components:
        for comp in (&comps, &self.modified).join() {
            // ...
        }

        // To iterate over all inserted/modified components;
        for comp in (&comps, &self.modified & &self.inserted).join() {
            // ...
        }
    }
}

fn main() {
    let mut world = World::new();
    world.register::<Comp>();

    // You will want to register the system `ReaderId`s
    // before adding/modifying/removing any entities and components.
    //
    // Otherwise you won't receive any of the modifications until
    // you start tracking them.
    let mut comp_system = {
        let mut comps = world.write_storage::<Comp>();
        CompSystem {
            reader_id: comps.register_reader(),
            modified: BitSet::new(),
            inserted: BitSet::new(),
        }
    };

    world.create_entity().with(Comp(19u32)).build();

    {
        let mut comps = world.write_storage::<Comp>();
        let events = comps.channel().read(&mut comp_system.reader_id);
        assert_eq!(events.len(), 1);
    }

    #[cfg(feature = "storage-event-control")]
    {
        world.write_storage::<Comp>().set_event_emission(false);
        world.create_entity().with(Comp(19u32)).build();

        {
            let mut comps = world.write_storage::<Comp>();
            let events = comps.channel().read(&mut comp_system.reader_id);
            assert_eq!(events.len(), 0);
        }

        world.write_storage::<Comp>().set_event_emission(true);
        world.create_entity().with(Comp(19u32)).build();

        {
            let mut comps = world.write_storage::<Comp>();
            let events = comps.channel().read(&mut comp_system.reader_id);
            assert_eq!(events.len(), 1);
        }
    }
}

Trait Implementations

Returns the “default value” for a type. Read more

Event channel tracking modified/inserted/removed components.

Mutable event channel tracking modified/inserted/removed components.

Clean the storage given a bitset with bits set for valid indices. Allows us to safely drop the storage. Read more

Tries reading the data associated with an Index. This is unsafe because the external set used to protect this storage is absent. Read more

Tries mutating the data associated with an Index. This is unsafe because the external set used to protect this storage is absent. Read more

Inserts new data for a given Index. Read more

Removes the data associated with an Index. Read more

Drops the data associated with an Index. This could be used when a more efficient implementation for it exists than remove when the data is no longer needed. Defaults to simply calling remove. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.

The type for initializers.

Initializes a with the given initializer. Read more

Dereferences the given pointer. Read more

Mutably dereferences the given pointer. Read more

Drops the object pointed to by the given pointer. Read more

Tries to create the default.

Calls try_default and panics on an error case.

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.