|
libecs-cpp 2.1.0
Entity Component System library in C++
|
A container: the entities, components, systems and resources that are updated together. More...
#include <Container.hpp>

Public Member Functions | |
| Container (ecs::Manager *manager) | |
| Creates a container with a generated handle. | |
| Container (ecs::Manager *manager, const std::string &handle) | |
| Creates a container with the given handle. | |
| ~Container () | |
| Stops the container as Stop() does, then releases it. | |
| void | Start () |
| Starts the container's own thread, which runs 30 passes a second. | |
| void | Start (std::chrono::microseconds interval) |
| Starts the container's own thread with the given time between passes. | |
| void | Start (uint32_t) |
| Starts the container's own thread with the interval given as a number of microseconds. | |
| void | ClockSet (const ecs::Clock *clock) |
| Replaces the clock that the systems of this container read. | |
| void | Stop () |
| Stops the container and calls System::Shutdown() on every started system, last registered first. | |
| void | SystemsInitialize () |
| Calls System::Initialize() on every registered system that has not been started, in registration order. | |
| ecs::System * | System (std::unique_ptr< ecs::System > system) |
| Registers a system and returns a pointer to it. | |
| std::shared_ptr< ecs::Component > | Component (std::unique_ptr< ecs::Component > component) |
| Attaches a component to the entity named by its EntityHandle and returns the stored component. | |
| template<class T = ecs::Component> | |
| std::shared_ptr< T > | ComponentGet (const std::string &entity, const std::string &type) const |
| Looks up the component with the given type name on the given entity, as class T. | |
| bool | ComponentHas (const std::string &entity, const std::string &type) const |
| Says whether the given entity has a component stored under the given type name. | |
| void | ComponentDestroy (const std::string &entity, const std::string &type) |
| Removes the component with the given type name from the given entity. | |
| ecs::Entity * | Entity (const std::string &handle) |
| Returns the entity with this handle, creating it if it does not exist. | |
| ecs::Entity * | Entity () |
| Creates an entity with a generated handle. | |
| void | EntityDestroy (const std::string &handle) |
| Removes an entity and its components. | |
| void | SystemDestroy (const std::string &handle) |
| Removes a system. | |
| nlohmann::json | Export () const |
| Describes the container, its entities and its systems as JSON. | |
| void | Update () |
| Runs one pass: the deferred functions, then the start of new systems, then one update of each system. | |
| void | MessageSubmit (const nlohmann::json &message) |
| Sends a message to the system named in message["destination"]["system"]. | |
| void | Defer (std::function< void()> fn) |
| Hands a function to the container thread, which runs it at the start of the next pass. | |
| void | ResourceAdd (const std::string &name, ecs::Resource r) |
| Stores a resource under a name, replacing any resource of that name. | |
| void | Resources (const std::unordered_map< std::string, std::shared_ptr< ecs::Resource > > &resources) |
| Stores every resource in the given map under its name, replacing resources of the same names. | |
| std::shared_ptr< const ecs::Resource > | ResourceGet (const std::string &name) const |
| Returns the resource stored under a name as a shared read-only view, without copying its bytes. | |
| ecs::Uuid | UuidGet () |
| Generates a new identifier. | |
| void | Log (const std::string &message, const std::string &level="info") |
| Sends a line to the log destination. | |
| void | LoggerSet (std::function< void(const std::string &, const std::string &)> fn) |
| Replaces the log destination. | |
Public Attributes | |
| std::unordered_map< std::string, std::unique_ptr< ecs::Entity > > | Entities |
| The entities of the container, by handle. | |
| ecs::Manager * | Manager = nullptr |
| The manager that owns the container. | |
| const std::string | Handle |
| The container's handle: the name given to Manager::Container(), or a generated UUID. | |
| ecs::TypeEntityComponentList | Components |
| The components of the container, by type name and then by entity handle. | |
| std::unordered_map< std::string, std::unique_ptr< ecs::System > > | Systems |
| The systems of the container, by handle. | |
A container: the entities, components, systems and resources that are updated together.
A container is driven in one of two ways. After Start() it has its own thread, which runs a pass (Update()) at a fixed interval. Without Start(), the application calls Update() itself. The thread that runs the passes is the container thread. Any other thread is an outside caller.
The complete rules, with a table of every member, are in the Threading section of REFERENCE.md.
| ecs::Container::Container | ( | ecs::Manager * | manager | ) |
Creates a container with a generated handle.
Applications normally call Manager::Container() instead, which creates the container and owns it.
| ecs::Container::Container | ( | ecs::Manager * | manager, |
| const std::string & | handle | ||
| ) |
Creates a container with the given handle.
Applications normally call Manager::Container(handle) instead, which creates the container and owns it.
| ecs::Container::~Container | ( | ) |
| void ecs::Container::ClockSet | ( | const ecs::Clock * | clock | ) |
Replaces the clock that the systems of this container read.
The new clock is given to every system in the container and to every system registered later. Each system starts its schedule, timers and elapsed-time measurement again from the new clock (see System::ClockSet()). The pointer is not owned and the clock must outlive the container. A null pointer selects the real steady clock, the default. Meant for containers driven by calls to Update(), for example with an ecs::ManualClock in a test.
Thread: container thread only.
| std::shared_ptr< ecs::Component > ecs::Container::Component | ( | std::unique_ptr< ecs::Component > | component | ) |
Attaches a component to the entity named by its EntityHandle and returns the stored component.
The container becomes the sole owner at the call: the caller's pointer is empty afterwards, whether the call is accepted or rejected, and a rejected component is released exactly once. Pass std::make_unique<T>(...) or a std::move()d std::unique_ptr; raw pointers and shared_ptrs do not compile.
If the entity already has a component of the same Type, the new component replaces it, so an entity has exactly one component of each type. Anyone holding a shared_ptr to the replaced component keeps a valid object. The same type on a different entity is kept alongside.
Throws std::runtime_error, leaving the container unchanged, for a null pointer, an empty Type, an empty EntityHandle, and an EntityHandle that names no entity in this container.
Thread: container thread only.
| void ecs::Container::ComponentDestroy | ( | const std::string & | entity, |
| const std::string & | type | ||
| ) |
Removes the component with the given type name from the given entity.
The arguments may be fields of the component being removed. An unknown entity or type name is a silent no-op.
Thread: container thread only.
|
inline |
Looks up the component with the given type name on the given entity, as class T.
The result is empty when the entity has no component with that type name, when the type name has never been used, when the entity is unknown, when either string is empty, and when the stored component is not a T. With the default T, ecs::Component, any stored component is returned. The result keeps its object valid even if the component is later replaced or removed.
The call never throws, never changes the container and takes no lock. Unlike Components[type][entity], which inserts an empty entry for a type or entity it does not find, it leaves the table as it was. Given existing std::string arguments it does not allocate. A string literal longer than 15 characters allocates when it is converted to std::string, so code that runs every pass should hold its type names in std::string constants.
Thread: container thread only.
| bool ecs::Container::ComponentHas | ( | const std::string & | entity, |
| const std::string & | type | ||
| ) | const |
Says whether the given entity has a component stored under the given type name.
Only the name is compared, so the component's class does not matter. An empty slot left by Components[type][entity] counts as no component. The call never throws, never changes the container, takes no lock and does not allocate given existing std::string arguments.
Thread: container thread only.
| void ecs::Container::Defer | ( | std::function< void()> | fn | ) |
Hands a function to the container thread, which runs it at the start of the next pass.
The function runs once, before any system is updated and never in the middle of a pass. Functions run in the order the calls were accepted. A function submitted from a deferred function, or during a pass, runs in the next pass. A container that is never updated never runs them. Functions still pending when the container is stopped or destroyed are discarded without running, and a call made after that is dropped.
If a function throws, the error is logged at level "error", the rest of the batch still runs, and the first exception is rethrown by Update() before any system is updated. On the container's own thread it is caught at the thread boundary, as for a system's error.
The function and everything it captures must stay valid until it runs or is discarded.
Thread: any, including from inside the container.
| ecs::Entity * ecs::Container::Entity | ( | ) |
Creates an entity with a generated handle.
The pointer is valid until the entity is removed or the container is destroyed.
Thread: container thread only.
| ecs::Entity * ecs::Container::Entity | ( | const std::string & | handle | ) |
Returns the entity with this handle, creating it if it does not exist.
The pointer is valid until the entity is removed or the container is destroyed.
Thread: container thread only.
| void ecs::Container::EntityDestroy | ( | const std::string & | handle | ) |
Removes an entity and its components.
The handle may be the entity's own Handle. An unknown handle, including an empty one, is a silent no-op.
Thread: container thread only.
| nlohmann::json ecs::Container::Export | ( | ) | const |
Describes the container, its entities and its systems as JSON.
This is a const query: overrides of System::Export() must not add or remove systems, entities or components.
Thread: container thread only.
| void ecs::Container::Log | ( | const std::string & | message, |
| const std::string & | level = "info" |
||
| ) |
Sends a line to the log destination.
Each call goes to exactly one destination, one that was installed at some time during the call; a call that starts after LoggerSet() returned uses the new destination. The destination is called with no lock held, so it may itself call Log() or LoggerSet(). If the destination is empty the line is dropped. Lines a system logged before it was registered reach the destination when it is registered.
The default destination of a new container writes "[level] message": error and warning to standard error, every other level to standard output. A stream gets colour codes only when it is an interactive terminal and the NO_COLOR environment variable is unset or empty. Each stream is decided once, when the container is created, so redirecting it later does not change the decision. A destination set with LoggerSet() always receives the plain message and level.
Thread: any.
| void ecs::Container::LoggerSet | ( | std::function< void(const std::string &, const std::string &)> | fn | ) |
Replaces the log destination.
An empty function makes later Log() calls drop their lines. The previous destination may still finish a call that began before the replacement, so it must stay callable until then.
Thread: any, including from inside a destination.
| void ecs::Container::MessageSubmit | ( | const nlohmann::json & | message | ) |
Sends a message to the system named in message["destination"]["system"].
The message must be a JSON object with a "destination" object that holds a non-empty text "system". message["destination"]["container"] is not read: the message goes to this container whatever that field holds. A system can be addressed by handle once it has been registered with System().
Throws std::runtime_error, changing nothing and taking no lock, if the message breaks these rules; the text names the missing or wrong field and, for a wrong type, the type found. Also throws std::runtime_error if the system is unknown; the text names this container.
Thread: any.
| void ecs::Container::ResourceAdd | ( | const std::string & | name, |
| ecs::Resource | r | ||
| ) |
Stores a resource under a name, replacing any resource of that name.
Passing a temporary or a std::move()d value costs no copy of the bytes; passing an lvalue copies them once. Readers that still hold the replaced resource keep their data. Treat a resource as read-only once added.
Thread: container thread only.
| std::shared_ptr< const ecs::Resource > ecs::Container::ResourceGet | ( | const std::string & | name | ) | const |
Returns the resource stored under a name as a shared read-only view, without copying its bytes.
The result is empty when the name is unknown; nothing is created and nothing is thrown. The data stays valid for as long as the result is held, even after the resource is replaced or the container is destroyed.
Thread: container thread only.
| void ecs::Container::Resources | ( | const std::unordered_map< std::string, std::shared_ptr< ecs::Resource > > & | resources | ) |
Stores every resource in the given map under its name, replacing resources of the same names.
The stored resources share their bytes with the map's; nothing is copied.
Thread: container thread only.
| void ecs::Container::Start | ( | ) |
Starts the container's own thread, which runs 30 passes a second.
On the thread, systems are started (System::Initialize()) before the first pass, and again whenever a system has been registered since the last pass.
Start() takes effect once. A repeated call does nothing: it does not start a second thread, does not start any system a second time and does not change the interval. A container that has been stopped, or whose manager has shut down, cannot be started; the call logs one warning and returns. Make a new container instead.
A container that is never started is driven by the application's calls to Update(). The manager does not stop such a container; its owner stops it with Stop() or by destroying it.
Thread: call it once, from the thread that owns the container, before any other thread uses the container.
| void ecs::Container::Start | ( | std::chrono::microseconds | interval | ) |
Starts the container's own thread with the given time between passes.
Behaves as Start(). An interval of zero runs the passes back to back. An interval below zero or above ecs::MAX_INTERVAL throws std::runtime_error and leaves the container not started. The interval does not delay a stop: a stop request ends the wait at once. The thread waits on the real steady clock whatever clock is set with ClockSet().
| void ecs::Container::Start | ( | uint32_t | ) |
Starts the container's own thread with the interval given as a number of microseconds.
Behaves as Start(std::chrono::microseconds).
| void ecs::Container::Stop | ( | ) |
Stops the container and calls System::Shutdown() on every started system, last registered first.
Each started system receives Shutdown() once; a system that was never started does not receive it. Calling Stop() again does nothing. A stopped container cannot be restarted. After the call, Update() does nothing and a system registered later is never started.
When the call returns depends on the caller and on how the container is driven:
During the shutdown:
Thread: any.
| ecs::System * ecs::Container::System | ( | std::unique_ptr< ecs::System > | system | ) |
Registers a system and returns a pointer to it.
The system is in Systems when the call returns. It is started (System::Initialize()) once, on the container thread, at the start of the next pass and so before its first update. A system registered after the container has been stopped is never started and is released without Shutdown().
Thread: container thread only.
| void ecs::Container::SystemDestroy | ( | const std::string & | handle | ) |
Removes a system.
When the call returns the system is no longer part of the container: it is not in Systems, is not exported, cannot receive messages, and is not started or updated again, and its timers do not fire, even later in the same pass. Its handle may be registered again at once. The handle passed in may be the system's own Handle. An unknown handle, including an empty one, is a silent no-op, so removing a system twice releases it once.
If the system was started it receives Shutdown() once, just before it is released. An error thrown by Shutdown() is logged and swallowed. When that happens depends on where the call is made:
Thread: container thread only.
| void ecs::Container::SystemsInitialize | ( | ) |
Calls System::Initialize() on every registered system that has not been started, in registration order.
Update() does this by itself at the start of each pass, so calling it is optional, and calling it again never starts a system twice. It does nothing once the container has been stopped.
A system registered during the call is not started by it; it is started and updated from the next Update(). A system removed during the call before it was reached is not started. Systems removed during the call are released when it returns. If Initialize() throws, the error is logged with the system's handle and rethrown after every change requested so far has completed.
Thread: container thread only.
| void ecs::Container::Update | ( | ) |
Runs one pass: the deferred functions, then the start of new systems, then one update of each system.
After the container has been stopped or destroyed, Update() does nothing. On a container with its own thread it does nothing from the moment a stop is requested.
Thread: container thread only, one thread at a time.
| ecs::Uuid ecs::Container::UuidGet | ( | ) |
Generates a new identifier.
Thread: any.
| ecs::TypeEntityComponentList ecs::Container::Components |
The components of the container, by type name and then by entity handle.
Public so systems can iterate it. Reading Components[type][entity] inserts an empty entry when the type or entity is missing; ComponentGet() does not.
Thread: container thread only.
| std::unordered_map<std::string, std::unique_ptr<ecs::Entity> > ecs::Container::Entities |
The entities of the container, by handle.
Public so systems can iterate it; create and remove entities with Entity() and EntityDestroy().
Thread: container thread only.
| const std::string ecs::Container::Handle |
The container's handle: the name given to Manager::Container(), or a generated UUID.
Set at construction and never changes.
Thread: any.
| ecs::Manager* ecs::Container::Manager = nullptr |
The manager that owns the container.
Set at construction and never changes.
Thread: any.
| std::unordered_map<std::string, std::unique_ptr<ecs::System> > ecs::Container::Systems |
The systems of the container, by handle.
Public so systems can iterate it; register and remove systems with System() and SystemDestroy().
Thread: container thread only.