About Inexor Reactive Graph Flow

What is Inexor?

  • Inexor will be a new first-person shooter game which is based on a new octree-based game engine.
  • Inexor focuses on classic gameplay as we've seen in Cube2 or the Quake series.
  • Inexor will be written from ground up new in C++17 and Rust.
  • You can contribute anything you want: code, content, ideas...
  • Inexor and all its content is 100% open source!

Graph

What is a Graph?

A graph organizes highly interconnected data. The state of an Entity Component System can be ideally represented with the help of the graph. Inexor is the first game engine to introduce a graph as a basis.

graph TD;
    A(Entity A)===>|Relation A|B(Entity B);
    A(Entity A)===>|Relation B|C(Entity C);
    B(Entity B)===>|Relation C|D(Entity D);
    C(Entity C)===>|Relation D|D(Entity D);

Why do we need a graph?

The main benefits of a graph are:

  • A universal data structure for everything
  • Relations are first class citizens
  • Benefit from types and instances which makes things intuitive
  • Benefit from navigation which is fast and intuitive
  • Benefit from the semantics of highly connected, intuitive data
  • Properties can store not only certain primitive data but complete documents

What type of graph does Inexor implement?

Internally, Inexor is an in-memory graph database with the following characteristics:

CharacteristicsDescription
Directed GraphEach relation points from one entity to another entity
Typed Graph
  • Node: Each entity instance is of an entity type
  • Edge: Each relation instance is of a relation type
Property Graph
  • Entities have properties
  • Relations have properties

In addition, Inexor takes up concepts from entity component systems. Types, i.e. entity types and relation types, are assembled using components. The type system itself is flat and supports composition rather than inheritance. Components can be added or removed from instances, i.e. entity instances or relation instances.

What is Reactive?

Now that we understand how data is stored, here's how data interacts. The approach is that the data itself is "alive". To do this, Inexor adopts a concept from reactive programming.

In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change.

It is the ingenious combination of a graph with reactive programming. The property instances are not static and only contain data. Rather, they are streams of data. If you change the value of a property instance, you fill the data stream of this property instance. Data streams are linked together. For example, if the stream of one property instance is linked to the stream of another property instance and you change the value of the first property instance, the value of the second property instance will automatically change as well. Data is thus propagated from one station to the next, triggering a cascade of propagations.

graph TD;
    A(Property Instance 1)-->|stream|B(Property Instance 2);
    A-->|stream|C(Property Instance 3);
    B-->|stream|D(Property Instance 4);
    C-->|stream|D;
    D-->|stream|E(Property Instance 5);
    B-->|stream|F(Property Instance 6);

In addition, Inexor remembers the last value in each property instance. This is done by subscribing to your own data stream and caching it. This allows subsequent querying of the value of a property instance.

Remember this basic concept:

  1. Every property is a stream not only data
  2. Property streams can be subscribed and published
  3. The streams of two properties can be connected and changes will be propagated (cascade)

Behaviour driven design

The data flow is therefore automatic. Building on this, Inexor applies the concept of behaviour-driven design. The goal is to use these data streams to simulate behaviour.

Behaviors can be implemented on components, entities and relations. To do this, one or more incoming data streams are combined, calculations are performed and written to one or more outgoing data streams.

For example, the entity type "AND gate" implements a behavior by subscribing to the two input properties, combining them into a combination data stream and performing an AND operation on the incoming pairs of values. The result of the AND operation is itself a data stream and this is linked to the output property.

This example shows how an entity type is wired internally. They are all data streams that are cleverly combined with one another and thus depict behavior.

It is interesting that this behavior also works for relations. For example, connectors are also implemented behaviors of streams. It is interesting that connectors connect the data stream from a property instance of the outgoing entity instance with the data stream from a property instance of the incoming entity instance.

graph TD;
    subgraph key_ctrl ["Entity Instance (Input Key)"]
    direction RL
    CTRL_KEY(Property<br><br>key_name=CTRL);
    CTRL_KD(Output Property<br><br>key_down);
    end

    subgraph key_f10 ["Entity Instance (Input Key)"]
    F10_KD(Output Property<br><br>key_down);
    F10_KEY(Property<br><br>key_name=F10);
    end

    CTRL_KD--->|connector|LHS;
    F10_KD--->|connector|RHS;

    subgraph logicalgate ["Entity Instance (Logical Gate)"]
    LHS(Input Property<br><br>LHS)o-->ZIP;
    RHS(Input Property<br><br>RHS)o-->ZIP;
    ZIP(Zipped stream of LHS and RHS)-->COMBINATOR;
    COMBINATOR(Combinator Function<br><br>AND)-->RESULT;
    RESULT(Output Property<br><br>Result);
    end

    RESULT--->|connector|CMD_EXIT;

    subgraph system_command_exit ["Entity Instance (System Command)"]
    CMD_COMMAND(Property<br><br>Command);
    CMD_EXIT(Input Property<br><br>trigger)
    end

For example the AND-Gate accepts inputs at the properties lhs and rhs. Both streams are subscribed and zipped. The zipped stream is calculated with a function - in this case the AND-Operator. This results in another (invisible) stream which is connected with the property result. The entity type AND-Gate defines that the properties lhs, rhs and result have to exist. Furthermore, the socket types are defined: lhs and rhs are Input-Sockets and result is a Output-Socket. The behaviour is like the internal wiring of entity instances and of relation instances. A behaviour can be added to entity instances and removed from entity/relation instances.

In the Reactive Graph Flow there are ComponentBehaviours, EntityBehaviours and RelationBehaviours.

TypeBehaviour
Entity Type "and"Entity Behaviour Logical Gate with AND-combinator
Relation Type "default_connector"Relation Behaviour Default Connector

Behaviours are provided by plugins.

What is a flow?

Control flows can be implemented based on the graph, the data streams and the behavior-driven design. It is important here that the available modules that implement the behavior are linked with connectors.

For example, a flow can consist of a logic that links several AND gates with each other using connectors. Both the AND gate and the connector are behaviors. But the arrangement of these behaviors within a flow makes them powerful.

Entire game modes can be implemented with the help of flows. Or just parts of it that are used in multiple game modes, such as a mechanism to pick up, drop, and score flags.

Flows are also useful for making maps more interactive. With the help of flows and behaviors, it can be ensured that a door opens in a map when you press switch 1 and switch 2. Or you determine the color of your own team's base based on the current score. Or you control particle emitters, depending on how many players are near the emitter. The possibilities for this are endless and want to be used!

flowchart LR
    subgraph flow ["Flow"]
        direction LR
        subgraph key_ctrl ["Entity Instance (Input Key)"]
            CTRL_KD(Output Property<br><br>key_down);
        end
    
        subgraph key_f10 ["Entity Instance (Input Key)"]
            F10_KD(Output Property<br><br>key_down);
        end
    
        CTRL_KD-->|connector|LHS;
        F10_KD-->|connector|RHS;
    
        subgraph logicalgate ["Entity Instance (Logical Gate)"]
            LHS(Input Property<br><br>LHS)-.->RESULT;
            RHS(Input Property<br><br>RHS)-.->RESULT;
            RESULT(Output Property<br><br>Result);
        end
    
        RESULT-->|connector|CMD_EXIT(Input Property trigger);
    
        subgraph system_command_exit ["Entity Instance (System Command)"]
            CMD_COMMAND(Property<br><br>Command);
            CMD_EXIT(Input Property<br><br>trigger)
        end
    end

Design Goals

This chapter describes what the application's design goals are and aren't.

Plugin System

The plugin system is a central building block and is therefore mentioned first. The goal of the plugin system is to keep the core as small as possible. The core of the application is described in the next section. Plugins, on the other hand, contain the functionalities.

The aim of the plugin system is also that you don't have to restart the core application completely just because a small part has changed. It should be sufficient to restart the modified plugin.

Also, if a plugin stops working, or if the plugin's configuration is changed, restarting the plugin might avoid restarting the core application.

In addition, the file size of the binary file is reduced since only its libraries are compiled in the core application.

In addition, only what is actually used is loaded. Plugins that are not required for the application can be deactivated.

Core Application

The core application consists of a few central building blocks. As already mentioned in the introduction, this includes a built-in graph database.

Based on this, the type system is managed by the core application. This means that in the business logic layer there is a management service to edit the type system. With the help of these services, new types can be created, queried, searched for and deleted.

Furthermore, the instance system is managed. Similar to the type system, new instances can be created, queried, searched for and deleted.

Reactive instances are managed by the reactive instance system with reactive entity instances, reactive relation instances and reactive flow instances.

Finally, the two main approaches to the Reactive Graph Flow are provided.

One access to the Reactive Graph Flow is the plugin system, which is granted access to the business logic layer of the core application via the plugin API. Type system and reactive instance system are available to every (Rust) plugin.

Second access is the GraphQL server, which also provides access to the type system and the reactive instance system.

In addition, there are cross-sectional functionalities such as lifecycle management, logging and configuration.

For configurations, TOML should be used as the configuration format wherever possible.

The application should be able to be adapted to the application by means of configuration.

Core Application Non-Goals

This section explains which goals are not pursued in the core application.

  • Commands
  • Configurations for purposes other than logging, plugins, GraphQL server
  • System-Variables
  • Meta data
  • Implementation of certain reactive behaviors: e.g. logic gates, arithmetic gates
  • Implementation of game modes, maps, mods
  • Synchronization
  • Flow-Editor
  • Graphics-Rendering
  • Octree-Editor

Roadmap

See Milestones

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.9.0-4] - 2023-03-26

Added

  • Configuration: Configure the instance
  • Configuration: Configure the graphql server
  • Configuration: Configure the plugins
  • Configuration: Added config manager
  • Plugin Resolver: Disable all plugins
  • Plugin Resolver: Disable specific plugins
  • Plugin Repository Manager: Remove duplicate plugins in plugin installation folder
  • Executable: Added cli argument for the instance name and description
  • Executable: Added cli argument for disabling all plugins
  • Executable: Added cli argument for disabling a specific plugin
  • Executable: Added cli argument for disabling hot deployment
  • Executable: Added cli argument for configuring the hostname and port of the GraphQL server
  • Executable: Added cli argument for configuring the shutdown timeout
  • Type System: Allow merging types into existing types and plugin type providers are now merging instead of ignoring changes
  • GraphQL: Simplify trigger action by providing a dedicated GraphQL mutation
  • GraphQL: Allow sorting of type properties
  • GraphQL: Allow sorting of type extensions

Changed

  • Workspace: Create mono repository
  • Workspace: Migrate repository frp into workspace
  • Workspace: Migrate repository model into workspace
  • Workspace: Migrate repository builder into workspace
  • Workspace: Migrate repository reactive into workspace
  • Runtime: Refactor into runtime and binary
  • Runtime: Use tokio as async runtime
  • Plugin Resolver: Resolving plugins asynchronously
  • Plugin Lifecycle: Make plugin activate and deactivate methods async
  • GraphQL: Generate dynamic GraphQL schema using async-graphql 5.0.0

[0.8.0] - 2022-02-01

Added

  • Plugins: Plugin resolver state machine
  • Plugins: Plugin dependencies and plugin versioning
  • Plugins: Plugin installation directory
  • Plugins: Hot deployment
  • Plugins: Gracefully shutdown and refresh plugins and their behaviours during deployment / hot deployment
  • Plugins: GraphQL: List, start, stop and restart plugins
  • Plugins: GraphQL: List unsatisfied dependencies
  • Behaviours: Lifecycles (init, connect, disconnect, shutdown)
  • Behaviours: A state machine manages the behaviour (connecting, disconnecting, reconnecting)
  • Behaviours: Compute behaviour initially
  • Behaviours: Validation layer for behaviours
  • Behaviours: Factories can construct behaviour instances
  • Behaviours: Handle behaviour construction errors
  • Behaviours: Macro support for implementing behaviours
  • Behaviours: Added GraphQL API for behaviours + Allow connecting and disconnecting behaviours manually (flow editor)
  • Behaviours: Unit tests for lots of reactive behaviours
  • Model: Introduce namespaces
  • Model: Use concrete type system identifiers instead of strings
  • API: Validate types on construction (validate that components or entity types are present)

Changed

  • Behaviours: Centralized behaviour storage (no more store behaviours in the plugins)
  • Behaviours: Reimplemented lots of reactive behaviours
  • Plugins: Split most plugins into a model and plugin crate
  • API: Make service API and plugin API more consistent (same/similar method names + use type system identifiers)
  • Tooling: rustfmt uses separate lines per import

[0.7.0] - 2022-09-25

Added

  • Flow Types: Replicate and instantiate flows multiple times (like flow templates)
  • Dynamic Graph: A second GraphQL api make the access to the instance system more intuitive
  • Plugins: Avoid boilerplate using plugin macros
  • Plugins: Metadata about the plugin
  • Plugins: Dependency information
  • Plugins: Expose GraphQL query service to plugins

Changed

  • Plugin: Dedicated error messages for the plugin API
  • Plugin: Default trait implementations for the plugin API to reduce boilerplate
  • Plugin: Return option none if a plugin doesn't use certain functionality

[0.6.0] - 2022-02-26

Added

  • Documentation: Added initial documentation as book (using mdBook)
  • GraphQL: Navigate from components to entity types and to relation types
  • GraphQL: Navigate from a property instance to the property type
  • GraphQL: Shortcut labels
  • GraphQL: Filter flows by flow type
  • GraphQL: Get flow by label
  • GraphQL: Search instances by property value
  • GraphQL: Added method to add/remove component to/from an existing instances
  • GraphQL: Added filters for instances for applied components/behaviours
  • Model: Components can have extensions
  • Core: Provide required components and entity types in the application core
  • Core: Event System for Type System and Instance System

Changed

  • GraphQL: Rename mutation types (Remove prefix GraphQl)
  • GraphQL: Rename type_name to type
  • GraphQL: Stream property instances in subscriptions instead of handcrafted JSONs
  • GraphQL: Simplified label resolver
  • GraphQL: Removed behaviour resolver from types
  • Model: Make extensions a separate type (used in components, entity types and relation types)
  • Model: Removed behaviours from types
  • Model: Added list of applied components and behaviours to reactive instances
  • Documentation: Convert code of conduct and changelog to markdown
  • Documentation: Added code of conduct and changelog to book

[0.5.0] - 2022-01-20

Added

  • Plugin API: Added find, import & export methods for all managers
  • Core: Search for type names with wildcards

Changed

  • Refactored dependency injection library
  • Updated dependencies of the dependency injection library

Removed

  • Plugin-API: Removed dependencies for faster builds and smaller plugin binaries: actix-http, actix-web, async-std, query_interface, inexor-rgf-core-reactive

MIT License

Copyright (c) 2011-2023 The Inexor Collective.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Code of Conduct

Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language
  • Being respectful of differing viewpoints and experiences
  • Gracefully accepting constructive criticism
  • Focusing on what is best for the community
  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances
  • Trolling, insulting or derogatory comments, and personal or political attacks
  • Public or private harassment
  • Publishing others' private information, such as a physical or electronic address, without explicit permission
  • Other conduct which could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@inexor.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq

2. Installation

For now please follow the Build Instructions.

Linux

TODO

Describe how to install Inexor Reactive Graph Flow on linux

Raspberry Pi

TODO

Describe how to install Inexor Reactive Graph Flow on a raspberry pi

Windows

TODO

Describe how to install Inexor Reactive Graph Flow on windows

Configuration

Configure Logging

Edit config/logging.toml

Appender

Stdout

Writes to stdout.

[appenders.stdout]
kind = "console"

[appenders.stdout.encoder]
pattern = "{d(%H:%M:%S%.3f)} [{l}] [{T}] {M}:{L} - {m}{n}\r"

Documentation of the date format library

File

Writes to file.

[appenders.file-application]
kind = "file"
path = "log/inexor-rgf-application.log"

[appenders.file-application.encoder]
pattern = "{d(%Y-%m-%d %H:%M:%S%.3f)} [{l}] [{T}] {M}:{L} - {m}{n}"

Default log level

Set the default logging level to "debug" and attach the stdout and file-application appender to the root.

[root]
level = "debug"
appenders = [ "stdout", "file-application" ]

Per Log levels

Set the log level for a specific module:

[loggers."inexor_rgf_application::implementation::component_manager_impl"]
level = "info"

Documentation of the logging library

Additive

Route log events sent to the inexor_rgf_plugin_mqtt::behaviour::relation::mqtt_subscribes logger to the file-plugin-mqtt appender, and not the normal appenders installed at the root.

[loggers."inexor_rgf_plugin_mqtt::behaviour::relation::mqtt_subscribes"]
level = "debug"
appenders = [ "file-plugin-mqtt" ]
additive = false

Configure HTTP/GraphQL server

  1. Edit config/graphql.toml

Bind address and port

Configure the bind address and port:

hostname = "localhost"
port = 31415

Hostname

If you bind on localhost the GraphQL server is not reachable from outside. Use the hostname or ip which is reachable from outside.

Secure connections (HTTPS/WSS)

Enable HTTPS/WSS:

secure = true

Shutdown timeout

The following setting Timeout for graceful worker shutdown in seconds.

After receiving a stop signal, workers have this much time to finish serving requests. Workers still alive after the timeout are force dropped. By default, shutdown timeout is set to 30 seconds.

shutdown_timeout = 3

Development

If you have to restart often during development, set this to a low value.

Number of workers

This setting specifies the number of worker threads. The default number of worker threads is the number of physical CPU cores available.

workers = 16

Default web resource provider

Plugins can provide web resources using a WebResourceProvider. The URLs are prefixed with the base path of the web resource provider. For example, the plugin binary provides a WebResourceProvider with the base path binary. Therefore, the URL starts with http(s)://hostname:31415/binary/.

The default_context_path defines which web resource provider is the default by its base path. This means that the URL https://hostname:31415/entities/uuid/property_name will be handled by the WebResourceProvider binary and is doing the same as https://hostname:31415/binary/entities/uuid/property_name.

default_context_path = "binary"

Default Base Path

In particular, this is very useful for web applications which shall handle the root URL: https://hostname:31415/

Logging

You can enable or disable logging of HTTP/GraphQL requests and specify the log format. If no format is specified the default log format is used.

[logging]
enabled = true
format = "%a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T"

List of Log Format Variables

https://docs.rs/actix-web/latest/actix_web/middleware/struct.Logger.html#format

Configure Plugins

Edit config/plugins.toml

Order of initialization

The plugins are initialized in the order of definition!

Deactive plugins

You can activate or deactivate plugins with the setting active. Remember that some plugins depend on other plugins.

Path

The path can be either relative to the working directory or absolute.

Linux

[[plugin]]
name = "inexor-rgf-plugin-base"
active = true
path = "../inexor-rgf-plugin-base/target/debug/libinexor_rgf_plugin_base.so"

[[plugin]]
name = "inexor-rgf-plugin-mqtt"
active = true
path = "../inexor-rgf-plugin-mqtt/target/debug/libinexor_rgf_plugin_mqtt.so"

Please note

  • The filename of the linked library is prefixed with lib
  • On linux the file extension is .so
  • The path separators are forward slashes /

Windows

[[plugin]]
name = "inexor-rgf-plugin-base"
active = true
path = "..\\inexor-rgf-plugin-base\\target\\debug\\inexor_rgf_plugin_base.dll"

[[plugin]]
name = "inexor-rgf-plugin-mqtt"
active = true
path = "..\\inexor-rgf-plugin-mqtt\\target/debug\\inexor_rgf_plugin_mqtt.dll"

Please note

  • The filename of the linked library is not prefixed with lib
  • On Windows the file extension is .dll
  • The path separators are backslashes and must be escaped \\

Model

The data model of Inexor Reactive Graph Flow can be divided into the type system and the instance system.

Type System

The type system is similar to type systems in programming languages. An object is of a specific, previously defined type.

The type says something about the semantic meaning. For example, just looking at the type name gives you an idea of what a "player" or a "camera" is, for example.

The type system also specifies the data model of such a semantic object. For example, a player has a name, is in a position, and you know how many frags he has scored. Therefore, it is defined that a player should have a property called "name".

Types are also related to each other. For example, a teleport entrance has one or more teleport exits. This relationship between the teleport input type and the teleport output type is called a relation type. The relation type also has a semantic meaning. In this case, the relation type "teleports_to" means that a player who is directly at the teleport entrance should be teleported to the teleport exit.

Another example is a team that has one player as a member. There is a relation type called "is_member_of" that leads from the entity type team to the entity type relation. Note the direction of the relation type.

This becomes even more relevant when there can be multiple distinct relationships between two entity types. A player can be a member of a team and he can be the team captain at the same time. The first type of relationship has already been described, the second has the name "is_captain_of".

ER Diagram

erDiagram
    Relation-Type
    Entity-Type
    Extension
    Component
    Property-Type
    Relation-Type ||--}o Component : composes
    Relation-Type o{--|| Entity-Type : outbound
    Relation-Type o{--|| Entity-Type : inbound
    Entity-Type ||--}o Component : composes
    Relation-Type ||--}o Property-Type : defines
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines
    Component ||--}o Property-Type : defines
    Relation-Type ||--}o Extension : has
    Entity-Type ||--}o Extension : has
    Property-Type ||--}o Extension : has

Example: Relation Type

graph LR
    E1(Entity Type<br>Teleporter)
    E2(Entity Type<br>Teledestination)
    R1(Relation Type<br>Teleports_To)
    E1--->|outbound|R1
    R1--->|inbound|E2

Example: Relation Types & Entity Types

graph TB
    P(Player)
    C(Camera)
    F(Flag)
    T(Team)
    B(Base)
    S(Playerstart)
    C--->|looks_at|P
    C--->|looks_at|F
    C--->|looks_at|B
    P--->|is_member_of|T
    P--->|frags|P
    T--->|is_located_at|B
    T--->|owns|F
    B--->|provides|S
    P--->|has_spawned_at|S

Instance System

Having described the type system, this section describes the instance system.

The type system describes what could exist.

A type defines how an instance should look like. An instance itself fills this type with life. There can be any number of instances of a type. For example, there is the player named "peter" and the player named "penacka". In this case there are two instances of the same type.

The following table shows that an instance and the corresponding type:

TypeInstance
Entity TypeEntity Instance
Relation TypeRelation Instance
Entity TypeFlow Instance
erDiagram
    Relation-Type
    Relation-Instance
    Entity-Type
    Entity-Instance
    Flow-Instance
    Relation-Instance o{--|| Relation-Type : is_a
    Entity-Instance o{--|| Entity-Type : is_a
    Flow-Instance o{--|| Entity-Type : is_a

Example: Relation Instances & Entity Instances

graph TB
    P1(Player 1)
    P2(Player 2)
    C1(Camera 1)
    C2(Camera 2)
    F1(Flag 1)
    F2(Flag 2)
    F3(Flag 3)
    F4(Flag 4)
    T1(Team Good)
    T2(Team Evil)
    B1(Base Good)
    B2(Base Evil)
    ST11(Playerstart Good 1)
    ST12(Playerstart Good 2)
    ST21(Playerstart Evil 1)
    ST22(Playerstart Evil 2)
    C1--->|looks_at|P1
    C2--->|looks_at|F2
    P1--->|is_member_of|T1
    P2--->|is_member_of|T2
    P1--->|fragged|P2
    P2--->|fragged|P1
    T1--->|is_located_at|B1
    T2--->|is_located_at|B2
    T1--->|owns|F1
    T1--->|owns|F2
    T2--->|owns|F3
    T2--->|owns|F4
    B1--->|provides|ST11
    B1--->|provides|ST12
    B2--->|provides|ST21
    B2--->|provides|ST22
    P1--->|has_spawned_at|ST11
    P2--->|has_spawned_at|ST22
    P1--->|stole|F3
    P2--->|stole|F2

Model: Component

A component is a reusable building block for entity types and relation types. A component semantically represents an aspect of one or more entity types or relation types.

For example, the component "named" represents that something has a name. In practice, the component has a property called "name" which contains the name. The "named" component can be used in a wide variety of cases. For example, a player has a name, a server has a name, and a player model also has a name. Apart from the name, they have little in common.

A component should provide at least one property. However, it is also possible to define several properties.

Another example is the "position" component, which defines the x, y, and z coordinates. Again, this is a perfect example of a Component. Because all things in a 3D world have a position. A player has a position, a camera, a flag or a light each have a position. But here, too, they have little in common. And yet they have at least one thing in common.

A player has a name and a position, i.e. an entity type (just like a relation type) can consist of several components. The properties of all components are merged.

Data Model

FieldDataTypeDescriptionExample
NameStringThe name of the componentlabeled
DescriptionStringTextual description of the componentThe label is an hierarchical path with static segments, named parameters and catch-all parameters.
PropertiesVec<Property Type>The properties which are applied on entity or relation instances.

ER Diagram

erDiagram
    Component {
        string name
        string description
    }
    Property-Type {
        string name
        string description
    }
    Extension {
        string name
        JSON extension
    }
    Component ||--}o Property-Type : defines
    Property-Type ||--}o Extension : has
    Entity-Type o{--}o Component : composes
    Relation-Type o{--}o Component : composes

GraphQL

Model: Entity Type

An entity type defines the characteristics that are common to all entity instances. In particular, an entity type defines which components it is combined from. Additional properties can also be defined.

Data Model

FieldDataTypeDescriptionExample
NameStringThe name of the entity typeplayer
NamespaceStringThe namespacelogical_gates
DescriptionStringTextual description of the entity type
ComponentsVec<Component>The components which composes the entity type. These provides additional properties
PropertiesVec<Property Type>The additional properties on entity instances
ExtensionsVec<Extension>A list of extensions which contains additional information

ER Diagram

erDiagram
    Entity-Type {
        string name
        string namespace
        string description
    }
    Entity-Instance {
        string id
        string label
        string description
    }
    Extension {
        string name
        JSON extension
    }
    Component {
        string name
        string description
    }
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Relation-Type {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Entity-Type o{--}o Component : composes
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines
    Component ||--}o Property-Type : defines
    Entity-Type ||--}o Relation-Type : outbound
    Entity-Type ||--}o Relation-Type : inbound
    Relation-Type o{--}o Component : composes
    Entity-Type ||--}o Extension : has
    Relation-Type ||--}o Extension : has
    Property-Type ||--}o Extension : has
    Entity-Instance ||--}o Entity-Type : is-a

GraphQL

Model: Relation Type

A relation type defines the characteristics that are common to all relation instances. In particular, a relation type defines the entity type of the outbound entity instance and the entity type of the inbound entity instance.

Like entity types, it is also defined which components make up the relation type and which additional properties it contains.

As with entity types, extensions to the relation type can be defined, such as the appearance of edges in a graph or the appearance of a connector of this type in the flow editor.

Data Model

FieldDataTypeDescription
Outbound TypeEntityTypeThe entity type of the outbound entity instance
NameStringThe name of the relation type
Full NameStringThe full name of the relation type
Inbound TypeEntityTypeThe entity type of the inbound entity instance
NamespaceStringThe namespace
DescriptionStringTextual description of the entity type
ComponentsVec<Component>The components which composes the relation type. These provides additional properties
PropertiesVec<Property Type>The additional properties on relation instances
ExtensionsVec<Extension>A list of extensions which contains additional information

Graph

graph LR;
    A(Outbound Entity Type)===>|"Relation Type"|B(Inbound Entity Type);

ER Diagram

erDiagram
    Relation-Type {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Entity-Type {
        string name
        string namespace
        string description
    }
    Relation-Instance {
        string uuid
    }
    Extension {
        string name
        JSON extension
    }
    Component {
        string name
        string description
    }
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Relation-Type ||--}o Component : composes
    Relation-Type o{--|| Entity-Type : outbound
    Relation-Type o{--|| Entity-Type : inbound
    Entity-Type ||--}o Component : composes
    Relation-Type ||--}o Property-Type : defines
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines
    Component ||--}o Property-Type : defines
    Relation-Type ||--}o Extension : has
    Entity-Type ||--}o Extension : has
    Property-Type ||--}o Extension : has
    Relation-Instance o{--|| Relation-Type : is-a

GraphQL

Model: Property Type

Components, entity types and relation types define which properties they contain. The so-called property types define the name, the data type and whether the property is an input or an output.

Data Model

FieldDataTypeDescriptionExample
NameStringThe name of the propertyname
DescriptionStringTextual description of the property
DataTypeDataTypeThe data type of the property
SocketTypeSocketTypeThe socket type
ExtensionsVec<Extension>The extensions contains additional information about the property type

ER Diagram

erDiagram
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Extension {
        string name
        JSON extension
    }
    Property-Type ||--}o Extension : has
    Component ||--}o Property-Type : defines
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines

Enum Data Type

A property has a data type. This is derived from serde_json::Value and maps JSON values or documents.

ValueDescription
NullRepresents a JSON null value
BoolRepresents a JSON boolean
NumberRepresents a JSON number, whether integer or floating point
StringRepresents a JSON string
ArrayRepresents a JSON array.
ObjectRepresents a JSON object.
AnyRepresents any type (relations)

Enum Socket Type

The socket type determines whether a property serves as an input or an output. It is also possible that a property is neither an input nor an output. Caution: At runtime, it is not prevented if an input is used as an output or an output is used as an input.

ValueDescription
NoneThe property doesn't act as input or output socket
InputThe property acts as input socket and accepts incoming connections
OutputThe property acts as output socket and accepts outgoing connections

Where is the value???

A property type only contains the definition of a property, but no data, since it is a type.

When an entity instance or a relation instance is created, the property types are used to create property instances. The property instances then contain a data stream and the last value.

Model: Flow Type

TODO

Model: Extension

There is information about a type that is useful for certain cases, but at the same time is not needed in every case. The data model of the types should remain lean and not be overloaded. Nevertheless, future changes and new functionalities should be made possible. However, this should be done in a way without becoming incompatible. This extended information is called extensions.

An extension consists of two fields. On the one hand the name of the extension and on the other hand a field with unstructured data in JSON format. This means that the data model of the extension is not specified. At the same time, however, it is possible to provide any conceivable information. Because a JSON value is used, any complex and nested information is possible. It is possible to deserialize the JSON value into a concrete data model.

An example of an extension is the information on how a flow editor should draw a shape of an entity type. The extension is called "flow_shape" and contains information about the color, size, label and alignment of the shape. This example shows that an extension can potentially contain very complex data structures and at the same time that an extension covers a case that is not relevant in all cases. For example, if the "Flow Editor" plugin is not installed or not activated, the extension is present but not used.

Data Model

FieldDataTypeDescription
NameStringThe name of the extension
ExtensionJSONThe extension as schema-less JSON.

ER Diagram

erDiagram
    Extension {
        string name
        JSON extension
    }
    Entity-Type ||--}o Extension : has
    Relation-Type ||--}o Extension : has
    Property-Type ||--}o Extension : has

List of known extensions

ExtensionDescription
dublin-coreMeta data
flow_editor_paletteAn entry for the palette of the flow editor
flow_editor_shapeDefinition of the shape of an entity instance in the flow editor
type_graph_shapeDefinition of the shape of an entity type in the type graph
instance_graph_shapeDefinition of the shape of an entity instance of in the instance graph

Extension dublin_core

    {
      "name": "dublin-core",
      "extension":{
        "title": "Decrement number",
        "subject": "Decrement number",
        "creator": "Hanack"
      }
    }

Extension flow_editor_palette

{
  "name": "flow_editor_palette",
  "extension": {
    "content": "Decrement",
    "styles": {
      "font-family": "Fira Code",
      "font-size": "12px",
      "padding": "5px"
    }
  }
}

Extension flow_editor_shape

{
  "name": "flow_editor_shape",
  "extension": {
    "width": 200,
    "socket": {
      "width": 60,
      "height": 30,
      "offset": 5
    },
    "offset": {
      "top": "socket.height",
      "bottom": "socket.height"
    },
    "elements": {
      "title": {
        "show": true,
        "type": "text",
        "content": "element.description",
        "position": {
          "left": 0,
          "top": 0,
          "width": "shape.width",
          "height": "socket.height"
        },
        "styles": {
          "font-size": "12px",
          "color": "black"
        }
      },
      "symbol": {
        "show": true,
        "type": "text",
        "content": "--",
        "position": {
          "left": 0,
          "top": 0,
          "width": "shape.width",
          "height": "shape.height"
        },
        "styles": {
          "fill": "steelblue",
          "font-size": "80px",
          "font-family": "Fira Code"
        }
      },
      "id": {
        "show": true,
        "type": "text",
        "content": "shape.id",
        "position": {
          "left": 0,
          "top": "shape.height-socket.height",
          "width": "shape.width",
          "height": "socket.height"
        },
        "styles": {
          "font-size": "9px",
          "color": "black"
        }
      }
    }
  }
}

GraphQL

Model: Entity Instance

An entity instance is an object with characteristics that is of a specific entity type. An entity instance contains a property instance for each property type defined either in the entity type or in one of the entity type's components.

In contrast to entity types, property instances are contained and each property instance contains a data stream and its last value.

UUIDs

Entity instances can be uniquely identified via a UUID. This is particularly important when entity instances are synchronized in a distributed system. For example, a player's representation exists not only in the local client, but also on the server and on clients connected to it.

Error rendering admonishment

Failed with: TOML parsing error: expected an equals, found an identifier at line 1 column 6

Original markdown input:

```admonish info UUIDs
* UUIDs are unique and unique across distributed systems
* UUIDs can be represented as string and as a 128 bit unsigned integer
```

Labels

Labels are also unique, but optional. While UUIDs are hard to remember, labels are easier to remember. This allows entity instances to be addressed using labels, for example in GraphQL queries.

If you compare the query with and without a label, you will find that the query is easier to understand thanks to the label and transports significantly more information about what is actually happening.

Info

Labels are optional

query {
  instances {
    entities(label: "/org/inexor/input/any_device/key/key_f7") {
      properties(name: "key_down") {
        value
      }
    }
  }
}
query {
  instances {
    entities(id: "c7ec76e0-40e8-587e-bc89-e9cd31fa75a0") {
      properties(name: "key_down") {
        value
      }
    }
  }
}

Data Model

FieldDataTypeDescription
TypeEntityTypeThe type of the entity instance
IDUUIDThe unique identifier of the entity instance
LabelString (optional)The label of the entity instance
DescriptionStringTextual description of the entity type
PropertiesVec<Property Instance>The properties of the entity instance
ComponentsVecThe currently applied components
BehavioursVecThe currently applied behaviours

Graph

graph LR;
    EI(Entity Instance);
    I1(Entity Instance)===>|"Relation Instance"|EI;
    I2(Entity Instance)===>|"Relation Instance"|EI;
    I3(Entity Instance)===>|"Relation Instance"|EI;
    EI===>|"Relation Instance"|O1(Entity Instance);
    EI===>|"Relation Instance"|O2(Entity Instance);
    EI===>|"Relation Instance"|O3(Entity Instance);

ER Diagram

erDiagram
    Entity-Type {
        string name
        string namespace
        string description
    }
    Entity-Instance {
        string id
        string label
        string description
    }
    Relation-Type {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Relation-Instance {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Property-Instance {
        string name
        JSON value
    }
    Entity-Instance ||--}o Property-Instance : stores
    Entity-Instance o{--}o Relation-Instance : outbound
    Entity-Instance o{--}o Relation-Instance : inbound
    Relation-Instance ||--}o Property-Instance : stores
    Relation-Instance o{--|| Relation-Type : is-a
    Property-Instance o{--|| Property-Type : is-a
    Entity-Instance o{--|| Entity-Type : is-a
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines
    Entity-Type ||--}o Relation-Type : outbound
    Entity-Type ||--}o Relation-Type : inbound

GraphQL

Model: Relation Instance

A relation instance is a connection between an outbound entity instance and an inbound entity instance. The relation instance has a type, the relation type. The relation type defines which entity type the outbound entity type and which entity type the inbound entity type must have.

Data Model

FieldDataTypeDescription
OutboundEntityInstanceThe outbound entity instance
TypeRelationTypeThe relation type
InboundEntityInstanceThe inbound entity instance
DescriptionStringTextual description of the relation instance
PropertiesVec<Property Instance>The properties
ComponentsVecThe currently applied components
BehavioursVecThe currently applied behaviours

Graph

graph LR;
    A(Outbound Entity Instance)===>|"Relation Instance"|B(Inbound Entity Instance);

ER Diagram

erDiagram
    Entity-Type {
        string name
        string namespace
        string description
    }
    Entity-Instance {
        string id
        string label
        string description
    }
    Relation-Type {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Relation-Instance {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Property-Instance {
        string name
        JSON value
    }
    Entity-Instance ||--}o Property-Instance : stores
    Entity-Instance o{--}o Relation-Instance : outbound
    Entity-Instance o{--}o Relation-Instance : inbound
    Relation-Instance ||--}o Property-Instance : stores
    Relation-Instance o{--|| Relation-Type : is-a
    Property-Instance o{--|| Property-Type : is-a
    Entity-Instance o{--|| Entity-Type : is-a
    Entity-Type ||--}o Property-Type : defines
    Relation-Type ||--}o Property-Type : defines
    Entity-Type ||--}o Relation-Type : outbound
    Entity-Type ||--}o Relation-Type : inbound

GraphQL

Model: Property Instance

A property instance is a data stream. The current value of the property instance can be queried. Data streams can be connected to each other (both within an instance and to data streams from property instances of other entity or relation instances).

The property type defines the name, data type and socket type of the property instance.

Data Model

FieldDataTypeDescription
NameStringThe name of the property
ValueValueThe value of the property
TypeProperty TypeThe type of the property

Graph

graph TD;
    PI1(Property Instance);
    PI2(Property Instance);
    PI3(Property Instance);
    EI(Entity Instance)--->PI1;
    EI(Entity Instance)--->PI2;
    EI(Entity Instance)--->PI3;

ER Diagram

erDiagram
    Property-Type {
        string name
        string description
        enum DataType
        enum SocketType
    }
    Property-Instance {
        string name
        string value
    }
    Entity-Instance {
        string id
        string label
        string description
    }
    Relation-Instance {
        string name
        string instanceTypeName
        string namespace
        string description
    }
    Property-Type ||--}o Property-Instance : is-a
    Entity-Instance ||--}o Property-Instance : stores
    Entity-Instance o{--}o Relation-Instance : outbound
    Entity-Instance o{--}o Relation-Instance : inbound
    Relation-Instance ||--}o Property-Instance : stores

Model: Flow Instances

What is a flow instance?

A flow instance is a collection of entity instances and relation instances. Most of the time, a flow instance serves a specific purpose.

Wrapper Entity Instance

A flow instance is itself an entity instance. The properties of the entity instance can be viewed as inputs and outputs for the entire flow instance. The entity instance which is the flow instance is called wrapper entity instance.

graph LR;
    F(Flow Instance = Entity Instance);
    IPI1(Input Property Instance)===>F;
    IPI2(Input Property Instance)===>F;
    IPI3(Input Property Instance)===>F;
    F===>OPI1(Output Property Instance);
    F===>OPI2(Output Property Instance);
    F===>OPI3(Output Property Instance);

Nested Flows

Flow instances are nestable. That is, an entity instance that is itself a flow instance can be used in another flow instance. These nested flow instances can be thought of as subprograms or sub flows. The input properties of the subflow can be considered as parameters of the subprogram and the output properties of the subflow can be considered as the function result of the subprogram.

An entity instance can exist in several flow instances at the same time. For example, a TOML configuration loaded into an entity instance can be used in multiple flow instances.

Nested Flow Instance Example

graph LR;
    subgraph Outer-Flow-Instance
        direction LR
        E1(Entity Instance);
        E2(Entity Instance);
        E3(Entity Instance);
        E4(Entity Instance);
        E5(Entity Instance);
        E1--->|connector|IF1E1
        E4--->|connector|IF1E1
        E4--->|connector|E5
        subgraph Inner-Flow-Instance-1
            direction LR
            IF1E1-->|connector|IF1E2
        end
        IF1E2--->E2
        E2--->|connector|IF2E1
        E5--->|connector|IF2E1
        subgraph Inner-Flow-2
            direction LR
            IF2E1-->|connector|IF2E2
        end
        IF2E2--->|connector|E3
    end

GraphQL

GraphQL API

GraphQL is the most important interface for interaction with the Reactive Graph Flow.

What is GraphQL?

GraphQL stands for "Graph Query Language". It is a query language with which data present in graphs can be efficiently queried and selectively modified.

Why do we use GraphQL?

As already described, Inexor uses a graph to hold the highly interconnected data of the Entity Component System. A query language like GraphQL allows navigating on this graph. For example, it is possible to navigate from an entity instance to its entity type. It is best described by the three graphics that show the possibilities available through the GraphQL interface.

GraphQL was chosen as the query language because it is independent of a specific programming language and because it can be used across technologies.

GraphQL also supports subscribing to change to data. This is also the case, for example, in the reactive property instances. It is possible to subscribe to a property of an entity instance via GraphQL and get changes to this property in real time.

Other advantages of GraphQL are type safety and the fact that semantics can be mapped better.

Queries

GraphQL Queries

GraphQL Schema Visualization

Mutations

GraphQL Mutations

GraphQL Schema Visualization

Subscriptions

GraphQL Subscriptions

GraphQL Schema Visualization

GraphQL Endpoint

The GraphQL endpoint can be reached at http://hostname/31415/graphql or ws://hostname/31415/graphql.

The GraphQL schema is well documented.

Schema Introspection

The GraphQL Server allows introspection and returns a GraphQL schema including documentation. With this it is possible to validate queries and mutations and some tools can use the schema to provide autocompletion for creating queries.

GraphQL Tools

GraphQL API: Components

GraphQL API: Entity Types

Relation Types

Get Relation Type by Name

query {
  types {
    relations(name: "example_relation") {
      name
      instanceTypeName
      description
      outboundTypes {
        name
      }
      inboundTypes {
        name
      }
      components {
        name
      }
      properties {
        name
        dataType
        socketType
        extensions {
          name
          extension
        }
      }
    }
  }
}

Find Relation Type

query {
  types {
    relations(search: "example_relation?") {
      name
      instanceTypeName
      description
      outboundTypes {
        name
      }
      inboundTypes {
        name
      }
      components {
        name
      }
      properties {
        name
        dataType
        socketType
      }
    }
  }
}

Create Relation Type

mutation {
  types {
    relations {
      create(
        name: "example_relation"
        outboundType: "entitytype1"
        inboundType: "entitytype2"
        components: [
          "component1"
        ]
        properties: [
          {
            name: "example_property"
            description: "Example Property"
            dataType: NUMBER
            socketType: INPUT
            extensions: []
          }
        ]
      ) {
        name
        description
        outboundTypes {
          name
        }
        inboundTypes {
          name
        }
        components {
          name
        }
        properties {
          name
          dataType
          socketType
        }
      }
    }
  }
}

Delete Relation Type

mutation {
  types {
    relations {
      delete(name: "example_relation")
    }
  }
}

Flow Types

TODO

Extension

Query extensions of an entity type

query {
  types {
    entities(name: "example_entity") {
      name
      properties {
        extensions {
          name
          extension
        }
      }
    }
  }
}

Query extension by name

query {
  types {
    entities(name: "example_entity") {
      name
      properties {
        extensions(name: "flow_editor_palette") {
          extension
        }
      }
    }
  }
}

Entity Instances

Query an entity instance by id

query {
  instances {
    entities(id: "0-0-0-0") {
      id
      type {
        name
      }
      properties {
        name
        value
      }
    }
  }
}

Query a property instance by name of an entity instance by label

query {
  instances {
    entities(label: "/org/inexor/input/any_device/key_f10") {
      properties(name: "key_down") {
        value
      }
    }
  }
}

Query the positions of all cameras that are looking at a player

query {
  instances {
    entities(type: "player", label: "/org/inexor/game/players/Hanack") {
      inbound(type: "look_at") {
        outbound {
          properties(name: "position") {
            value
          }
        }
      }
    }
  }
}

Relation Instances

Get pairs of outbound + inbound entity instances of all relations of a specific type

query {
  instances {
    relations(type: "looks_at") {
      outbound {
        id
      }
      inbound {
        id
      }
    }
  }
}

Get all inbound relation instances of an entity instance

query {
  instances {
    entities(id: "uuid") {
      inbound {
        type {
          name
        }
        properties {
          name
          value
        }
      }
    }
  }
}

Get the entity instances which are inbound relations to an entity instance

query {
  instances {
    entities(type: "player") {
      inbound {
        outbound {
          id
          type {
            name
          }
          properties {
            name
            value
          }
        }
      }
    }
  }
}

Get the entity instances which are outbound relations to an entity instance

query {
  instances {
    entities(type: "player") {
      outbound {
        inbound {
          id
          type {
            name
          }
          properties {
            name
            value
          }
        }
      }
    }
  }
}

Flow Instance

Get all flow ids

query {
  instances {
    flows {
      id
    }
  }
}

Get all flows of a particular entity type

query {
  instances {
    flows(type: "generic_flow") {
      id
    }
  }
}

Get the label of a flows by id

query {
  instances {
    flows(id: "uuid") {
      label
    }
  }
}

Get the id of a flows by label

query {
  instances {
    flows(label: "/org/inexor/flows/game-servers") {
      id
    }
  }
}

Get the entity instance which are contained by a flow

query {
  instances {
    flows(id: "uuid") {
      entities {
        id
      }
    }
  }
}

Get the properties of the wrapper entity instance of a flow

query {
  instances {
    flows(id: "uuid") {
      wrapper {
        properties {
          name
          value
          type {
            dataType
            socketType
          }
        }
      }
    }
  }
}

Get the relation instances which are contained by a flow

query {
  instances {
    flows(id: "uuid") {
      relations {
        outbound {
          id
        }
        type {
          name
        }
        inbound {
          id
        }
      }
    }
  }
}

Get all entity instances and all relation instances of a flow

query {
  instances {
    flows(id: "uuid") {
      type {
        name
      }
      label
      entities {
        id
        label
        type {
          name
        }
        properties {
          name
          value
        }
      }
      relations {
        type {
          name
        }
        outbound {
          id
        }
        inbound {
          id
        }
        properties {
          name
          value
        }
      }
    }
  }
}

Property Instance Subscriptions

GraphQL Schema Visualization

Subscribe changes of the key right-ctrl

subscription keyDownRightCtrl {
  entity(
    label: "/org/inexor/input/any_device/key/key_rightctrl",
    propertyName: "key_down"
  ) {
    name
    value
    type {
      name
      dataType
      socketType
      extensions {
        name
        extension
      }
    }
  }
}

Dynamic Graph API

Advantages

  1. It is easier to query or modify because you operate on real types not abstract types
  2. Queries and mutations are more readable
  3. The navigation through the Dynamic Graph is much easier and more natural 4It is easier to use by the consumer because it's not necessary to convert the abstract types into the real types as you already operate on real types 5It is possible to use a code generator to generate types from the Dynamic Graph schema

Limitations

  1. Not everything is possible
  2. The Dynamic Graph schema contains all Components, Entity Types and Relation Types as defined by the type system. The Reactive Graph Flow enables you to add further components to existing Entity Instances or Relation Instances. The schema simply cannot contain which components have been added to which instances at runtime. Querying for these properties is possible but makes the query invalid.
  3. Due to the parsing and resolving at runtime the Dynamic Graph is notable slower than the GraphQL API which is generated at compile time.

GraphQL Endpoint

The GraphQL endpoint can be reached at http://hostname/31415/dynamic-graph or ws://hostname/31415/dynamic-graph.

The GraphQL schema documentation is automatically generated using the documentation of the Components, Entity Types or Relation Types.

Schema Introspection

The GraphQL Server allows introspection and returns a GraphQL schema including documentation. With this it is possible to validate queries and mutations and some tools can use the schema to provide autocompletion for creating queries.

Schema Regeneration

The GraphQL schema is regenerated each time a Component, an Entity Type or a Relation Type is added or removed.

GraphQL Tools

Usage

5. Plugin System

The plugin system is a central building block and is therefore mentioned first. The goal of the plugin system is to keep the core as small as possible. The core of the application is described in the next section. Plugins, on the other hand, contain the functionalities.

The aim of the plugin system is also that you don't have to restart the core application completely just because a small part has changed. It should be sufficient to restart the modified plugin.

Also, if a plugin stops working, or if the plugin's configuration is changed, restarting the plugin might avoid restarting the core application.

In addition, the file size of the binary file is reduced since only its libraries are compiled in the core application.

In addition, only what is actually used is loaded. Plugins that are not required for the application can be deactivated.

Trait Plugin

#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {

    // ===== Metadata =====

    fn metadata(&self) -> Result<PluginMetadata, PluginError> {}

    // ===== Lifecycle =====

    fn init(&self) -> Result<(), PluginError> {}

    fn post_init(&self) -> Result<(), PluginError> {}

    fn pre_shutdown(&self) -> Result<(), PluginError> {}

    fn shutdown(&self) -> Result<(), PluginError> {}

    // ===== Context =====

    fn set_context(&self, context: Arc<dyn PluginContext>) -> Result<(), PluginError> {}

    // ===== Providers =====
    
    fn get_component_provider(&self) -> Result<Arc<dyn ComponentProvider>, PluginError> {}

    fn get_entity_type_provider(&self) -> Result<Arc<dyn EntityTypeProvider>, PluginError> {}

    fn get_relation_type_provider(&self) -> Result<Arc<dyn RelationTypeProvider>, PluginError> {}

    fn get_component_behaviour_provider(&self) -> Result<Arc<dyn ComponentBehaviourProvider>, PluginError> {}

    fn get_entity_behaviour_provider(&self) -> Result<Arc<dyn EntityBehaviourProvider>, PluginError> {}

    fn get_relation_behaviour_provider(&self) -> Result<Arc<dyn RelationBehaviourProvider>, PluginError> {}

    fn get_flow_provider(&self) -> Result<Arc<dyn FlowProvider>, PluginError> {}

    fn get_web_resource_provider(&self) -> Result<Arc<dyn WebResourceProvider>, PluginError> {}
}
}

Provide plugin metadata

Built-in metadata of the plugin:

  • The name of the plugin (use environment variable CARGO_PKG_NAME)
  • The description of the plugin (use environment variable CARGO_PKG_DESCRIPTION)
  • The version of the plugin (use environment variable CARGO_PKG_VERSION)
#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {
    fn metadata(&self) -> Result<PluginMetadata, PluginError> {
        Ok(PluginMetadata {
            name: env!("CARGO_PKG_NAME").into(),
            description: env!("CARGO_PKG_DESCRIPTION").into(),
            version: env!("CARGO_PKG_VERSION").into(),
        })
    }
}
}

Lifecycle

The plugin trait provides methods to do initialization and shutdown before and after setting up and shutting down providers.

TODO: Lifecycle Image

#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {
    fn init(&self) -> Result<(), PluginError> {
        // Called before initialization of the providers
        Ok(())
    }

    fn post_init(&self) -> Result<(), PluginError> {
        // Called after initialization of the providers
        Ok(())
    }

    fn pre_shutdown(&self) -> Result<(), PluginError> {
        // Called before shutdown of the providers
        Ok(())
    }

    fn shutdown(&self) -> Result<(), PluginError> {
        // Called after shutdown of the providers
        Ok(())
    }
}
}

Trait PluginContext

#![allow(unused)]
fn main() {
pub trait PluginContext: Send + Sync {
    /// Returns the component manager.
    fn get_component_manager(&self) -> Arc<dyn ComponentManager>;

    /// Returns the entity type manager.
    fn get_entity_type_manager(&self) -> Arc<dyn EntityTypeManager>;

    /// Returns the relation type manager.
    fn get_relation_type_manager(&self) -> Arc<dyn RelationTypeManager>;

    /// Returns the entity instance manager.
    fn get_entity_instance_manager(&self) -> Arc<dyn EntityInstanceManager>;

    /// Returns the relation instance manager.
    fn get_relation_instance_manager(&self) -> Arc<dyn RelationInstanceManager>;

    /// Returns the flow manager.
    fn get_flow_manager(&self) -> Arc<dyn FlowManager>;
}
}

Make use of the plugin context

The plugin have to store a reference to the plugin context. Therefore, you have to implement the method set_context which is called during initialization:

#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {
    fn set_context(&self, context: Arc<dyn PluginContext>) -> Result<(), PluginError> {
        self.context.0.write().unwrap().replace(context);
        Ok(())
    }
}
}

If you want to make use of the EntityInstanceManager you have to:

  1. Get a RwLock on the context
  2. Get a reference of the EntityInstanceManager using get_entity_instance_manager
  3. Create the entity instance using a builder (non-reactive)
  4. Create/register the entity instance using create returns a reactive entity instance
#![allow(unused)]
fn main() {
impl TestManager {
    fn create_entity(&self) {
        let reader = self.context.0.read().unwrap();
        let entity_instance_manager = reader.as_ref().unwrap().get_entity_instance_manager().clone();
        let entity_instance = EntityInstanceBuilder::new("entity_type_name")
            .property("property_name", json!("property_value"))
            .get();
        let reactive_entity_instance = entity_instance_manager.create(entity_instance);
    }
}
}

Implement or ignore providers

If your plugin provide components you have to return a reference of the ComponentProvider:

#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {
    fn get_component_provider(&self) -> Result<Arc<dyn ComponentProvider>, PluginError> {
        let component_provider = self.component_provider.clone();
        let component_provider: Result<Arc<dyn ComponentProvider>, _> = <dyn query_interface::Object>::query_arc(component_provider);
        if component_provider.is_err() {
            return Err(PluginError::NoComponentProvider);
        }
        Ok(component_provider.unwrap())
    }
}
}

If your plugin doesn't provide components:

#![allow(unused)]
fn main() {
impl Plugin for ComparisonPluginImpl {
    fn get_component_provider(&self) -> Result<Arc<dyn ComponentProvider>, PluginError> {
        Err(PluginError::NoComponentProvider)
    }
}
}

The same applies to all other providers:

  • ComponentProvider
  • EntityTypeProvider
  • RelationTypeProvider
  • EntityBehaviourProvider
  • RelationBehaviourProvider
  • FlowProvider
  • WebResourceProvider

Component Provider

Plugins can implement the trait ComponentProvider in order to register components during initialization of the plugin.

Trait ComponentProvider

#![allow(unused)]
fn main() {
impl ComponentProvider for ExampleComponentProviderImpl {
    fn get_components(&self) -> Vec<Component> {
        // Return a vector of components
    }
}
}

Use cases

  • Read Components from external JSON-file from a specific location
  • Build JSON-file into plugin binary using RustEmbed
  • Programmatically create Components

Entity Type Provider

Plugins can implement the trait EntityTypeProvider in order to register entity types during initialization of the plugin.

Trait EntityTypeProvider

#![allow(unused)]
fn main() {
impl EntityTypeProvider for ExampleEntityTypeProviderProviderImpl {
    fn get_entity_types(&self) -> Vec<EntityType> {
        // Return a vector of entity types
    }
}
}

Use cases

  • Read EntityTypes from external JSON-file from a specific location
  • Build JSON-file into plugin binary using RustEmbed
  • Programmatically create EntityTypes

Relation Type Provider

Plugins can implement the trait RelationTypeProvider in order to register relation types during initialization of the plugin.

Trait RelationTypeProvider

#![allow(unused)]
fn main() {
impl RelationTypeProvider for ExampleRelationTypeProviderProviderImpl {
    fn get_relation_types(&self) -> Vec<RelationType> {
        // Return a vector of relation types
    }
}
}

Use cases

  • Read RelationTypes from external JSON-file from a specific location
  • Build JSON-file into plugin binary using RustEmbed
  • Programmatically create RelationTypes

Flow Provider

Plugins can implement the trait FlowProvider in order to create a flow during initialization of the plugin.

Trait FlowProvider

#![allow(unused)]
fn main() {
impl FlowProvider for ExampleFlowProviderProviderImpl {
    fn get_flows(&self) -> Vec<Flow> {
        // Return a vector of flows
    }
}
}

Use cases

  • Read Flows from external JSON-file from a specific location
  • Build JSON-file into plugin binary using RustEmbed
  • Programmatically create Flows

Component Behaviour Provider

Entity Behaviour Provider

Trait EntityBehaviourProvider

#![allow(unused)]
fn main() {
const EXAMPLE: &'static str = "example";

#[wrapper]
pub struct ExampleStorage(std::sync::RwLock<std::collections::HashMap<Uuid, std::sync::Arc<Example>>>);

#[provides]
fn create_example_storage() -> ExampleStorage {
    ExampleStorage(std::sync::RwLock::new(std::collections::HashMap::new()))
}

#[async_trait]
pub trait ExampleEntityBehaviourProvider: EntityBehaviourProvider + Send + Sync {
    fn create_example(&self, entity_instance: Arc<ReactiveEntityInstance>);

    fn remove_example(&self, entity_instance: Arc<ReactiveEntityInstance>);

    fn remove_by_id(&self, id: Uuid);
}

pub struct ExampleEntityBehaviourProviderImpl {
    example_behaviours: ExampleStorage,
}

interfaces!(ExampleEntityBehaviourProviderImpl: dyn EntityBehaviourProvider);

#[component]
impl ExampleEntityBehaviourProviderImpl {
    #[provides]
    fn new() -> Self {
        Self {
            example_behaviours: create_example_storage(),
        }
    }
}

#[async_trait]
#[provides]
impl ExampleEntityBehaviourProvider for ExampleEntityBehaviourProviderImpl {
    fn create_example(&self, entity_instance: Arc<ReactiveEntityInstance>) {
        let id = entity_instance.id;
        let example_behaviour = Example::new(entity_instance);
        if example_behaviour.is_ok() {
            let example_behaviour = Arc::new(example_behaviour.unwrap());
            self.example_behaviours.0.write().unwrap().insert(id, example_behaviour);
            debug!("Added behaviour {} to entity instance {}", EXAMPLE, id);
        }
    }

    fn remove_example(&self, entity_instance: Arc<ReactiveEntityInstance>) {
        self.example_behaviours.0.write().unwrap().remove(&entity_instance.id);
        debug!("Removed behaviour {} from entity instance {}", EXAMPLE, entity_instance.id);
    }

    fn remove_by_id(&self, id: Uuid) {
        if self.example_behaviours.0.write().unwrap().contains_key(&id) {
            self.example_behaviours.0.write().unwrap().remove(&id);
            debug!("Removed behaviour {} from entity instance {}", EXAMPLE, id);
        }
    }
}

impl EntityBehaviourProvider for ExampleEntityBehaviourProviderImpl {
    fn add_behaviours(&self, entity_instance: Arc<ReactiveEntityInstance>) {
        match entity_instance.clone().type_name.as_str() {
            EXAMPLE => self.create_example(entity_instance),
            _ => {}
        }
    }

    fn remove_behaviours(&self, entity_instance: Arc<ReactiveEntityInstance>) {
        match entity_instance.clone().type_name.as_str() {
            EXAMPLE => self.remove_example(entity_instance),
            _ => {}
        }
    }

    fn remove_behaviours_by_id(&self, id: Uuid) {
        self.remove_by_id(id);
    }
}
}

Relation Behaviour Provider

Web Resource Provider

Plugins can implement the trait WebResourceProvider in order to register handlers for endpoints.

Use Cases

  • Deliver static files & web applications
  • Provide REST endpoints

Trait WebResourceProvider

#![allow(unused)]
fn main() {
impl WebResourceProvider for ExampleWebResourceProviderImpl {
    fn get_context_path(&self) -> String {
        String::from("example")
    }

    fn handle_web_resource(
        &self,
        path: String,
        _request: Request<HttpBody>,
    ) -> Result<Response<HttpBody>> {
        let asset = FlowEditorWebResourceAsset::get(path.as_ref());
        match asset {
            Some(asset) => {
                let body: HttpBody = match asset.data {
                    Cow::Borrowed(bytes) => HttpBody::Binary(bytes.to_vec()),
                    Cow::Owned(bytes) => HttpBody::Binary(bytes.to_vec()),
                };
                let mime_type = from_path(path.as_str()).first_or_octet_stream();
                Response::builder()
                    .status(StatusCode::OK)
                    .header(CONTENT_TYPE, mime_type.to_string())
                    .body(body)
            }
            None => Response::builder()
                .status(StatusCode::NOT_FOUND)
                .body(HttpBody::None),
        }
    }
}
}

Plugins

NameDescription
ArithmeticProvides arithmetic gates and operations
AssetDownload and update assets
BaseProvides basic components and entity types
BinaryHandles binary data
ComparisonProvides comparison gates
ConfigLoad configuration files
ConnectorProvides property connectors
GraphQL ClientGraphQL client
GraphQL Schema VisualizationVisualization of the GraphQL schema
HTTPHTTP and JSONRPC
Input DeviceInput device handling
JSONHandles JSON arrays and objects
LogicalProvides logical operations
Meta DataMeta Data - Dublin Core, EXIF
NotificationCreate desktop notifications
NumericNumeric operations
RandomGenerate random numbers
SchedulerTimers and scheduled jobs
StringProvides string operations
System CommandExecutes OS commands
System EnvironmentProvides environment variables
TaxonomyTaxonomy - categories and tags
ValueValues and state management

Plugin Arithmetic

This plugin provides arithmetic gates and operations.

Components

NamePropertiesDataTypeSocketTypeDescription
arithmetic_operationlhsnumberinput
resultnumberoutput
arithmetic_gatelhsnumberinput
rhsnumberinput
resultnumberoutput

Entity Types

NameComponentsPropertiesDataTypeSocketTypeDescription
addarithmetic_gatelhsnumberinputAddition
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
counteractiontriggerboolinputIf triggered, the result will be incremented by 1
actionresultnumberoutput
decrementarithmetic_operationlhsnumberinputDecrements the input by 1
arithmetic_operationresultnumberoutput
divarithmetic_gatelhsnumberinputDivision
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
incrementarithmetic_operationlhsnumberinputIncrements the input by 1
arithmetic_operationresultnumberoutput
maxarithmetic_gatelhsnumberinputMax value
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
minarithmetic_gatelhsnumberinputMin value
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
modarithmetic_gatelhsnumberinputModulo
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
mularithmetic_gatelhsnumberinputMultiplication
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput
subarithmetic_gatelhsnumberinputSubtraction
arithmetic_gaterhsnumberinput
arithmetic_gateresultnumberoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-arithmetichttps://github.com/inexorgame/inexor-rgf-plugin-arithmetic

Usage



Asset

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-assethttps://github.com/inexorgame/inexor-rgf-plugin-asset

Usage

Plugin Base

Basic components and entity types.

Components

NamePropertiesDataTypeSocketTypeDescription
namednamestringNone
describabledescriptionstringNone
flow_2df2dxnumberNone
f2dynumberNone
f2dwnumberNone
f2dhnumberNone
flow_3df3dxnumberNone
f3dynumberNone
f3dznumberNone
f3dwnumberNone
f3dhnumberNone
f3ddnumberNone
licensedlicensestringNoneThe SPDX license identifier. See: https://spdx.org/licenses/
attributionstringNoneTitle, author, source and license. Best practices for attribution: https://wiki.creativecommons.org/wiki/best_practices_for_attribution
versionedversionstringNoneThe version number. Use semantic versioning. See: https://semver.org/

Entity Types

NamePropertiesDataTypeSocketTypeDescription
Generic Flow

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-basehttps://github.com/inexorgame/inexor-rgf-plugin-base

Plugin Binary

This plugin provides two entity types for loading binary data from a file into a property as a Data-URL with BASE64 encoding and for storing da property which contains a Data-URL with BASE64 encoding into a file.

This is the base for loading textures, sounds, maps and any other type of binary data.

Data URL

Example of a data URL:

data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
vr4MkhoXe0rZigAAAABJRU5ErkJggg==

Components

NamePropertyData TypeSocket TypeDescription
binary_datadata_urlstringnoneData-URL, BASE64 encoded

Entity Types

Feed the streams with binary data from files.

NameComponentPropertyData TypeSocket TypeDescription
LoadBinaryfilenamestringinputThe filename to load the binary data from
binary_datadata_urlstringoutputData-URL, BASE64 encoded
SaveBinaryfilenamestringinputThe filename to store the binary data into
binary_datadata_urlstringinputData-URL, BASE64 encoded

Web Resources

Download binary resources via HTTP.

HTTP MethodResource PatternDescription
GET/binary/entities/{uuid}/{property_name}Converts the Data-URL into a binary data and returns it as web resource
GET/binary//entities/label/*labelConverts the Data-URL into a binary data and returns it as web resource

Examples

RequestEntity InstanceProperty Name
GET /binary/entity/dce4bd25-7b25-4a6a-8567-5429a2b3a101/data_urlid=dce4bd25-7b25-4a6a-8567-5429a2b3a101data_url
GET /binary/entity/label/org/inexor/logo/data_urllabel=/org/inexor/logo/{:property}data_url

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-binaryhttps://github.com/inexorgame/inexor-rgf-plugin-binary

Resources

  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

Usage

Plugin Comparison

Components

NamePropertyData TypeSocket Type
comparison_gatelhsanyinput
rhsanyinput
resultbooloutput

Entity Types

NameComponentsDescription
equalscomparison_gateReturns true, if lhs and rhs are equal
greater_thancomparison_gateReturns true, if lhs is greater than rhs
greater_than_or_equalscomparison_gateReturns true, if lhs is greater than or equal to rhs
lower_thancomparison_gateReturns true, if lhs is lower than rhs
lower_than_or_equalscomparison_gateReturns true, if lhs is lower than or equal to rhs
not_equalscomparison_gateReturns true, if lhs and rhs are not equal

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-comparisonhttps://github.com/inexorgame/inexor-rgf-plugin-comparison

Usage

Plugin Config

Read configuration files (TOML) into an entity instance.

Entity Types

NamePropertiesData TypeSocket Type
config_filefilenamestringnone
configurationobjectoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-confighttps://github.com/inexorgame/inexor-rgf-plugin-config

Usage

Usage (GraphQL) {.tabset}

Read in a configuration

mutation {
  instances {
    entities {
      create(
        type: "config_file"
        id: "aed6c9b0-e495-4423-baeb-5597b66416f4"
        properties: [
          {
            name: "filename"
            value: "config/plugins.toml"
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "filename"
            "configuration"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Get all configurations

query getAllConfigs {
  instances {
    entities(type: "config_file") {
      id
      label
      description
      properties {
        name
        value
      }
    }
  }
}

"Reload" a configuration

By triggering the property `filename' the configuration file will be read again:

mutation reloadPluginsConfig {
  instances {
    entities {
      update(
        id: "aed6c9b0-e495-4423-baeb-5597b66416f4",
        properties: [
          {
            name: "filename",
            value: "config/plugins.toml"
          }
        ]
      ) {
        id
        type {
          name
        }
        properties {
          name
          value
        }
      }
    }
  }
}

Plugin Connector

What is a Connector?

A connector connects a property of the outbound entity instance with a property of the inbound entity instance and propagates the changes of the value.

During propagation a propagation function is called. The propagation function has one single input (the incoming value). Connectors of different types has different propagation functions.

The propagation function can only do simple things (like casting or logging) but in fact even these simple operations makes the control flow much simpler and much more readable.

How does a connector work?

The connector is a relation instance which connects two entity instances. The relation itself stores the names of the output property and the input property.

In theory, it's also possible to connect two properties of the same entity instance.

On construction the streams are connected.

On destruction of the connector, the stream will be removed.


Warning

  1. Connecting properties of the same entity instance is discouraged to prevent feedback loops
  2. No type checks are performed on construction (yet; you are responsible)
  3. There is no check about feedback loops (yet; you are responsible)
  4. Renaming the relation properties (outbound_property_name, inbound_property_name) doesn't have any effect (yet). You have to remove the old connector and recreate a new connector.

Components

NameDescriptionPropertiesData TypeSocket Type
connectorConnects two propertiesoutbound_property_namestringnone
inbound_property_namestringnone
bufferA buffer for FIFOs and interpolationbuffer_sizenumbernone
bufferarraynone
propagation_counterCounts connector propagations. This component can be applied on all types of connectorspropagation_countnumbernone

Relation Types

NameComponentsDescription
buffered_fifo_connectorconnectorThis connector propagates the first inserted value of the FIFO buffer with the given size
buffer
debounce_connectorconnectorThis connector propagates the value if and only if the value is different
debug_connectorconnectorThis connector logs the value before propagation (log level debug)
default_connectorconnectorThis is the default connector type, which simply does nothing than propagate the value
delay_connectorconnectorThis connector propagates the value after a given duration. This operation is blocking
numeric_interpolation_connectorconnectorThis connector propagates the average of the numeric elements in the buffer
buffer
parse_float_connectorconnectorThis connector parses a string value and propagates a float value
parse_int_connectorconnectorThis connector parses a string value and propagates a int value
to_string_connectorconnectorThis connector converts the value of any type to string before propagation
trace_connectorconnectorThis connector logs the value before propagation (log level trace)
increment_by_connectorconnectorThis connector adds the value of the outbound property to the value of the inbound property
decrement_by_connectorconnectorThis connector subtracts the value of the outbound property from the value of the inbound property

Future: More (useful) connectors

NameComponentsDescription
str_split_connectorconnectorA string is split into tokens. Propagates an JSON array of string tokens
str_join_connectorconnectorJoins an array of strings and propagates the resulting string

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-connectorhttps://github.com/inexorgame/inexor-rgf-plugin-connector

Usage

Usage

GraphQL: Create a new connector

mutation {
  instances {
    relations {
      createConnector: create(
        edgeKey: {
          outboundId: "dc82735e-28ec-4c24-aedb-d968b73b288f",
          typeName: "default_connector--value--value"
          inboundId: "4cf8e6d8-f92e-4ffa-9610-ec0635f55e08",
        },
        properties: [
          {
            name: "outbound_property_name",
            value: "value"
          },
          {
            name: "inbound_property_name",
            value: "value"
          }
        ]
      ) {
        type {
          name
          fullName
        },
        properties(
          names: [
            "outbound_property_name",
            "inbound_property_name"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Plugin GraphQL Client

This plugin provides a web based GraphQL client.

Altair

This plugin uses Altair.

Altair GraphQL Client helps you debug GraphQL queries and implementations - taking care of the hard part so you can focus on actually getting things done.

Btw: You can also use Altair as standalone application or as firefox addon.

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repositories

NameRepository
inexor-rgf-plugin-graphql-clienthttps://github.com/inexorgame/inexor-rgf-plugin-graphql-client
Altair GraphQL Clienthttps://github.com/altair-graphql/altair

Plugin GraphQL Schema Visualization

This plugin represents the GraphQL API as an interactive graph.

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-graphql-schema-visualizationhttps://github.com/inexorgame/inexor-rgf-plugin-graphql-schema-visualization

Plugin: HTTP

This plugin provides a reactive http client integration. Using entities of type http requests via HTTP can be made.

With this plugin it's possible to integrate external services into your home automation.

Entity Types

NamePropertyData TypeSocket Type
httpurlstringinput
methodstringinput
request_headersobjectinput
payloadobjectinput
response_headersobjectoutput
resultobjectoutput
statusnumberoutput
jsonrpcurlstringinput
jsonrpc_versionstringinput
methodstringnone
paramsobjectinput
resultobjectoutput
errorobjectoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-httphttps://github.com/inexorgame/inexor-rgf-plugin-http

Usage

Usage

GraphQL

Create HTTP Request

First create an entity instance of type http and specify the URL and the method.

How to execute the HTTP Request

By creating the entity instance no request will be made. The next example explains how to execute the request.

mutation {
  instances {
    entities {
      create(
        type: "http",
        id: "a3370278-b05c-4d1a-ad57-cabd575c37e4",
        properties: [
          {
            name: "url",
            value: "https://api.sunrise-sunset.org/json?lat=47.557400&lng=9.707209&formatted=0"
          },
          {
            name: "method",
            value: "GET"
          },
          {
            name: "request_headers",
            value: {}
          },
          {
            name: "payload",
            value: {}
          },
          {
            name: "response_headers",
            value: {}
          },
          {
            name: "result",
            value: {}
          },
          {
            name: "status",
            value: 500
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "url",
            "method",
            "request_headers",
            "payload",
            "response_headers",
            "result",
            "status"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Execute HTTP Request

By triggering the property payload the HTTP request will be executed and the results will be written into the property result.

mutation {
  instances {
    entities {
      update(
        id: "a3370278-b05c-4d1a-ad57-cabd575c37e4",
        properties: [
          {
            name: "payload",
            value: {}
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "url",
            "method",
            "request_headers",
            "payload",
            "response_headers",
            "result",
            "status"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Plugin: Input Device

This plugin makes input devices (keyboard, mouse, gamepad, remote control...) available as entities using linux evdev.

Use cases

  • Global Hotkeys
    • Running basic functions (server list, chat, ...) in background
    • No window have to be open or minimized
  • Home Automation
    • Use a real keyboard (for example a wireless numpad keyboards) as interface to control flows
  • Robotics
    • Control your robot with keys or a 3d mouse

Event Types

  • Key (List)
    • Keyboard Key
    • Mouse Button
  • Led (List)
    • Keyboard Led
  • Relative Axis (List)
    • Mouse Position XY
    • Mouse Wheels
    • Volume Wheels on Keyboards
  • Absolute Axis (List)
    • Joysticks
  • Switch (List)
    • Lid shut
    • Microphone Jack inserted
    • Camera lens covered
    • Front proximity sensor active
    • Cover closed

Entity Instances

Entity instances represents a key/led/... and each can be addressed using a stable UUID (by device name and key/led/... id) or a stable human-readable label.

query getKeyStateF7 {
  instances {
    entities(label: "/org/inexor/input/any_device/key/key_f7") {
      label
      properties(name: "key_down") {
        value
      }
    }
  }
}

"Specific Device" and "Any Device"

Each event is forwarded twice:

DeviceEventEntity Instance Label
keyboard1key_f7
  • /org/inexor/input/keyboard1/key/key_f7
  • /org/inexor/input/any_device/key/key_f7
keyboard2key_f7
  • /org/inexor/input/keyboard1/key/key_f7
  • /org/inexor/input/any_device/key/key_f7

This allows you to address a key/led/... event on a specific device or the key/led/... event on all devices.

Type System

Inexor

Entity Types

NamePropertyData TypeSocket Type
InputDevicenamestringoutput
eventobjectoutput
physical_pathstringoutput
driver_versionstringoutput
vendornumberoutput
productnumberoutput
versionnumberoutput
InputDeviceKeykeystringnone
key_codenumbernone
key_downbooloutput
InputDeviceLedledstringnone
led_typenumbernone
statebooloutput
InputDeviceRelativeAxisrelative_axisstringnone
relative_axis_typenumbernone
statenumberoutput
InputDeviceAbsoluteAxisabsolute_axisstringnone
absolute_axis_typenumbernone
statenumberoutput
InputDeviceSwitchswitchstringnone
switch_typenumbernone
statenumberoutput

Relation Types

NameOutbound Entity TypeInbound Entity Type
KeyEventInputDeviceInputDeviceKey
LedEventInputDeviceInputDeviceLed
RelativeAxisEventInputDeviceInputDeviceRelativeAxis
AbsoluteAxisEventInputDeviceInputDeviceAbsoluteAxis
SwitchEventInputDeviceInputDeviceSwitch

Entity Behaviours

NameDescription
InputDeviceStreams input events from evdev and sets the entity instance property event

Relation Behaviours

NameDescription
KeyEventPropagates input events and filters by event type (key event) and key code defined by the inbound entity instance
LedEventPropagates input events and filters by event type (led event) and led type defined by the inbound entity instance
RelativeAxisEventPropagates input events and filters by event type (relative axis event) and relative axis type defined by the inbound entity instance
AbsoluteAxisEventPropagates input events and filters by event type (absolute axis event) and absolute axis type defined by the inbound entity instance
SwitchEventPropagates input events and filters by event type (switch event) and switch type defined by the inbound entity instance

Rust Crate / Rust Reference

  • https://github.com/emberian/evdev/ (Linux only)
  • https://www.freedesktop.org/software/libevdev/doc/latest/

Platform Compatibility

PlatformCompatibility
Linux
MacOSx
Windowsx

Repository

NameRepository
inexor-rgf-plugin-input-devicehttps://github.com/inexorgame/inexor-rgf-plugin-input-device

Appendix: Input Device Types

Keys

A key changed state. A key, or button, is usually a momentary switch (in the circuit sense). It has two states: down, or up. There are events for when keys are pressed (become down) and released (become up). There are also “key repeats”, where multiple events are sent while a key is down.

NameDescription
KEY_RESERVED
KEY_ESC
KEY_1
KEY_2
KEY_3
KEY_4
KEY_5
KEY_6
KEY_7
KEY_8
KEY_9
KEY_0
KEY_MINUS
KEY_EQUAL
KEY_BACKSPACE
KEY_TAB
KEY_Q
KEY_W
KEY_E
KEY_R
KEY_T
KEY_Y
KEY_U
KEY_I
KEY_O
KEY_P
KEY_LEFTBRACE
KEY_RIGHTBRACE
KEY_ENTER
KEY_LEFTCTRL
KEY_A
KEY_S
KEY_D
KEY_F
KEY_G
KEY_H
KEY_J
KEY_K
KEY_L
KEY_SEMICOLON
KEY_APOSTROPHE
KEY_GRAVE
KEY_LEFTSHIFT
KEY_BACKSLASH
KEY_Z
KEY_X
KEY_C
KEY_V
KEY_B
KEY_N
KEY_M
KEY_COMMA
KEY_DOT
KEY_SLASH
KEY_RIGHTSHIFT
KEY_KPASTERISK
KEY_LEFTALT
KEY_SPACE
KEY_CAPSLOCK
KEY_F1
KEY_F2
KEY_F3
KEY_F4
KEY_F5
KEY_F6
KEY_F7
KEY_F8
KEY_F9
KEY_F10
KEY_NUMLOCK
KEY_SCROLLLOCK
KEY_KP7
KEY_KP8
KEY_KP9
KEY_KPMINUS
KEY_KP4
KEY_KP5
KEY_KP6
KEY_KPPLUS
KEY_KP1
KEY_KP2
KEY_KP3
KEY_KP0
KEY_KPDOT
KEY_ZENKAKUHANKAKU
KEY_102ND
KEY_F11
KEY_F12
KEY_RO
KEY_KATAKANA
KEY_HIRAGANA
KEY_HENKAN
KEY_KATAKANAHIRAGANA
KEY_MUHENKAN
KEY_KPJPCOMMA
KEY_KPENTER
KEY_RIGHTCTRL
KEY_KPSLASH
KEY_SYSRQ
KEY_RIGHTALT
KEY_LINEFEED
KEY_HOME
KEY_UP
KEY_PAGEUP
KEY_LEFT
KEY_RIGHT
KEY_END
KEY_DOWN
KEY_PAGEDOWN
KEY_INSERT
KEY_DELETE
KEY_MACRO
KEY_MUTE
KEY_VOLUMEDOWN
KEY_VOLUMEUP
KEY_POWER
KEY_KPEQUAL
KEY_KPPLUSMINUS
KEY_PAUSE
KEY_SCALE
KEY_KPCOMMA
KEY_HANGEUL
KEY_HANJA
KEY_YEN
KEY_LEFTMETA
KEY_RIGHTMETA
KEY_COMPOSE
KEY_STOP
KEY_AGAIN
KEY_PROPS
KEY_UNDO
KEY_FRONT
KEY_COPY
KEY_OPEN
KEY_PASTE
KEY_FIND
KEY_CUT
KEY_HELP
KEY_MENU
KEY_CALC
KEY_SETUP
KEY_SLEEP
KEY_WAKEUP
KEY_FILE
KEY_SENDFILE
KEY_DELETEFILE
KEY_XFER
KEY_PROG1
KEY_PROG2
KEY_WWW
KEY_MSDOS
KEY_COFFEE
KEY_DIRECTION
KEY_CYCLEWINDOWS
KEY_MAIL
KEY_BOOKMARKS
KEY_COMPUTER
KEY_BACK
KEY_FORWARD
KEY_CLOSECD
KEY_EJECTCD
KEY_EJECTCLOSECD
KEY_NEXTSONG
KEY_PLAYPAUSE
KEY_PREVIOUSSONG
KEY_STOPCD
KEY_RECORD
KEY_REWIND
KEY_PHONE
KEY_ISO
KEY_CONFIG
KEY_HOMEPAGE
KEY_REFRESH
KEY_EXIT
KEY_MOVE
KEY_EDIT
KEY_SCROLLUP
KEY_SCROLLDOWN
KEY_KPLEFTPAREN
KEY_KPRIGHTPAREN
KEY_NEW
KEY_REDO
KEY_F13
KEY_F14
KEY_F15
KEY_F16
KEY_F17
KEY_F18
KEY_F19
KEY_F20
KEY_F21
KEY_F22
KEY_F23
KEY_F24
KEY_PLAYCD
KEY_PAUSECD
KEY_PROG3
KEY_PROG4
KEY_DASHBOARD
KEY_SUSPEND
KEY_CLOSE
KEY_PLAY
KEY_FASTFORWARD
KEY_BASSBOOST
KEY_PRINT
KEY_HP
KEY_CAMERA
KEY_SOUND
KEY_QUESTION
KEY_EMAIL
KEY_CHAT
KEY_SEARCH
KEY_CONNECT
KEY_FINANCE
KEY_SPORT
KEY_SHOP
KEY_ALTERASE
KEY_CANCEL
KEY_BRIGHTNESSDOWN
KEY_BRIGHTNESSUP
KEY_MEDIA
KEY_SWITCHVIDEOMODE
KEY_KBDILLUMTOGGLE
KEY_KBDILLUMDOWN
KEY_KBDILLUMUP
KEY_SEND
KEY_REPLY
KEY_FORWARDMAIL
KEY_SAVE
KEY_DOCUMENTS
KEY_BATTERY
KEY_BLUETOOTH
KEY_WLAN
KEY_UWB
KEY_UNKNOWN
KEY_VIDEO_NEXT
KEY_VIDEO_PREV
KEY_BRIGHTNESS_CYCLE
KEY_BRIGHTNESS_AUTO
KEY_DISPLAY_OFF
KEY_WWAN
KEY_RFKILL
KEY_MICMUTE
BTN_0
BTN_1
BTN_2
BTN_3
BTN_4
BTN_5
BTN_6
BTN_7
BTN_8
BTN_9
BTN_LEFT
BTN_RIGHT
BTN_MIDDLE
BTN_SIDE
BTN_EXTRA
BTN_FORWARD
BTN_BACK
BTN_TASK
BTN_TRIGGER
BTN_THUMB
BTN_THUMB2
BTN_TOP
BTN_TOP2
BTN_PINKIE
BTN_BASE
BTN_BASE2
BTN_BASE3
BTN_BASE4
BTN_BASE5
BTN_BASE6
BTN_DEAD
BTN_SOUTH
BTN_EAST
BTN_C
BTN_NORTH
BTN_WEST
BTN_Z
BTN_TL
BTN_TR
BTN_TL2
BTN_TR2
BTN_SELECT
BTN_START
BTN_MODE
BTN_THUMBL
BTN_THUMBR
BTN_TOOL_PEN
BTN_TOOL_RUBBER
BTN_TOOL_BRUSH
BTN_TOOL_PENCIL
BTN_TOOL_AIRBRUSH
BTN_TOOL_FINGER
BTN_TOOL_MOUSE
BTN_TOOL_LENS
BTN_TOOL_QUINTTAP
BTN_TOUCH
BTN_STYLUS
BTN_STYLUS2
BTN_TOOL_DOUBLETAP
BTN_TOOL_TRIPLETAP
BTN_TOOL_QUADTAP
BTN_GEAR_DOWN
BTN_GEAR_UP
KEY_OK
KEY_SELECT
KEY_GOTO
KEY_CLEAR
KEY_POWER2
KEY_OPTION
KEY_INFO
KEY_TIME
KEY_VENDOR
KEY_ARCHIVE
KEY_PROGRAM
KEY_CHANNEL
KEY_FAVORITES
KEY_EPG
KEY_PVR
KEY_MHP
KEY_LANGUAGE
KEY_TITLE
KEY_SUBTITLE
KEY_ANGLE
KEY_ZOOM
KEY_MODE
KEY_KEYBOARD
KEY_SCREEN
KEY_PC
KEY_TV
KEY_TV2
KEY_VCR
KEY_VCR2
KEY_SAT
KEY_SAT2
KEY_CD
KEY_TAPE
KEY_RADIO
KEY_TUNER
KEY_PLAYER
KEY_TEXT
KEY_DVD
KEY_AUX
KEY_MP3
KEY_AUDIO
KEY_VIDEO
KEY_DIRECTORY
KEY_LIST
KEY_MEMO
KEY_CALENDAR
KEY_RED
KEY_GREEN
KEY_YELLOW
KEY_BLUE
KEY_CHANNELUP
KEY_CHANNELDOWN
KEY_FIRST
KEY_LAST
KEY_AB
KEY_NEXT
KEY_RESTART
KEY_SLOW
KEY_SHUFFLE
KEY_BREAK
KEY_PREVIOUS
KEY_DIGITS
KEY_TEEN
KEY_TWEN
KEY_VIDEOPHONE
KEY_GAMES
KEY_ZOOMIN
KEY_ZOOMOUT
KEY_ZOOMRESET
KEY_WORDPROCESSOR
KEY_EDITOR
KEY_SPREADSHEET
KEY_GRAPHICSEDITOR
KEY_PRESENTATION
KEY_DATABASE
KEY_NEWS
KEY_VOICEMAIL
KEY_ADDRESSBOOK
KEY_MESSENGER
KEY_DISPLAYTOGGLE
KEY_SPELLCHECK
KEY_LOGOFF
KEY_DOLLAR
KEY_EURO
KEY_FRAMEBACK
KEY_FRAMEFORWARD
KEY_CONTEXT_MENU
KEY_MEDIA_REPEAT
KEY_10CHANNELSUP
KEY_10CHANNELSDOWN
KEY_IMAGES
KEY_DEL_EOL
KEY_DEL_EOS
KEY_INS_LINE
KEY_DEL_LINE
KEY_FN
KEY_FN_ESC
KEY_FN_F1
KEY_FN_F2
KEY_FN_F3
KEY_FN_F4
KEY_FN_F5
KEY_FN_F6
KEY_FN_F7
KEY_FN_F8
KEY_FN_F9
KEY_FN_F10
KEY_FN_F11
KEY_FN_F12
KEY_FN_1
KEY_FN_2
KEY_FN_D
KEY_FN_E
KEY_FN_F
KEY_FN_S
KEY_FN_B
KEY_BRL_DOT1
KEY_BRL_DOT2
KEY_BRL_DOT3
KEY_BRL_DOT4
KEY_BRL_DOT5
KEY_BRL_DOT6
KEY_BRL_DOT7
KEY_BRL_DOT8
KEY_BRL_DOT9
KEY_BRL_DOT10
KEY_NUMERIC_0
KEY_NUMERIC_1
KEY_NUMERIC_2
KEY_NUMERIC_3
KEY_NUMERIC_4
KEY_NUMERIC_5
KEY_NUMERIC_6
KEY_NUMERIC_7
KEY_NUMERIC_8
KEY_NUMERIC_9
KEY_NUMERIC_STAR
KEY_NUMERIC_POUND
KEY_CAMERA_FOCUS
KEY_WPS_BUTTON
KEY_TOUCHPAD_TOGGLE
KEY_TOUCHPAD_ON
KEY_TOUCHPAD_OFF
KEY_CAMERA_ZOOMIN
KEY_CAMERA_ZOOMOUT
KEY_CAMERA_UP
KEY_CAMERA_DOWN
KEY_CAMERA_LEFT
KEY_CAMERA_RIGHT
KEY_ATTENDANT_ON
KEY_ATTENDANT_OFF
KEY_ATTENDANT_TOGGLE
KEY_LIGHTS_TOGGLE
BTN_DPAD_UP
BTN_DPAD_DOWN
BTN_DPAD_LEFT
BTN_DPAD_RIGHT
KEY_ALS_TOGGLE
KEY_BUTTONCONFIG
KEY_TASKMANAGER
KEY_JOURNAL
KEY_CONTROLPANEL
KEY_APPSELECT
KEY_SCREENSAVER
KEY_VOICECOMMAND
KEY_BRIGHTNESS_MIN
KEY_BRIGHTNESS_MAX
KEY_KBDINPUTASSIST_PREV
KEY_KBDINPUTASSIST_NEXT
KEY_KBDINPUTASSIST_PREVGROUP
KEY_KBDINPUTASSIST_NEXTGROUP
KEY_KBDINPUTASSIST_ACCEPT
KEY_KBDINPUTASSIST_CANCEL
BTN_TRIGGER_HAPPY1
BTN_TRIGGER_HAPPY2
BTN_TRIGGER_HAPPY3
BTN_TRIGGER_HAPPY4
BTN_TRIGGER_HAPPY5
BTN_TRIGGER_HAPPY6
BTN_TRIGGER_HAPPY7
BTN_TRIGGER_HAPPY8
BTN_TRIGGER_HAPPY9
BTN_TRIGGER_HAPPY10
BTN_TRIGGER_HAPPY11
BTN_TRIGGER_HAPPY12
BTN_TRIGGER_HAPPY13
BTN_TRIGGER_HAPPY14
BTN_TRIGGER_HAPPY15
BTN_TRIGGER_HAPPY16
BTN_TRIGGER_HAPPY17
BTN_TRIGGER_HAPPY18
BTN_TRIGGER_HAPPY19
BTN_TRIGGER_HAPPY20
BTN_TRIGGER_HAPPY21
BTN_TRIGGER_HAPPY22
BTN_TRIGGER_HAPPY23
BTN_TRIGGER_HAPPY24
BTN_TRIGGER_HAPPY25
BTN_TRIGGER_HAPPY26
BTN_TRIGGER_HAPPY27
BTN_TRIGGER_HAPPY28
BTN_TRIGGER_HAPPY29
BTN_TRIGGER_HAPPY30
BTN_TRIGGER_HAPPY31
BTN_TRIGGER_HAPPY32
BTN_TRIGGER_HAPPY33
BTN_TRIGGER_HAPPY34
BTN_TRIGGER_HAPPY35
BTN_TRIGGER_HAPPY36
BTN_TRIGGER_HAPPY37
BTN_TRIGGER_HAPPY38
BTN_TRIGGER_HAPPY39
BTN_TRIGGER_HAPPY40

LEDs

An LED was toggled.

NameDescription
LED_NUML
LED_CAPSL
LED_SCROLLL
LED_COMPOSE
LED_KANA
LED_SLEEPStand-by
LED_SUSPEND
LED_MUTE
LED_MISCGeneric indicator
LED_MAILMessage waiting
LED_CHARGINGExternal power connected

Relative Axes

Movement on a relative axis. There is no absolute coordinate frame, just the fact that there was a change of a certain amount of units. Used for things like mouse movement or scroll wheels.

NameDescription
REL_X
REL_Y
REL_Z
REL_RX
REL_RY
REL_RZ
REL_HWHEEL
REL_DIAL
REL_WHEEL
REL_MISC
REL_RESERVED
REL_WHEEL_HI_RES
REL_HWHEEL_HI_RES

Absolute Axes

Movement on an absolute axis. Used for things such as touch events and joysticks.

NameDescription
ABS_X
ABS_Y
ABS_Z
ABS_RX
ABS_RY
ABS_RZ
ABS_THROTTLE
ABS_RUDDER
ABS_WHEEL
ABS_GAS
ABS_BRAKE
ABS_HAT0X
ABS_HAT0Y
ABS_HAT1X
ABS_HAT1Y
ABS_HAT2X
ABS_HAT2Y
ABS_HAT3X
ABS_HAT3Y
ABS_PRESSURE
ABS_DISTANCE
ABS_TILT_X
ABS_TILT_Y
ABS_TOOL_WIDTH
ABS_VOLUME
ABS_MISC
ABS_MT_SLOTMT slot being modified
ABS_MT_TOUCH_MAJORMajor axis of touching ellipse
ABS_MT_TOUCH_MINORMinor axis (omit if circular)
ABS_MT_WIDTH_MAJORMajor axis of approaching ellipse
ABS_MT_WIDTH_MINORMinor axis (omit if circular)
ABS_MT_ORIENTATIONEllipse orientation
ABS_MT_POSITION_XCenter X touch position
ABS_MT_POSITION_YCenter Y touch position
ABS_MT_TOOL_TYPEType of touching device
ABS_MT_BLOB_IDGroup a set of packets as a blob
ABS_MT_TRACKING_IDUnique ID of the initiated contact
ABS_MT_PRESSUREPressure on contact area
ABS_MT_DISTANCEContact over distance
ABS_MT_TOOL_XCenter X tool position
ABS_MT_TOOL_YCenter Y tool position

Switches

Change in a switch value. Switches are boolean conditions and usually correspond to a toggle switch of some kind in hardware.

NameDescription
SW_LIDset = lid shut
SW_TABLET_MODEset = tablet mode
SW_HEADPHONE_INSERTset = inserted
SW_RFKILL_ALLrfkill master switch, type ‘any’
SW_MICROPHONE_INSERTset = inserted
SW_DOCKset = plugged into doc
SW_LINEOUT_INSERTset = inserted
SW_JACK_PHYSICAL_INSERTset = mechanical switch set
SW_VIDEOOUT_INSERTset = inserted
SW_CAMERA_LENS_COVERset = lens covered
SW_KEYPAD_SLIDEset = keypad slide out
SW_FRONT_PROXIMITYset = front proximity sensor active
SW_ROTATE_LOCKset = rotate locked/disabled
SW_LINEIN_INSERTset = inserted
SW_MUTE_DEVICEset = device disabled
SW_PEN_INSERTEDset = pen inserted
SW_MACHINE_COVERset = cover closed

Appendix: Shared Keys

Inexor

Inexor

Plugin: JSON

This plugin adds functionality to operate with complex data structures. Properties of entity instances or relation instances can have different data types. It's possible to store even complex data using the data types array and object. This is handy if you receive data from an MQTT endpoint or if you want to represent more complex data. But it makes it also necessary to unpack or pack these data in order to operate with it.

Entity Types

NamePropertyData TypeSocket Type
ArrayPusharrayarrayinput
to_be_added_valueanyinput
resultarrayoutput
ArrayPoparrayarrayinput
resultarrayoutput
removed_valueanyinput
ArrayGetByIndexarrayarrayinput
indexnumberoutput
resultanyoutput
ObjectSetPropertyobjectobjectinput
property_namestringinput
property_valueanyinput
resultobjectoutput
ObjectRemovePropertyobjectobjectinput
property_namestringinput
resultobjectoutput
removed_valueanyoutput
ObjectGetPropertyobjectobjectinput
property_namestringinput
resultanyoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-jsonhttps://github.com/inexorgame/inexor-rgf-plugin-json

Usage

Plugin Logical

This plugin provides logical gates and operations.

Components

NamePropertiesDataTypeSocketTypeDescription
conditionconditionboolinputAccepts a boolean condition and returns an result
resultanyoutputThe datatype may be overridden by the concrete entity type
actiontriggerboolinputTriggers an action when receiving a boolean true on the trigger property
resultanyoutputThe datatype may be overridden by the concrete entity type
generatortriggerbooloutputProduces triggers (sends boolean true signals)
logical_gatelhsboolinput
rhsboolinput
resultbooloutput
logical_operationlhsboolinput
resultbooloutput

Entity Types

NameComponentsPropertiesDataTypeSocketTypeDescription
notlogical_operationlhsboolinputNOT-Operation
logical_operationresultbooloutput
andlogical_gatelhsboolinputAND-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
nandlogical_gatelhsboolinputNAND-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
norlogical_gatelhsboolinputNOR-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
orlogical_gatelhsboolinputOR-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
xorlogical_gatelhsboolinputXOR-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
xnorlogical_gatelhsboolinputXNOR-Gate
logical_gaterhsboolinput
logical_gateresultbooloutput
if_then_elseconditionconditionboolinputEach time it's triggered, either the then-payload or the else-payload gets propagated
then_payloadanyinputWill be propagated if the condition is true
else_payloadanyinputWill be propagated if the condition is false
conditionresultanyoutput
toggleactiontriggerboolinputIf triggered the result will toggled
actionresultbooloutput
triggeractiontriggerboolinputIf triggered the payload will be copied to the result
payloadanyinput
actionresultanyoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-logicalhttps://github.com/inexorgame/inexor-rgf-plugin-logical

Plugin Meta Data

This plugin provides generic components for meta data.

Components

NamePropertiesDataTypeSocketType
dublin_coredc_languagestringnone
dc_titlestringnone
dc_subjectstringnone
dc_coveragestringnone
dc_descriptionstringnone
dc_identifierstringnone
dc_formatstringnone
dc_typestringnone
dc_creatorstringnone
dc_contributorstringnone
dc_datestringnone
dc_publisherstringnone
dc_relationstringnone
dc_rightsstringnone
dc_sourcestringnone

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repositories

NameRepository
inexor-rgf-plugin-metadatahttps://github.com/inexorgame/inexor-rgf-plugin-metadata

Usage

Plugin: Notification

Create desktop notifications.

Entity Types

NamePropertyData TypeSocket Type
DesktopNotificationshowboolinput
app_namestringinput
summarystringinput
bodystringinput
iconstringinput
timeoutnumberinput

Platform Compatibility

PlatformCompatibilityNotes
Linuxhttps://github.com/hoodie/notify-rust#linuxbsd-support
MacOShttps://github.com/hoodie/notify-rust#macos-support
Windowshttps://github.com/hoodie/notify-rust#windows-support

Repository

NameRepository
inexor-rgf-plugin-notificationhttps://github.com/inexorgame/inexor-rgf-plugin-notification

Usage

GraphQL

Create Desktop Notification

mutation {
  instances {
    entities {
      create(
        type: "desktop_notification",
        id: "dfe30808-9242-4af8-aced-556ffe617038",
        properties: [
          {
            name: "show",
            value: false
          },
          {
            name: "body",
            value: "Test"
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "show",
            "app_name",
            "summary",
            "body",
            "icon",
            "timeout"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Show Desktop Notification

The property show=true triggers the notification.

mutation {
  instances {
    entities {
      update(
        id: "dfe30808-9242-4af8-aced-556ffe617038",
        properties: [
          {
            name: "show",
            value: true
          },
          {
            name: "summary",
            value: "Important Message"
          },
          {
            name: "body",
            value: "Lorem Ipsum"
          },
          {
            name: "icon",
            value: "computer"
          },
          {
            name: "timeout",
            value: 1000
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "show",
            "app_name",
            "summary",
            "body",
            "icon",
            "timeout"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Plugin: Numeric

Numeric operations

Components

NamePropertyData TypeSocket Type
numeric_operationlhsnumberinput
resultnumberoutput
numeric_gatelhsnumberinput
rhsnumberinput
resultnumberoutput

Entity Types

NameComponentsDescription
absnumeric_operationComputes the absolute value
acosnumeric_operationComputes the arccosine of a number
acoshnumeric_operationInverse hyperbolic cosine function
asinnumeric_operationComputes the arcsine of a number
asinhnumeric_operationInverse hyperbolic sine function
atannumeric_operationComputes the arctangent of a number
atan2numeric_gateComputes the four quadrant arctangent in radians
atanhnumeric_operationInverse hyperbolic tangent function
cbrtnumeric_operationReturns the cube root of a number
ceilnumeric_operationReturns the smallest integer greater than or equal to a number
cosnumeric_operationComputes the cosine of a number (in radians)
coshnumeric_operationHyperbolic cosine function
expnumeric_operationReturns e^(input), (the exponential function)
exp2numeric_operationReturns 2^(input)
floornumeric_operationReturns the largest integer less than or equal to a number
fractnumeric_operationReturns the fractional part of a number
hypotnumeric_gateCalculates the length of the hypotenuse of a right-angle triangle given legs of length x and y
lnnumeric_operationReturns the natural logarithm of the number
lognumeric_gateReturns the logarithm of the number with respect to an arbitrary base
log2numeric_operationReturns the base 2 logarithm of the number
log10numeric_operationReturns the base 10 logarithm of the number
pownumeric_gateRaises a number to a power
recipnumeric_operationTakes the reciprocal (inverse) of a number, 1/x
roundnumeric_operationReturns the nearest integer to a number. Round half-way cases away from 0.0
signumnumeric_operationReturns a number that represents the sign of the input
sinnumeric_operationComputes the sine of a number (in radians)
sinhnumeric_operationHyperbolic sine function
sqrtnumeric_operationReturns the square root of a number
tannumeric_operationComputes the tangent of a number (in radians)
tanhnumeric_operationHyperbolic tangent function
to_degreesnumeric_operationConverts radians to degrees
to_radiansnumeric_operationConverts degrees to radians
truncnumeric_operationReturns the integer part of a number

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-numerichttps://github.com/inexorgame/inexor-rgf-plugin-numeric

Usage

Plugin: Random

Generate random numbers, strings, UUIDs or booleans.

Entity Types

NamePropertyData TypeSocket TypeNote
pseudo_random_numbertriggerboolinput
seednumberinputfixed, u64
resultnumberoutput
random_booltriggerboolinput
resultbooloutput
random_numbertriggerboolinput
resultnumberoutput
random_integer_within_rangetriggerboolinput
lownumberinputInclusive
highnumberinputExclusive
resultnumberoutput
random_stringtriggerboolinput
lengthnumberinput
resultstringoutput
random_uuidtriggerboolinput
resultstringoutputThe generated UUID as string representation

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-randomhttps://github.com/inexorgame/inexor-rgf-plugin-random

Usage

Plugin: Scheduler

Timers and scheduled jobs

Scheduled Jobs

A scheduled job triggers periodically using a cron expression.

Cron Expression

Comma separated values such as 5,8,10 represent more than one time value. So for example, a schedule of 0 2,14,26 * * * * would execute on the 2nd, 14th, and 26th minute of every hour.

Ranges can be specified with a dash. A schedule of 0 0 * 5-10 * * would execute once per hour but only on day 5 through 10 of the month.

Day of the week can be specified as an abbreviation or the full name. A schedule of 0 0 6 * * Sun,Sat would execute at 6am on Sunday and Saturday.

Note that the year may be omitted.

Examples

Cron Expressionsecminhourday of monthmonthday of weekyearDescription
* * * * * * ********Runs every second
0 2,14,26 * * * *02,14,26****Run once per hour but only on day 5 through 10 of the month
0 0 * 5-10 * *00*5-10**Run at 6am on Sunday and Saturday

Timers

A timer triggers after a given duration. The duration can be specified either as a number in milliseconds or as a string in ISO 8601 Duration Format.

ISO 8601 Duration Format

What is the ISO 8601 Duration Format?

Durations define the amount of intervening time in a time interval and are represented by the format P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W. In these representations, the [n] is replaced by the value for each of the date and time elements that follow the [n]. Leading zeros are not required, but the maximum number of digits for each element should be agreed to by the communicating parties. The capital letters P, Y, M, W, D, T, H, M, and S are designators for each of the date and time elements and are not replaced.

https://en.wikipedia.org/wiki/ISO_8601#Durations

Designators

DesignatorDescription
PP is the duration designator (for period) placed at the start of the duration representation.
YY is the year designator that follows the value for the number of years.
MM is the month designator that follows the value for the number of months.
WW is the week designator that follows the value for the number of weeks.
DD is the day designator that follows the value for the number of days.
TT is the time designator that precedes the time components of the representation.
HH is the hour designator that follows the value for the number of hours.
MM is the minute designator that follows the value for the number of minutes.
SS is the second designator that follows the value for the number of seconds.

Examples

DurationDescription
PT10SRepresents a duration of ten seconds.
PT1M30SRepresents a duration of one minute and thirty seconds.
PT1HRepresents a duration of one hour.
P1DT12HRepresents a duration of one day and twelve hours.
P3Y6M4DT12H30M5SRepresents a duration of three years, six months, four days, twelve hours, thirty minutes, and five seconds.

Entity Types

NameComponentPropertyData TypeSocket TypeDescription
scheduled_jobschedulestringinputCron Expression
generatortriggerbooloutput
timerdurationnumber
or
string
inputDuration in milliseconds or as ISO8601 Duration Format
generatortriggerbooloutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repositories

Usage

Usage

GraphQL: Create a new timer

mutation {
  instances {
    entities {
      create(
        type: "timer",
        id: "46e2ecd0-3e91-4205-99c9-d9543923a73a",
        properties: [
          {
            name: "duration",
            value: 1000
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "duration",
            "trigger"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

GraphQL: Update duration of an existing timer

mutation {
  instances {
    entities {
      update(
        id: "46e2ecd0-3e91-4205-99c9-d9543923a73a",
        properties: [
          {
            name: "duration",
            value: 1500
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "duration",
            "trigger"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Plugin: String

Components

NamePropertyData TypeSocket Type
string_bool_operationlhsstringinput
resultbooloutput
string_comparisonlhsstringinput
rhsstringinput
resultbooloutput
string_gatelhsstringinput
rhsstringinput
resultstringoutput
string_number_operationlhsstringinput
resultnumberoutput
string_operationlhsstringinput
resultstringoutput
string_string_number_gatelhsstringinput
rhsstringinput
resultnumberoutput

Entity Types / Behaviours

NameComponentDescription
camel_casestring_operationConverts the input to camel case
capitalizestring_operationConverts the first character of the input to upper case and convert the rest of the input to lower case
char_countstring_number_operationCounts the characters
char_count_graphemesstring_number_operationCounts the graphemes in the input string taking care of surrogate pairs and combining marks
chop_afterstring_operationReturns everything after the given search
chop_after_laststring_operationReturns everything after the last given search
chop_beforestring_operationReturns everything before the given search
chop_before_laststring_operationReturns everything before the last given search
chop_remove_prefixstring_operationExtracts the prefix from the input
chop_remove_suffixstring_operationExtracts the suffix from the input
concatstring_gateConcatenate lhs with rhs
containsstring_comparisonReturns true, if lhs contains rhs
count_substringsstring_string_number_gateCounts the number of substring appearances in the input string
count_unique_wordsstring_string_number_gateCounting occurrences of unique words in the input string. This function respects unicode
count_wordsstring_string_number_gateCounts the number of words in the input string
decapitalizestring_operationConverts the first character of the input to lower case and convert the rest of the input to lower case
ends_withstring_comparisonReturns true, if lhs ends with rhs
escape_htmlstring_operationEscapes HTML special characters
escape_regexpstring_operationEscapes the regular expression special characters
is_alphastring_bool_operationChecks whether the input string contains only alpha characters
is_alpha_digitstring_bool_operationChecks whether the input string contains contains only alpha and digit characters
is_blankstring_bool_operationChecks whether the input string is empty or contains only whitespaces
is_camel_casestring_bool_operationChecks whether the input string is camelCased
is_capitalizestring_bool_operationChecks whether the input string is capitalized and the rest of the input string is lower case
is_decapitalizestring_bool_operationChecks whether the input string is decapitalized and the rest of the input string is converted to lower case
is_digitstring_bool_operationChecks whether the input string contains only digit characters
is_emptystring_bool_operationChecks whether the input string is empty
is_kebab_casestring_bool_operationChecks whether the input string is kebab-cased
is_lower_firststring_bool_operationChecks whether the input string has the first character in lower case
is_lowercasestring_bool_operationChecks whether the input string has only lower case characters
is_numericstring_bool_operationChecks whether the input string is numeric
is_pascal_casestring_bool_operationChecks whether the input string is PascalCased
is_shouty_kebab_casestring_bool_operationChecks whether the input string is SHOUTY-KEBAB-CASED
is_shouty_snake_casestring_bool_operationChecks whether the input string is SHOUTY_SNAKE_CASED
is_snake_casestring_bool_operationChecks whether the input string is snake_cased
is_train_casestring_bool_operationChecks whether the input string is Train-Cased
is_title_casestring_bool_operationChecks whether the input string is a titlecased string and there is at least one character
is_upper_firststring_bool_operationChecks whether the input string has the first character in upper case
is_uppercasestring_bool_operationChecks whether the input string has only upper case characters
kebab_casestring_operationConverts the input to kebab case
lower_firststring_operationConverts the first character of the input to lower case
lowercasestring_operationConverts the input to lower case
pascal_casestring_operationConverts the input to pascal case
shouty_kebab_casestring_operationConverts the input to SHOUTY kebab case
shouty_snake_casestring_operationConverts the input to SHOUTY snake case
snake_casestring_operationConverts the input to snake case
starts_withstring_comparisonReturns true, if lhs starts with rhs
string_lengthstring_number_operationReturns the length of the input string
strip_html_tagsstring_operationStrips all HTML tags
swap_casestring_operationConverts the input to swap case
templatingRenders a template
title_casestring_operationConverts the input to title case
train_casestring_operationConverts the input to train case
trimstring_operationRemoves whitespace at the beginning and end of a string
trim_endstring_operationRemoves whitespace at the end of a string
trim_startstring_operationRemoves whitespace at the beginning of a string
unescape_htmlstring_operationUnescapes HTML special characters
upper_firststring_operationConverts the first character of the input to upper case
uppercasestring_operationConverts the input to upper case

Rust Crate / Rust Reference

  • https://doc.rust-lang.org/std/string/struct.String.html
  • https://docs.rs/voca_rs/latest/voca_rs/index.html

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-stringhttps://github.com/inexorgame/inexor-rgf-plugin-string

Usage

Plugin: System Command

This plugin enables system commands to be executed. This is useful in home automation applications, e.g. if you want to control a headless Raspberry Pi Zero.

It can be configured in which directory the system command is executed.

A system command is executed by triggering (i.e. changing) the spawn property. Please see the GraphQL example.

When a program runs, it writes to stdout and stderr. These outputs are available in the corresponding properties after the program has ended.

A word about safety

Only activate the plugin if you know what you are doing. Do not run the Reactive Graph Flow as root user, because the system command will be executed as the same user! The GraphQL endpoint should also be specially protected.

Entity Types

NamePropertyData TypeSocket Type
SystemCommandnamestringnone
current_dirstringnone
commandstringnone
spawnarrayinput
stdinstringinput
stdoutstringoutput
stderrstringoutput

Usage

GraphQL: Create System Command "List Current Directory"

The system command is only generated but not yet executed.

mutation {
  instances {
    entities {
      create(
        type: "system_command",
        id: "57cd91ba-b437-4ba9-b274-b5e1ad4abbe5",
        properties: [
          {
            name: "name",
            value: "List files in the current directory"
          },
          {
            name: "current_dir",
            value: "."
          },
          {
            name: "command",
            value: "ls"
          },
          {
            name: "spawn",
            value: []
          },
          {
            name: "stdin",
            value: ""
          },
          {
            name: "stdout",
            value: ""
          },
          {
            name: "stderr",
            value: ""
          }
        ]
      ) {
        id,
        type {name},
        properties(
          names: [
            "name",
            "current_dir",
            "command",
            "spawn",
            "stdin",
            "stdout",
            "stderr"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

GraphQL: Execute System Command "List Current Directory"

A system command is executed when the property spawn is changed. This property expects an array with arguments. If the command is to be executed without arguments, an empty array can be passed.

mutation {
  instances {
    entities {
      update(
        id: "57cd91ba-b437-4ba9-b274-b5e1ad4abbe5",
        properties: [
          {
            name: "spawn",
            value: [
              "-l",
              "-a"
            ]
          }
        ]
      ) {
        id,
        type {name},
        properties(
          names: [
            "name",
            "current_dir",
            "command",
            "spawn",
            "stdin",
            "stdout",
            "stderr"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-system-commandhttps://github.com/inexorgame/inexor-rgf-plugin-system-command

Plugin: System Environment

The plugin creates entity instances for each environment variable. As environment variables doesn't change at runtime this happens only at initialization.

UUIDs

Info

The UUIDs of the entity instances are stable.

Labels

Each entity instance which represents a system environment variable has a label.

System EnvLabel
$HOME/org/inexor/system/env/home
$PATH/org/inexor/system/env/path

Entity Types

NamePropertiesData TypeSocket Type
EnvVarnamestringnone
labelstringnone
valuestringoutput

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repository

NameRepository
inexor-rgf-plugin-system-environmenthttps://github.com/inexorgame/inexor-rgf-plugin-system-environment

Usage

Plugin: Taxonomy

This plugin provides a generic taxonomy system with categories and tags.

Use Cases

  • Categorization of assets
  • Categorization of servers
  • Categorization of teams

Categories and Tagging

Taxonomy

Components

NamePropertiesDataTypeSocketTypeDescription
weightedweightnumbernoneThe weight of a relation between two entity instances (Range from 0.0 to 1.0)

Entity Types

NameComponentsPropertiesDataTypeSocketTypeDescription
categorynamednamestringnoneThe name of the category
describabledescriptionstringnoneThe description of the category
tagnamednamestringnoneThe tag name

Relation Types

NameDescriptionComponentsSource Entity TypeTarget Entity Type
categorized_asweighted*category
has_subcategoryweightedcategorycategory
tagged_withweighted*tag

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repositories

NameRepository
inexor-rgf-plugin-taxonomyhttps://github.com/inexorgame/inexor-rgf-plugin-taxonomy

Usage

Plugin: Value

This plugin provides value and state components. States extends values with debounce mechanism. States are important for several use cases, for example user interfaces and digital twins.

State Management

The state management is an essential part of controlling external things which can change it's state by itself. An example is a user interface checkbox, which can be toggled by the user. The state of the checkbox should be reflected in the state property. On the other hand, the reactive graph flow should be able to change the state which should be reflected by the user interface checkbox. This double-binding requires that the old internal state is stored and the new state is compared with the old state. Debouncing the state is necessary to prevent feedback loops and undefined behaviour.

Use Cases

  • User Interfaces: Checkboxes, Switches, ...
  • States on remote systems (HTTP, GraphQL, MQTT)
  • Digital Twins

Components

ComponentPropertiesData TypeSocket TypeDescription
value_booleanvaluebooleanoutputA boolean value
value_numbervaluenumberoutputA numeric value
value_stringvaluestringoutputA string value
value_arrayvaluearrayoutputA array value
value_objectvalueobjectoutputA object value
state_booleanstatebooleannoneA boolean state
set_statebooleaninput
state_numberstatenumbernoneA numeric state
set_statenumberinput
state_stringstatestringnoneA string state
set_statestringinput
state_arraystatearraynoneA array state
set_statearrayinput
state_objectstateobjectnoneA object state
set_stateobjectinput
value_debugger_debugDebugger for values (log level debug)
value_debugger_traceDebugger for values (log level trace)
state_debugger_debugDebugger for states (log level debug)
state_debugger_traceDebugger for states (log level trace)

Entity Types

NameComponentsDescription
value_arrayvalue_arrayA array value
value_booleanvalue_booleanA boolean value
value_numbervalue_numberA numeric value
value_stringvalue_stringA string value
value_objectvalue_objectA object value
state_arrayvalue_arrayA array state
state_array
state_booleanvalue_booleanA boolean state
state_boolean
state_numbervalue_numberA numeric state
state_boolean
state_stringvalue_stringA string state
state_boolean
state_objectvalue_objectA object state
state_boolean

Platform Compatibility

PlatformCompatibility
Linux
MacOS
Windows

Repositories

NameRepository
inexor-rgf-plugin-valuehttps://github.com/inexorgame/inexor-rgf-plugin-value

Events

Events are (of course!) represented as reactive entity instances that have a component event. Since Inexor is already a reactive system it wouldn't make sense to implement another event system. So, basically the events are conventions rather than implementation.

Component

Entity types or relation types can be composed with the component event in order to have an property event.

ComponentPropertyData TypeSocket Type
EventeventAnyOutput

Events: System Events

System events are events that are emitted by the system itself. There is no other way to get this information than through these events.

Entity Types

Entity TypeComponentPropertyData TypeSocket Type
System EventeventeventAnyOutput
labeledlabelStringNone

Types of Events

LabelDescriptionPayload
/org/inexor/events/type/component/createdTriggered if a component has been createdName of the created component
/org/inexor/events/type/component/deletedTriggered if a component has been deletedName of the deleted component
/org/inexor/events/type/entity/createdTriggered if an entity type has been createdName of the created entity type
/org/inexor/events/type/entity/deletedTriggered if an entity type has been deletedName of the deleted entity type
/org/inexor/events/type/relation/createdTriggered if a relation type has been createdName of the created relation type
/org/inexor/events/type/relation/deletedTriggered if a relation type has been deletedName of the deleted relation type
/org/inexor/event/type/changedTriggered if the type system has changed
/org/inexor/events/instance/entity/createdTriggered if an entity instance has been createdUUID of the created entity instance
/org/inexor/events/instance/entity/deletedTriggered if an entity instance has been deletedUUID of the deleted entity instance
/org/inexor/events/instance/relation/createdTriggered if a relation instance has been createdEdge key of the created relation instance
/org/inexor/events/instance/relation/deletedTriggered if a relation instance has been deletedEdge key of the deleted relation instance
/org/inexor/events/flow/createdTriggered if a flow has been createdUUID of the created flow
/org/inexor/events/flow/deletedTriggered if a flow has been deletedUUID of the deleted flow

Label

Subscribing to these events is easily possible with a label.

GraphQL Subscription

It is possible to subscribe to these events via GraphQL subscription.

Transform and Zip Multiple Events

Instead of subscribing multiple events directly you can zip multiple events and subscribe to the result.

Get new components

subscription getSystemEventComponentCreated {
  entity(label: "/org/inexor/event/type/component/created", propertyName: "event") {
    name
    value
    type {
      dataType
      socketType
    }
  }
}

Get which components have been deleted

subscription getSystemEventComponentDeleted {
  entity(label: "/org/inexor/event/type/component/deleted", propertyName: "event") {
    name
    value
    type {
      dataType
      socketType
    }
  }
}

Get new flows

subscription getSystemEventFlowCreated {
  entity(label: "/org/inexor/event/instance/flow/created", propertyName: "event") {
    name
    value
    type {
      dataType
      socketType
    }
  }
}

Get which flows have been deleted

subscription getSystemEventCFlowDeleted {
  entity(label: "/org/inexor/event/instance/flow/deleted", propertyName: "event") {
    name
    value
    type {
      dataType
      socketType
    }
  }
}

Events: Shutdown

Shutting down

PropertyValueDescription
shutdowntrueBoolean: Shutdown immediately
shutdown5Numeric: Shutdown in 5 seconds

GraphQL

mutation {
  instances {
    entities {
      update(
        label: "/org/inexor/commands/core/shutdown"
        properties: [
          {
            name: "shutdown"
            value: 5
          }
        ]
      ) {
        id
        type {
          name
        }
        properties(
          names: [
            "shutdown"
          ]
        ) {
          name
          value
        }
      }
    }
  }
}

Reactive Graph Patterns

Reactive Graph Flow allows the best of three worlds:

  • Graph Databases => Graph Patterns
  • Flow Control System => Flow Patterns
NameDescription
Generators & ActionsGenerators and Actions
ConditionsConditions
Time SeriesTime Series

Generators and Actions

What is a generator?

A generator produces boolean trues and sends them via the output property trigger.

There are many types of generators:

  • A periodic timer
  • A scheduler
  • A changed file on the filesystem (fs_notify)
  • A random bool generator
  • An user which has pressed a button on the user interface
  • A physical event from an input device like a keystroke

What is a action?

A action is something which should be executed or processed only and only if the input property trigger receives a boolean true.

Combine triggers and actions within a single entity using components

An entity instance can have multiple components.

For example, you can combine file with fs_notify and load_binary.

By doing so, the trigger property is both:

  • A generator: The component fs_notify fires trigger when the file has been modified on the filesystem
  • A action: The component load_binary gets triggered immediately and loads the content of the file into a property

This example shows the great possibilities of the component system and how to use the generator-action-system. By combining components into one single entity instance, the behaviour of the entity instance can get more complex. At the same time a flow is more compact.

Another example is to combine a user-interface-button as generator with an update-the-asset-repository as an action.

Builders

The builder pattern is useful when you would otherwise require many constructors or where construction has side effects.

Cargo.toml

Don't forget to include the dependency:

[dependencies]
inexor-rgf-core-builder = { git = "https://github.com/inexorgame/inexor-rgf-core-builder.git" }

Component Builder

Constructs a new component programmatically.

#![allow(unused)]
fn main() {
fn build_component() -> Component {
    ComponentBuilder::new("namespace", "component_name")
        .description("A test component with several properties")
        // Provide properties
        .property("property_1", DataType::String)
        .property_from(PropertyType::new("property_2", DataType::Bool))
        .string_property("property_3")
        .bool_property("property_4")
        .number_property("property_5")
        .array_property("property_6")
        .object_property("property_7")
        .input_property("property_8", DataType::Bool)
        .output_property("property_9", DataType::Bool)
        // Provide extensions
        .extension("security", json!({
            roles: [
                "ADMIN"
            ]
        }))
        .build()
}
}

Register Component

The component is not yet registered.

Register the component using ComponentManager::register

Entity Type Builder

Constructs a new entity type programmatically.

#![allow(unused)]
fn main() {
fn build_entity_type() -> EntityType {
    EntityTypeBuilder::new("namespace", "entity_type_name")
        .description("A test entity type")
        // Compose with the labeled component
        .component("labeled")
        // Provide additional properties
        .property("property_1", DataType::String)
        .property_from(PropertyType::new("property_2", DataType::Bool))
        .string_property("property_3")
        .bool_property("property_4")
        .number_property("property_5")
        .input_property("property_6", DataType::Bool)
        .output_property("property_7", DataType::Bool)
        // Provide extensions
        .extension("usage", json!("ls [OPTION]... [FILE]..."))
        .build()
}
}

Register Entity Type

The entity type is not yet registered.

Register the entity type using EntityTypeManager::register

Relation Type Builder

Constructs a new relation type programmatically.

#![allow(unused)]
fn main() {
fn build_relation_type() -> RelationType {
    RelationTypeBuilder::new(
        // Outbound Entity Type
        "teleport",
        // Relation Type Name
        "teleports_to",
        // Inbound Entity Type
        "tele_destination"
    )
        .description("A relation type which connects a teleport with a tele destination. The property weight defines the probability")
        // Compose with the weighted component
        .component("weighted")
        // Provide additional properties
        .property("property_1", DataType::String)
        .property_from(PropertyType::new("property_2", DataType::Bool))
        .string_property("property_3")
        .bool_property("property_4")
        .number_property("property_5")
        .input_property("property_6", DataType::Bool)
        .output_property("property_7", DataType::Bool)
        // Provide extensions
        .extension("flow_arrow_color", json!("#ff7700"))
        .extension("flow_arrow_labeling", json!("teleports from {outbound.name} to {inbound.name}"))
        .build()
}
}

Register Relation Type

The relation type is not yet registered.

Register the relation type using RelationTypeManager::register

Entity Instance Builder

Constructs a new entity instance programmatically. This is the non-reactive variant.

#![allow(unused)]
fn main() {
fn build_entity_instance(type_name: String) -> EntityInstance {
    EntityInstanceBuilder::new(type_name)
        .id(id)
        .property("property_1", json!("value_1"))
        .get()
}

fn build_entity_instance_from_type(entity_type: EntityType) -> EntityInstance {
    EntityInstanceBuilder::from(entity_type)
        .id(id)
        .property("property_1", json!("value_1"))
        .get()
}
}

Create Reactive Entity Instance

The entity instance is non-reactive and not yet registered.

Create a reactive instance and register it using ReactiveEntityInstanceManager::create_reactive_instance

Relation Instance Builder

Constructs a new relation instance programmatically. This is the non-reactive variant.

Create Reactive Relation Instance

The relation instance is non-reactive and not yet registered.

Create a reactive instance and register it using ReactiveRelationInstanceManager::create_reactive_instance

Reactive Entity Instance Builder

Constructs a new reactive entity instance programmatically.

Register Reactive Entity Instance

The reactive entity instance is fully functional, but not yet registered.

Register the reactive entity instance using ReactiveEntityInstanceManager::register_reactive_instance

Reactive Relation Instance Builder

Constructs a new reactive relation instance programmatically.

Register Reactive Relation Instance

The reactive relation instance is fully functional, but not yet registered.

Register the reactive relation instance using ReactiveRelationInstanceManager::register_reactive_instance

Flow Builder

7. Development

Development / Supported Platforms

The inexor reactive graph flow is completely platform-agnostic, which allows it to run on various operating systems.

Microsoft Windows

We support x64 Microsoft Windows 8, 8.1 and 10.

We have build instructions for Windows.

Linux

We support x64/arm7 Linux.

We have specific build instructions for Ubuntu and Raspberry Pi 2-4.

If you have found a way to set it up for other Linux distributions, please open a pull request and let us know!

macOS and iOS

We do not support macOS or iOS yet.

Android

We also do not support Android yet.

Development / Build

Install build tools (rust and rustup)

Linux / Raspberry Pi / MacOS

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Rustup

rustup is an installer for the systems programming language Rust.

Windows

Please follow the instructions for Installing rustup on Windows

Install nightly toolchain

Once you have rustup up and running, please install the nightly toolchain.

rustup update nightly

Clone the repository

git clone https://github.com/inexorgame/inexor-rgf-application.git

Build the application

Build in development mode:

cd inexor-rgf-application
cargo build

Build in release mode (takes longer, smaller binaries):

cd inexor-rgf-application
cargo build --release

Cargo

Cargo is the Rust package manager. Cargo downloads your Rust package's dependencies, compiles your packages, makes distributable packages, and uploads them to crates.io, the Rust community’s package registry.

Run the application

cargo run

or:

cargo run --release

Checkout new version and rebuild

  1. Fetch the latest version from git:
    git pull
    
  2. (Optional) If you want a completely clean build
    cargo clean
    
  3. Update dependencies
    cargo update
    
  4. Build the application
    cargo build
    
  5. Repeat this for all plugins

Plugin API version must match

The application and the plugins must be compiled with the same version of the Plugin API!

Build plugins

Checkout and build the plugin

cd ..
git clone https://github.com/inexorgame/inexor-rgf-plugin-mqtt.git
cd inexor-rgf-plugin-mqtt

Build in development mode:

cargo build

Build in release mode:

cargo build --release

Configure plugin

Edit config/plugins.toml and add a section for the plugin. The name must match the crate name of the plugin. Specify the path to the dynamically linked library. The path can be either absolute or relative to the working directory of the application.

[[plugin]]
name = "inexor-rgf-plugin-mqtt"
active = true
path = "../inexor-rgf-plugin-mqtt/target/debug/libinexor_rgf_plugin_mqtt.so"

Artifact Location

Release builds are located in target/release instead of target/debug

Configure plugins

Please consult the documentation for configuring plugins

Plugin Compatibility

17:18:18.642 [ERROR] [main] inexor_rgf_application::implementation::plugin_registry_impl:198 - Cannot load plugin ../inexor-rgf-plugin-flow-manager/target/debug/libinexor_rgf_plugin_flow_manager.so because of a compiler version mismatch: rustc 1.61.0-nightly (expected: 1.63.0-nightly)

Rust Compiler

The plugins have to be compiled by the same rust compiler.

Development / Code Formatting

We use rustfmt. rustfmt is a tool for formatting Rust code according to style guidelines.

Install rustfmt

rustup component add rustfmt --toolchain nightly

Check formatting

cargo fmt --all -- --check

Reformat code

cargo fmt --all

Development / Binary File Size

Release Profiles

# Build binary with dev profile
cargo build
# Build binary with release profile
cargo build --release

Release Profiles

For more information about release profiles, please refer to:

Optimization Levels

For more information about rust compiler optimization levels, please have a look at

Strip Symbols

For more information about strip symbols, please see

Application (exe)

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
# 12MB -> 7.8MB
lto = true
# 7.8MB -> 4.5MB
strip = "symbols"

Plugin (.so/.dll)

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
# 12MB -> 7.8MB
lto = true
# 7.8MB -> 4.5MB
strip = "symbols"

Development / Packaging

cargo (Rust Package Manager)

cargo publish NOT_YET_AVAILABLE

snap (Linux)

  • https://snapcraft.io/docs/rust-plugin
  • https://snapcraft.io/docs/snap-confinement

Configuration Files

  • snapcraft.yaml
  • rust-toolchain.toml

Create Package

snapcraft

Install Package

The snap have to be installed with --devmode.

sudo snap install --devmode inexor-rgf-application_0.0.0_amd64.snap

Package Information

snap info --verbose inexor-rgf-application

snapcraft.yaml Examples

  • https://github.com/lenna-project/lenna-cli/blob/7c31c71d1dd060f0c922b3f8b5e87833b5c45600/snapcraft.yaml
  • https://github.com/mimblewimble/packaging/blob/af8f34c3a3055be8907a7a2c98cbf63e23e792e3/snap/snapcraft.yaml

Debian

(TODO)

  • https://crates.io/crates/cargo-deb

RPM

(TODO)

  • https://crates.io/crates/cargo-rpm

Arch

(TODO)

  • https://crates.io/crates/cargo-arch

(Windows)

(TODO)

  • https://crates.io/crates/msi

Development / Documentation

We use mdBook for this documentation.

Install

Please install these crates using cargo:

cargo install mdbook
cargo install mdbook-mermaid
cargo install mdbook-admonish
cargo install mdbook-preprocessor-graphql-playground
CrateDescription
mdbookCreate book from markdown files. Like Gitbook but implemented in Rust
mdbook-mermaidA preprocessor for mdbook to add mermaid support
mdbook-admonishA preprocessor for mdbook to add Material Design admonishments
mdbook-preprocessor-graphql-playgroundA preprocessor for mdbook to add GraphQL playgrounds

Build only

mdbook build

Build and watch documentation locally

mdbook serve --open