DCES Examples - Shared

This subsection documents the example application dces_shared.

In a minimalistic use-case, this example creates a single world. It creates two entities. Each entity gets assinged components that provide the properties “name”, “depth” and “size”. When using the ComponentBuilder for the second entity (component “name” == “CheckBox”) we make use of a method “with_shared()”, that will reference to an already given struct “size”.

A size system will query the entity store and manipulate the width and height struct values of components that provide and match a key “size”. As a rudimentary use case, we do simply increment them by one.

A print system will queries into the entity store “e_store”, collects given entities and prints out the “value” of any component that has a matching property key “name”. Without any fancy magic, entities name, its width and its height are printed to stdout. and assignes three component that provides the property “name”, “depth” and “size”.

Update Cargo.toml

First have a look at the corresponding Cargo.toml file as shown in Listing 1-1.

# ANCHOR: All
[package]
# ANCHOR: Name
name = "dces_shared"
# ANCHOR_END: Name
version = "0.4.0"
authors = [
	"Florian Blasius <flovanpt@posteo.de>",
	"Ralf Zerres <ralf.zerres.de@gmail.com>",
]
description = "DCES - Shared example"
documentation = "https://doc.redox-os.org/dces-guide"
repository = "https://gitlab.redox-os.org/redox-os/dces-guide/"
readme = "README.md"
license = "MIT"
keywords = [
	"dces",
	"ecs",
]
edition = "2021"

[profile.dev]
opt-level = 1

[dependencies]
dces = { git = "https://gitlab.redox-os.org/redox-os/dces-rust.git", branch = "develop" }

[[bin]]
# ANCHOR: Name
name = "dces_shared"
# ANCHOR_END: Name
path = "src/main.rs"
# ANCHOR_END: All

Listing 1-1: Projects metadata “dces_shared”

Program source code

All of the DCES specific code that is needed to build the dces_shared example is shown in Listing 1-2, that is encode in src/main.rs.

Enter the following commands inside the terminal window to compile and run in debug mode:

$ cargo run --example shared

The following output should be printed inside your console window:

entity: 0; name: Button; width: 6; height: 6
entity: 1; name: CheckBox; width: 6; height: 6

Recap and annotation

The anatomy of the dces_shared application

Let’s review the relevant parts of the dces_shared application. A more in depth view of a typical DCES application is documented inside the annotated dces_basic source.

Following code block extracts the implementation of the PrintSystem:

impl System<EntityStore> for PrintSystem {
    fn run(&self, ecm: &mut EntityComponentManager<EntityStore>, _: &mut Resources) {
        let (e_store, c_store) = ecm.stores();

        for entity in &e_store.inner {
            if let Ok(name) = c_store.get::<Name>("name", *entity) {
                if let Ok(size) = c_store.get::<Size>("size", *entity) {
                    println!(
                        "entity: {}; name: {}; width: {}; height: {}",
                        entity.0, name.0, size.width, size.height
                    );
                }
            }
        }
    }
}

The values of the component keys "name", "width" and "height" will be processed as already documented.

The new part is inside the handling of the Size-Structure to a component. Inside the main function, when we do attach the component “size” with ComponentBuilder inside the second entity, we make use of the with_shared method. This method is able to borrow the already assigned structure “size”. Therefore it will reference the given values.

                .with_shared::<Size>("size", source)

Complete example source

Find attached the complete source code for our dces_shared example.

use dces::prelude::*;

#[derive(Default)]
struct Size {
    width: u32,
    height: u32,
}

#[derive(Default)]
struct Name(String);

#[derive(Default)]
struct Depth(u32);

pub struct SizeSystem {
    source: Entity,
}

impl System<EntityStore> for SizeSystem {
    fn run(&self, ecm: &mut EntityComponentManager<EntityStore>, _: &mut Resources) {
        if let Ok(comp) = ecm
            .component_store_mut()
            .get_mut::<Size>("size", self.source)
        {
            comp.width += 1;
            comp.height += 1;
        }
    }
}

struct PrintSystem;

impl System<EntityStore> for PrintSystem {
    fn run(&self, ecm: &mut EntityComponentManager<EntityStore>, _: &mut Resources) {
        let (e_store, c_store) = ecm.stores();

        for entity in &e_store.inner {
            if let Ok(name) = c_store.get::<Name>("name", *entity) {
                if let Ok(size) = c_store.get::<Size>("size", *entity) {
                    println!(
                        "entity: {}; name: {}; width: {}; height: {}",
                        entity.0, name.0, size.width, size.height
                    );
                }
            }
        }
    }
}

fn main() {
    let mut world = World::from_entity_store(EntityStore::default());

    let source = world
        .create_entity()
        .components(
            ComponentBuilder::new()
                .with("name", Name(String::from("Button")))
                .with("depth", Depth(4))
                .with(
                    "size",
                    Size {
                        width: 5,
                        height: 5,
                    },
                )
                .build(),
        )
        .build();

    world
        .create_entity()
        .components(
            ComponentBuilder::new()
                .with("name", Name(String::from("CheckBox")))
                .with("depth", Depth(1))
                .with_shared::<Size>("size", source)
                .build(),
        )
        .build();

    world.create_system(PrintSystem).with_priority(1).build();

    world
        .create_system(SizeSystem { source })
        .with_priority(0)
        .build();

    world.run();
}

Listing 1-2: dces_shared - Create a World, its entities and a shared component.

Compiling and Running Are Separate Steps

The compiled dces_shared with cargo will be placed the resulting binary in the target subfolder of the project.

$ cargo build --release --bin dces_shared.rs
$ ../target/release/dces_shared

On Windows, you need to use backslash as a path delimiter:

> cargo build --release --bin dces_shared.rs
> ..\target\release\dces_shared.exe