libecs-cpp 2.1.0
Entity Component System library in C++
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Protected Member Functions | Protected Attributes | List of all members
ecs::System Class Referenceabstract

Base class for the logic that runs in a container. More...

#include <System.hpp>

Collaboration diagram for ecs::System:
Collaboration graph
[legend]

Public Member Functions

 System ()
 Creates a system whose handle is a newly generated UUID.
 
 System (const std::string &handle)
 Creates a system with the given handle.
 
virtual ~System ()=default
 Destroys the system.
 
virtual void Initialize ()
 Called once before the system's first Update(); override it to set the system up.
 
virtual void Shutdown ()
 Called once before a started system is destroyed; override it to release what the system holds.
 
virtual void Configure (const nlohmann::json &config)
 Receives the system's configuration; override it to read settings.
 
virtual void Update ()
 Called on each pass in which the system is due; override it with the system's work.
 
void UpdateSystem ()
 Fires the timers that are due, then calls Update().
 
void MessageSubmit (const nlohmann::json &message)
 Delivers a message to this system.
 
virtual nlohmann::json Export () const =0
 Describes the system as JSON; every subclass implements it.
 
size_t MessagesWaiting ()
 Moves any delivered messages into messages, then returns how many messages are waiting to be read.
 
std::chrono::microseconds ElapsedGet () const
 Returns the time that passed between the previous update of this system and the current one.
 
double ElapsedSecondsGet () const
 Returns ElapsedGet() in seconds.
 
void ClockSet (const ecs::Clock *clock)
 Replaces the clock this system reads.
 
uint32_t DeltaTimeGet ()
 Returns the whole milliseconds of this update's elapsed time.
 
void TimerClear (const std::string &name)
 Cancels every timer with this name.
 
void TimerAdd (Timer timer)
 Adds a timer, which first fires one full length from now.
 
void Log (const std::string &message, const std::string &level="info")
 Sends a line to the container's log destination with this system's handle as a prefix.
 

Public Attributes

const std::string Handle
 The system's handle, given to the constructor and never changed.
 
ecs::Container * Container = nullptr
 The container the system is registered in, or null before registration.
 
ecs::TypeEntityComponentList * Components = nullptr
 The components of the system's container (Container::Components), or null before registration.
 
ecs::Timing Timing
 The schedule that decides on which passes the system is updated.
 

Protected Member Functions

void componentsClear ()
 Removes every component listed in componentsToDelete from the container and empties the list.
 

Protected Attributes

std::queue< nlohmann::json > messages
 Messages ready to read, oldest first.
 
std::chrono::steady_clock::time_point lastTime = std::chrono::steady_clock::now()
 The construction time.
 
std::unordered_map< std::string, std::vector< std::string > > componentsToDelete
 Components a subclass has marked for removal, as entity handles by type name.
 
std::vector< ecs::Timer > timers
 The system's timers.
 
std::vector< std::pair< std::string, std::string > > bufferedLogMessages
 Lines logged before the system was registered, as (message, level) pairs in the order they were logged.
 

Detailed Description

Base class for the logic that runs in a container.

A subclass overrides Update() with its work and Export() with a description of itself, and may override Initialize(), Shutdown() and Configure(). On each pass in which the system's Timing says an update is due, the container calls UpdateSystem(), which fires the timers that are due and then calls Update().

Threading
MessageSubmit() and Handle may be used from any thread. Every other member is for the container thread only (see Container). A system must be alive when MessageSubmit() is called on it directly: sending through Container::MessageSubmit() or Manager::MessageSubmit() is safe against the system being removed, a direct pointer to the system is not.
Messages
A message sent to the system is moved into messages at the start of the system's next UpdateSystem(), or earlier if MessagesWaiting() is called. Read the queue in Update():
while(!this->messages.empty())
{
    nlohmann::json message = this->messages.front();
    this->messages.pop();
    // use the message
}
The mailbox is unbounded. An application that needs back-pressure provides it.

Constructor & Destructor Documentation

◆ System() [1/2]

ecs::System::System ( )

Creates a system whose handle is a newly generated UUID.

◆ System() [2/2]

ecs::System::System ( const std::string &  handle)

Creates a system with the given handle.

The handle is fixed for the life of the object and cannot be assigned afterwards.

A subclass names itself in its constructor's initialiser list:

class Foo : public ecs::System
{
  public:
    Foo() : ecs::System("Foo") {}
};

A plugin factory that returns a new Foo reaches the same constructor. An empty handle is refused when the system is registered.

◆ ~System()

virtual ecs::System::~System ( )
virtualdefault

Destroys the system.

Container->Log() works from a subclass's destructor.

Member Function Documentation

◆ ClockSet()

void ecs::System::ClockSet ( const ecs::Clock *  clock)

Replaces the clock this system reads.

The schedule, the timers and the elapsed-time measurement start again from the new clock's time, because readings of different clocks cannot be compared. The pointer is not owned and the clock must outlive the system. A null pointer selects the real steady clock, which is the default. A container sets the clock of every system it holds with Container::ClockSet().

Thread: container thread only.

◆ componentsClear()

void ecs::System::componentsClear ( )
protected

Removes every component listed in componentsToDelete from the container and empties the list.

Before the system is registered it logs a warning and does nothing.

◆ Configure()

virtual void ecs::System::Configure ( const nlohmann::json &  config)
virtual

Receives the system's configuration; override it to read settings.

The base implementation does nothing, and the library does not call it: the code that creates the system does.

Thread: container thread only.

◆ DeltaTimeGet()

uint32_t ecs::System::DeltaTimeGet ( )

Returns the whole milliseconds of this update's elapsed time.

Deprecated since 1.8.0: use ElapsedGet() or ElapsedSecondsGet().

The part of a millisecond that is left over is carried into the next update, so the running total stays within a millisecond of the true total. The value is the same on every call during one update. It is at most 4 294 967 295, which a single update longer than about 49.7 days reaches; ElapsedGet() is never limited.

Thread: container thread only.

◆ ElapsedGet()

std::chrono::microseconds ecs::System::ElapsedGet ( ) const

Returns the time that passed between the previous update of this system and the current one.

It is measured once per update, from the same clock reading that decided the system was due, and it is the same on every call during that update, from Update(), a timer callback or any helper. Reading it changes nothing, and a system that never reads it loses no time. The values of consecutive updates add up to the time the clock moved. Nothing is clamped: after a long stall the whole stall is reported, so code that integrates over the step should cap it itself, for example with std::min(this->ElapsedSecondsGet(), 0.25).

Before the first update, and during it, the value is the system's configured interval (Timing), because there is no earlier update to measure from. A system whose interval is zero therefore reports zero for its first update.

Thread: container thread only.

◆ ElapsedSecondsGet()

double ecs::System::ElapsedSecondsGet ( ) const

Returns ElapsedGet() in seconds.

Thread: container thread only.

◆ Export()

virtual nlohmann::json ecs::System::Export ( ) const
pure virtual

Describes the system as JSON; every subclass implements it.

It is a const query and must not add or remove systems, entities or components.

Thread: container thread only.

◆ Initialize()

virtual void ecs::System::Initialize ( )
inlinevirtual

Called once before the system's first Update(); override it to set the system up.

It is called exactly once per system, whichever way the system arrived: registered before the container started, registered later from code on the container thread (another system's Initialize() or Update(), a timer callback or a deferred function), registered in a container that the application drives with Update(), or registered as a replacement. Systems that are started together are started in registration order. A system removed before it was reached is never started.

If Initialize() throws, the error is logged with the system's handle and rethrown. The system counts as started: it is not started again, it is updated, and it receives Shutdown() when it leaves the container. The systems registered after it are started at the start of the next pass.

Container->Log() works from here.

Thread: container thread only.

◆ Log()

void ecs::System::Log ( const std::string &  message,
const std::string &  level = "info" 
)

Sends a line to the container's log destination with this system's handle as a prefix.

Before the system is registered the line is held, and registering the system delivers every held line to the destination, in order. Held lines are not capped. The usual levels are "error", "warning", "info" and "debug"; any other name is passed on as given.

Thread: container thread only.

◆ MessageSubmit()

void ecs::System::MessageSubmit ( const nlohmann::json &  message)

Delivers a message to this system.

Returns without waiting for the system's update. The message is moved into messages at the start of the system's next UpdateSystem(), which may be later in the pass that is running. Messages from one sender arrive in the order sent, each exactly once.

Thread: any. The system must be alive.

◆ MessagesWaiting()

size_t ecs::System::MessagesWaiting ( )

Moves any delivered messages into messages, then returns how many messages are waiting to be read.

A message delivered before the call is counted.

Thread: container thread only.

◆ Shutdown()

virtual void ecs::System::Shutdown ( )
inlinevirtual

Called once before a started system is destroyed; override it to release what the system holds.

It is called exactly once for every system that was started, and never for one that was not. The system receives no update and no routed message afterwards. When a container stops or is destroyed, its systems are shut down last registered first.

The thread depends on how the system leaves:

  • Removed or replaced: the container thread, at the end of the pass that removed it (at once when removed outside a pass).
  • A container with its own thread stops, or its manager shuts down: that thread, before it ends.
  • A container driven by the application's Update() calls is stopped or destroyed: the thread that calls Container::Stop() or destroys the container.

An exception thrown from Shutdown() is logged at level "error" with the system's handle and swallowed on every path; the other systems still receive Shutdown() and are released. A Shutdown() that blocks is waited for, not abandoned.

Container->Log() works from here on every path.

◆ TimerAdd()

void ecs::System::TimerAdd ( Timer  timer)

Adds a timer, which first fires one full length from now.

Throws std::runtime_error, adding nothing, when the length is below zero or above ecs::MAX_INTERVAL. Safe to call from a timer callback, on this system or on any other system of the same container. A timer added by one of this system's own timer callbacks is considered from the system's next update. From Update() the timer is added at once.

Thread: container thread only.

◆ TimerClear()

void ecs::System::TimerClear ( const std::string &  name)

Cancels every timer with this name.

Safe to call from a timer callback, including for the callback's own name. Cancelling and then adding the same name leaves only the new timer; adding and then cancelling removes both. From Update() the change takes effect at once.

Thread: container thread only.

◆ Update()

virtual void ecs::System::Update ( )
inlinevirtual

Called on each pass in which the system is due; override it with the system's work.

Thread: container thread only.

◆ UpdateSystem()

void ecs::System::UpdateSystem ( )

Fires the timers that are due, then calls Update().

The container calls this on each pass in which the system is due.

The clock is read once, at the start, and that one reading decides which timers are due and sets the elapsed time of this update (see ElapsedGet()).

Timers are visited in the order they were added. A timer added by a callback does not fire in this call. A timer cleared by a callback and not yet reached does not fire. Afterwards exactly the one-shot timers that fired are removed, and the timer changes made by callbacks take effect before Update() runs. If a callback removes its own system, no further timers fire and Update() is not called. If a callback throws, the error is rethrown after the timer changes have completed. When the call is made by Container::Update(), the container also logs the error with the system's handle.

Thread: container thread only.

Member Data Documentation

◆ bufferedLogMessages

std::vector<std::pair<std::string, std::string> > ecs::System::bufferedLogMessages
protected

Lines logged before the system was registered, as (message, level) pairs in the order they were logged.

Container::System() delivers them. They are not capped: a system that logs without limit before it is registered holds them all in memory.

◆ Components

ecs::TypeEntityComponentList* ecs::System::Components = nullptr

The components of the system's container (Container::Components), or null before registration.

Thread: container thread only.

◆ componentsToDelete

std::unordered_map<std::string, std::vector<std::string> > ecs::System::componentsToDelete
protected

Components a subclass has marked for removal, as entity handles by type name.

A subclass fills it while it iterates Components and then calls componentsClear().

◆ Container

ecs::Container* ecs::System::Container = nullptr

The container the system is registered in, or null before registration.

Set by Container::System() only.

Thread: container thread only.

◆ Handle

const std::string ecs::System::Handle

The system's handle, given to the constructor and never changed.

Thread: any.

◆ lastTime

std::chrono::steady_clock::time_point ecs::System::lastTime = std::chrono::steady_clock::now()
protected

The construction time.

Deprecated since 1.8.0 and not maintained by the library: use ElapsedGet().

◆ messages

std::queue<nlohmann::json> ecs::System::messages
protected

Messages ready to read, oldest first.

Read them in Update() with front() and pop().

Thread: container thread only.

◆ timers

std::vector<ecs::Timer> ecs::System::timers
protected

The system's timers.

Change them with TimerAdd() and TimerClear() only.

◆ Timing

ecs::Timing ecs::System::Timing

The schedule that decides on which passes the system is updated.

Change the interval with Timing.SetInterval(); the default is ecs::DEFAULT_INTERVAL, about 30 updates a second.

Thread: container thread only.


The documentation for this class was generated from the following file: