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:
Characteristics | Description |
---|---|
Directed Graph | Each relation points from one entity to another entity |
Typed Graph |
|
Property Graph |
|
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:
- Every property is a stream not only data
- Property streams can be subscribed and published
- 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-Socket
s
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 ComponentBehaviour
s, EntityBehaviours
and
RelationBehaviours
.
Type | Behaviour |
---|---|
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.
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
Raspberry Pi
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"
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"
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
- Edit
config/graphql.toml
Bind address and port
Configure the bind address and port:
hostname = "localhost"
port = 31415
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
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"
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"
https://docs.rs/actix-web/latest/actix_web/middleware/struct.Logger.html#format
Configure Plugins
Edit config/plugins.toml
You can activate or deactivate plugins with the setting active
. Remember that some plugins depend on
other plugins.
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"
- 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"
- 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:
Type | Instance |
---|---|
Entity Type | Entity Instance |
Relation Type | Relation Instance |
Entity Type | Flow 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
Field | DataType | Description | Example |
---|---|---|---|
Name | String | The name of the component | labeled |
Description | String | Textual description of the component | The label is an hierarchical path with static segments, named parameters and catch-all parameters. |
Properties | Vec<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
Field | DataType | Description | Example |
---|---|---|---|
Name | String | The name of the entity type | player |
Namespace | String | The namespace | logical_gates |
Description | String | Textual description of the entity type | |
Components | Vec<Component> | The components which composes the entity type. These provides additional properties | |
Properties | Vec<Property Type> | The additional properties on entity instances | |
Extensions | Vec<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
Field | DataType | Description |
---|---|---|
Outbound Type | EntityType | The entity type of the outbound entity instance |
Name | String | The name of the relation type |
Full Name | String | The full name of the relation type |
Inbound Type | EntityType | The entity type of the inbound entity instance |
Namespace | String | The namespace |
Description | String | Textual description of the entity type |
Components | Vec<Component> | The components which composes the relation type. These provides additional properties |
Properties | Vec<Property Type> | The additional properties on relation instances |
Extensions | Vec<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
Field | DataType | Description | Example |
---|---|---|---|
Name | String | The name of the property | name |
Description | String | Textual description of the property | |
DataType | DataType | The data type of the property | |
SocketType | SocketType | The socket type | |
Extensions | Vec<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.
Value | Description |
---|---|
Null | Represents a JSON null value |
Bool | Represents a JSON boolean |
Number | Represents a JSON number, whether integer or floating point |
String | Represents a JSON string |
Array | Represents a JSON array. |
Object | Represents a JSON object. |
Any | Represents 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.
Value | Description |
---|---|
None | The property doesn't act as input or output socket |
Input | The property acts as input socket and accepts incoming connections |
Output | The 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
Field | DataType | Description |
---|---|---|
Name | String | The name of the extension |
Extension | JSON | The 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
Extension | Description |
---|---|
dublin-core | Meta data |
flow_editor_palette | An entry for the palette of the flow editor |
flow_editor_shape | Definition of the shape of an entity instance in the flow editor |
type_graph_shape | Definition of the shape of an entity type in the type graph |
instance_graph_shape | Definition 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.
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.
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
Field | DataType | Description |
---|---|---|
Type | EntityType | The type of the entity instance |
ID | UUID | The unique identifier of the entity instance |
Label | String (optional) | The label of the entity instance |
Description | String | Textual description of the entity type |
Properties | Vec<Property Instance> | The properties of the entity instance |
Components | Vec | The currently applied components |
Behaviours | Vec | The 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
Field | DataType | Description |
---|---|---|
Outbound | EntityInstance | The outbound entity instance |
Type | RelationType | The relation type |
Inbound | EntityInstance | The inbound entity instance |
Description | String | Textual description of the relation instance |
Properties | Vec<Property Instance> | The properties |
Components | Vec | The currently applied components |
Behaviours | Vec | The 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
Field | DataType | Description |
---|---|---|
Name | String | The name of the property |
Value | Value | The value of the property |
Type | Property Type | The 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
Explore the GraphQL queries using the Plugin GraphQL Schema Visualization: https://hostname:31415/graphql-schema-visualization/graph/query
Mutations
Explore the GraphQL mutations using the Plugin GraphQL Schema Visualization https://hostname:31415/graphql-schema-visualization/graph/mutation
Subscriptions
Explore the GraphQL subscriptions using the Plugin GraphQL Schema Visualization: https://hostname:31415/graphql-schema-visualization/graph/subscription
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
Explore the subscriptions using the Plugin GraphQL Schema Visualization https://hostname:31415/graphql-schema-visualization/subscription
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
- It is easier to query or modify because you operate on real types not abstract types
- Queries and mutations are more readable
- 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
- Not everything is possible
- 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.
- 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.
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:
- Get a RwLock on the context
- Get a reference of the
EntityInstanceManager
usingget_entity_instance_manager
- Create the entity instance using a builder (non-reactive)
- 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
Component
s from external JSON-file from a specific location - Build JSON-file into plugin binary using RustEmbed
- Programmatically create
Component
s
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
EntityType
s from external JSON-file from a specific location - Build JSON-file into plugin binary using RustEmbed
- Programmatically create
EntityType
s
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
RelationType
s from external JSON-file from a specific location - Build JSON-file into plugin binary using RustEmbed
- Programmatically create
RelationType
s
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
Flow
s from external JSON-file from a specific location - Build JSON-file into plugin binary using RustEmbed
- Programmatically create
Flow
s
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
Name | Description |
---|---|
Arithmetic | Provides arithmetic gates and operations |
Asset | Download and update assets |
Base | Provides basic components and entity types |
Binary | Handles binary data |
Comparison | Provides comparison gates |
Config | Load configuration files |
Connector | Provides property connectors |
GraphQL Client | GraphQL client |
GraphQL Schema Visualization | Visualization of the GraphQL schema |
HTTP | HTTP and JSONRPC |
Input Device | Input device handling |
JSON | Handles JSON arrays and objects |
Logical | Provides logical operations |
Meta Data | Meta Data - Dublin Core, EXIF |
Notification | Create desktop notifications |
Numeric | Numeric operations |
Random | Generate random numbers |
Scheduler | Timers and scheduled jobs |
String | Provides string operations |
System Command | Executes OS commands |
System Environment | Provides environment variables |
Taxonomy | Taxonomy - categories and tags |
Value | Values and state management |
Plugin Arithmetic
This plugin provides arithmetic gates and operations.
Components
Name | Properties | DataType | SocketType | Description |
---|---|---|---|---|
arithmetic_operation | lhs | number | input | |
result | number | output | ||
arithmetic_gate | lhs | number | input | |
rhs | number | input | ||
result | number | output |
Entity Types
Name | Components | Properties | DataType | SocketType | Description |
---|---|---|---|---|---|
add | arithmetic_gate | lhs | number | input | Addition |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
counter | action | trigger | bool | input | If triggered, the result will be incremented by 1 |
action | result | number | output | ||
decrement | arithmetic_operation | lhs | number | input | Decrements the input by 1 |
arithmetic_operation | result | number | output | ||
div | arithmetic_gate | lhs | number | input | Division |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
increment | arithmetic_operation | lhs | number | input | Increments the input by 1 |
arithmetic_operation | result | number | output | ||
max | arithmetic_gate | lhs | number | input | Max value |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
min | arithmetic_gate | lhs | number | input | Min value |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
mod | arithmetic_gate | lhs | number | input | Modulo |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
mul | arithmetic_gate | lhs | number | input | Multiplication |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output | ||
sub | arithmetic_gate | lhs | number | input | Subtraction |
arithmetic_gate | rhs | number | input | ||
arithmetic_gate | result | number | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-arithmetic | https://github.com/inexorgame/inexor-rgf-plugin-arithmetic |
Usage
Asset
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-asset | https://github.com/inexorgame/inexor-rgf-plugin-asset |
Usage
Plugin Base
Basic components and entity types.
Components
Name | Properties | DataType | SocketType | Description |
---|---|---|---|---|
named | name | string | None | |
describable | description | string | None | |
flow_2d | f2dx | number | None | |
f2dy | number | None | ||
f2dw | number | None | ||
f2dh | number | None | ||
flow_3d | f3dx | number | None | |
f3dy | number | None | ||
f3dz | number | None | ||
f3dw | number | None | ||
f3dh | number | None | ||
f3dd | number | None | ||
licensed | license | string | None | The SPDX license identifier. See: https://spdx.org/licenses/ |
attribution | string | None | Title, author, source and license. Best practices for attribution: https://wiki.creativecommons.org/wiki/best_practices_for_attribution | |
versioned | version | string | None | The version number. Use semantic versioning. See: https://semver.org/ |
Entity Types
Name | Properties | DataType | SocketType | Description |
---|---|---|---|---|
Generic Flow |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-base | https://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
Name | Property | Data Type | Socket Type | Description |
---|---|---|---|---|
binary_data | data_url | string | none | Data-URL, BASE64 encoded |
Entity Types
Feed the streams with binary data from files.
Name | Component | Property | Data Type | Socket Type | Description |
---|---|---|---|---|---|
LoadBinary | filename | string | input | The filename to load the binary data from | |
binary_data | data_url | string | output | Data-URL, BASE64 encoded | |
SaveBinary | filename | string | input | The filename to store the binary data into | |
binary_data | data_url | string | input | Data-URL, BASE64 encoded |
Web Resources
Download binary resources via HTTP.
HTTP Method | Resource Pattern | Description |
---|---|---|
GET | /binary/entities/{uuid}/{property_name} | Converts the Data-URL into a binary data and returns it as web resource |
GET | /binary//entities/label/*label | Converts the Data-URL into a binary data and returns it as web resource |
Examples
Request | Entity Instance | Property Name |
---|---|---|
GET /binary/entity/dce4bd25-7b25-4a6a-8567-5429a2b3a101/data_url | id=dce4bd25-7b25-4a6a-8567-5429a2b3a101 | data_url |
GET /binary/entity/label/org/inexor/logo/data_url | label=/org/inexor/logo/{:property} | data_url |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-binary | https://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
Name | Property | Data Type | Socket Type |
---|---|---|---|
comparison_gate | lhs | any | input |
rhs | any | input | |
result | bool | output |
Entity Types
Name | Components | Description |
---|---|---|
equals | comparison_gate | Returns true, if lhs and rhs are equal |
greater_than | comparison_gate | Returns true, if lhs is greater than rhs |
greater_than_or_equals | comparison_gate | Returns true, if lhs is greater than or equal to rhs |
lower_than | comparison_gate | Returns true, if lhs is lower than rhs |
lower_than_or_equals | comparison_gate | Returns true, if lhs is lower than or equal to rhs |
not_equals | comparison_gate | Returns true, if lhs and rhs are not equal |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-comparison | https://github.com/inexorgame/inexor-rgf-plugin-comparison |
Usage
Plugin Config
Read configuration files (TOML) into an entity instance.
Entity Types
Name | Properties | Data Type | Socket Type |
---|---|---|---|
config_file | filename | string | none |
configuration | object | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-config | https://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
- Connecting properties of the same entity instance is discouraged to prevent feedback loops
- No type checks are performed on construction (yet; you are responsible)
- There is no check about feedback loops (yet; you are responsible)
- 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
Name | Description | Properties | Data Type | Socket Type |
---|---|---|---|---|
connector | Connects two properties | outbound_property_name | string | none |
inbound_property_name | string | none | ||
buffer | A buffer for FIFOs and interpolation | buffer_size | number | none |
buffer | array | none | ||
propagation_counter | Counts connector propagations. This component can be applied on all types of connectors | propagation_count | number | none |
Relation Types
Name | Components | Description |
---|---|---|
buffered_fifo_connector | connector | This connector propagates the first inserted value of the FIFO buffer with the given size |
buffer | ||
debounce_connector | connector | This connector propagates the value if and only if the value is different |
debug_connector | connector | This connector logs the value before propagation (log level debug) |
default_connector | connector | This is the default connector type, which simply does nothing than propagate the value |
delay_connector | connector | This connector propagates the value after a given duration. This operation is blocking |
numeric_interpolation_connector | connector | This connector propagates the average of the numeric elements in the buffer |
buffer | ||
parse_float_connector | connector | This connector parses a string value and propagates a float value |
parse_int_connector | connector | This connector parses a string value and propagates a int value |
to_string_connector | connector | This connector converts the value of any type to string before propagation |
trace_connector | connector | This connector logs the value before propagation (log level trace) |
increment_by_connector | connector | This connector adds the value of the outbound property to the value of the inbound property |
decrement_by_connector | connector | This connector subtracts the value of the outbound property from the value of the inbound property |
Future: More (useful) connectors
Name | Components | Description |
---|---|---|
str_split_connector | connector | A string is split into tokens. Propagates an JSON array of string tokens |
str_join_connector | connector | Joins an array of strings and propagates the resulting string |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-connector | https://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.
URL | API |
---|---|
http://hostname:31415/graphql-client/graph | GraphQL API |
http://hostname:31415/graphql-client/dynamic-graph | Dynamic Graph API |
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
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repositories
Name | Repository |
---|---|
inexor-rgf-plugin-graphql-client | https://github.com/inexorgame/inexor-rgf-plugin-graphql-client |
Altair GraphQL Client | https://github.com/altair-graphql/altair |
Plugin GraphQL Schema Visualization
This plugin represents the GraphQL API as an interactive graph.
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-graphql-schema-visualization | https://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
Name | Property | Data Type | Socket Type |
---|---|---|---|
http | url | string | input |
method | string | input | |
request_headers | object | input | |
payload | object | input | |
response_headers | object | output | |
result | object | output | |
status | number | output | |
jsonrpc | url | string | input |
jsonrpc_version | string | input | |
method | string | none | |
params | object | input | |
result | object | output | |
error | object | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-http | https://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.
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:
Device | Event | Entity Instance Label |
---|---|---|
keyboard1 | key_f7 |
|
keyboard2 | 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
Entity Types
Name | Property | Data Type | Socket Type |
---|---|---|---|
InputDevice | name | string | output |
event | object | output | |
physical_path | string | output | |
driver_version | string | output | |
vendor | number | output | |
product | number | output | |
version | number | output | |
InputDeviceKey | key | string | none |
key_code | number | none | |
key_down | bool | output | |
InputDeviceLed | led | string | none |
led_type | number | none | |
state | bool | output | |
InputDeviceRelativeAxis | relative_axis | string | none |
relative_axis_type | number | none | |
state | number | output | |
InputDeviceAbsoluteAxis | absolute_axis | string | none |
absolute_axis_type | number | none | |
state | number | output | |
InputDeviceSwitch | switch | string | none |
switch_type | number | none | |
state | number | output |
Relation Types
Name | Outbound Entity Type | Inbound Entity Type |
---|---|---|
KeyEvent | InputDevice | InputDeviceKey |
LedEvent | InputDevice | InputDeviceLed |
RelativeAxisEvent | InputDevice | InputDeviceRelativeAxis |
AbsoluteAxisEvent | InputDevice | InputDeviceAbsoluteAxis |
SwitchEvent | InputDevice | InputDeviceSwitch |
Entity Behaviours
Name | Description |
---|---|
InputDevice | Streams input events from evdev and sets the entity instance property event |
Relation Behaviours
Name | Description |
---|---|
KeyEvent | Propagates input events and filters by event type (key event) and key code defined by the inbound entity instance |
LedEvent | Propagates input events and filters by event type (led event) and led type defined by the inbound entity instance |
RelativeAxisEvent | Propagates input events and filters by event type (relative axis event) and relative axis type defined by the inbound entity instance |
AbsoluteAxisEvent | Propagates input events and filters by event type (absolute axis event) and absolute axis type defined by the inbound entity instance |
SwitchEvent | Propagates 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
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | x |
Windows | x |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-input-device | https://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.
Name | Description |
---|---|
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.
Name | Description |
---|---|
LED_NUML | |
LED_CAPSL | |
LED_SCROLLL | |
LED_COMPOSE | |
LED_KANA | |
LED_SLEEP | Stand-by |
LED_SUSPEND | |
LED_MUTE | |
LED_MISC | Generic indicator |
LED_MAIL | Message waiting |
LED_CHARGING | External 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.
Name | Description |
---|---|
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.
Name | Description |
---|---|
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_SLOT | MT slot being modified |
ABS_MT_TOUCH_MAJOR | Major axis of touching ellipse |
ABS_MT_TOUCH_MINOR | Minor axis (omit if circular) |
ABS_MT_WIDTH_MAJOR | Major axis of approaching ellipse |
ABS_MT_WIDTH_MINOR | Minor axis (omit if circular) |
ABS_MT_ORIENTATION | Ellipse orientation |
ABS_MT_POSITION_X | Center X touch position |
ABS_MT_POSITION_Y | Center Y touch position |
ABS_MT_TOOL_TYPE | Type of touching device |
ABS_MT_BLOB_ID | Group a set of packets as a blob |
ABS_MT_TRACKING_ID | Unique ID of the initiated contact |
ABS_MT_PRESSURE | Pressure on contact area |
ABS_MT_DISTANCE | Contact over distance |
ABS_MT_TOOL_X | Center X tool position |
ABS_MT_TOOL_Y | Center 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.
Name | Description |
---|---|
SW_LID | set = lid shut |
SW_TABLET_MODE | set = tablet mode |
SW_HEADPHONE_INSERT | set = inserted |
SW_RFKILL_ALL | rfkill master switch, type ‘any’ |
SW_MICROPHONE_INSERT | set = inserted |
SW_DOCK | set = plugged into doc |
SW_LINEOUT_INSERT | set = inserted |
SW_JACK_PHYSICAL_INSERT | set = mechanical switch set |
SW_VIDEOOUT_INSERT | set = inserted |
SW_CAMERA_LENS_COVER | set = lens covered |
SW_KEYPAD_SLIDE | set = keypad slide out |
SW_FRONT_PROXIMITY | set = front proximity sensor active |
SW_ROTATE_LOCK | set = rotate locked/disabled |
SW_LINEIN_INSERT | set = inserted |
SW_MUTE_DEVICE | set = device disabled |
SW_PEN_INSERTED | set = pen inserted |
SW_MACHINE_COVER | set = cover closed |
Appendix: Shared Keys
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
Name | Property | Data Type | Socket Type |
---|---|---|---|
ArrayPush | array | array | input |
to_be_added_value | any | input | |
result | array | output | |
ArrayPop | array | array | input |
result | array | output | |
removed_value | any | input | |
ArrayGetByIndex | array | array | input |
index | number | output | |
result | any | output | |
ObjectSetProperty | object | object | input |
property_name | string | input | |
property_value | any | input | |
result | object | output | |
ObjectRemoveProperty | object | object | input |
property_name | string | input | |
result | object | output | |
removed_value | any | output | |
ObjectGetProperty | object | object | input |
property_name | string | input | |
result | any | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-json | https://github.com/inexorgame/inexor-rgf-plugin-json |
Usage
Plugin Logical
This plugin provides logical gates and operations.
Components
Name | Properties | DataType | SocketType | Description |
---|---|---|---|---|
condition | condition | bool | input | Accepts a boolean condition and returns an result |
result | any | output | The datatype may be overridden by the concrete entity type | |
action | trigger | bool | input | Triggers an action when receiving a boolean true on the trigger property |
result | any | output | The datatype may be overridden by the concrete entity type | |
generator | trigger | bool | output | Produces triggers (sends boolean true signals) |
logical_gate | lhs | bool | input | |
rhs | bool | input | ||
result | bool | output | ||
logical_operation | lhs | bool | input | |
result | bool | output |
Entity Types
Name | Components | Properties | DataType | SocketType | Description |
---|---|---|---|---|---|
not | logical_operation | lhs | bool | input | NOT-Operation |
logical_operation | result | bool | output | ||
and | logical_gate | lhs | bool | input | AND-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
nand | logical_gate | lhs | bool | input | NAND-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
nor | logical_gate | lhs | bool | input | NOR-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
or | logical_gate | lhs | bool | input | OR-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
xor | logical_gate | lhs | bool | input | XOR-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
xnor | logical_gate | lhs | bool | input | XNOR-Gate |
logical_gate | rhs | bool | input | ||
logical_gate | result | bool | output | ||
if_then_else | condition | condition | bool | input | Each time it's triggered, either the then-payload or the else-payload gets propagated |
then_payload | any | input | Will be propagated if the condition is true | ||
else_payload | any | input | Will be propagated if the condition is false | ||
condition | result | any | output | ||
toggle | action | trigger | bool | input | If triggered the result will toggled |
action | result | bool | output | ||
trigger | action | trigger | bool | input | If triggered the payload will be copied to the result |
payload | any | input | |||
action | result | any | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-logical | https://github.com/inexorgame/inexor-rgf-plugin-logical |
Plugin Meta Data
This plugin provides generic components for meta data.
Components
Name | Properties | DataType | SocketType |
---|---|---|---|
dublin_core | dc_language | string | none |
dc_title | string | none | |
dc_subject | string | none | |
dc_coverage | string | none | |
dc_description | string | none | |
dc_identifier | string | none | |
dc_format | string | none | |
dc_type | string | none | |
dc_creator | string | none | |
dc_contributor | string | none | |
dc_date | string | none | |
dc_publisher | string | none | |
dc_relation | string | none | |
dc_rights | string | none | |
dc_source | string | none |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repositories
Name | Repository |
---|---|
inexor-rgf-plugin-metadata | https://github.com/inexorgame/inexor-rgf-plugin-metadata |
Usage
Plugin: Notification
Create desktop notifications.
Entity Types
Name | Property | Data Type | Socket Type |
---|---|---|---|
DesktopNotification | show | bool | input |
app_name | string | input | |
summary | string | input | |
body | string | input | |
icon | string | input | |
timeout | number | input |
Platform Compatibility
Platform | Compatibility | Notes |
---|---|---|
Linux | ✓ | https://github.com/hoodie/notify-rust#linuxbsd-support |
MacOS | ✓ | https://github.com/hoodie/notify-rust#macos-support |
Windows | ✓ | https://github.com/hoodie/notify-rust#windows-support |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-notification | https://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
Name | Property | Data Type | Socket Type |
---|---|---|---|
numeric_operation | lhs | number | input |
result | number | output | |
numeric_gate | lhs | number | input |
rhs | number | input | |
result | number | output |
Entity Types
Name | Components | Description |
---|---|---|
abs | numeric_operation | Computes the absolute value |
acos | numeric_operation | Computes the arccosine of a number |
acosh | numeric_operation | Inverse hyperbolic cosine function |
asin | numeric_operation | Computes the arcsine of a number |
asinh | numeric_operation | Inverse hyperbolic sine function |
atan | numeric_operation | Computes the arctangent of a number |
atan2 | numeric_gate | Computes the four quadrant arctangent in radians |
atanh | numeric_operation | Inverse hyperbolic tangent function |
cbrt | numeric_operation | Returns the cube root of a number |
ceil | numeric_operation | Returns the smallest integer greater than or equal to a number |
cos | numeric_operation | Computes the cosine of a number (in radians) |
cosh | numeric_operation | Hyperbolic cosine function |
exp | numeric_operation | Returns e^(input), (the exponential function) |
exp2 | numeric_operation | Returns 2^(input) |
floor | numeric_operation | Returns the largest integer less than or equal to a number |
fract | numeric_operation | Returns the fractional part of a number |
hypot | numeric_gate | Calculates the length of the hypotenuse of a right-angle triangle given legs of length x and y |
ln | numeric_operation | Returns the natural logarithm of the number |
log | numeric_gate | Returns the logarithm of the number with respect to an arbitrary base |
log2 | numeric_operation | Returns the base 2 logarithm of the number |
log10 | numeric_operation | Returns the base 10 logarithm of the number |
pow | numeric_gate | Raises a number to a power |
recip | numeric_operation | Takes the reciprocal (inverse) of a number, 1/x |
round | numeric_operation | Returns the nearest integer to a number. Round half-way cases away from 0.0 |
signum | numeric_operation | Returns a number that represents the sign of the input |
sin | numeric_operation | Computes the sine of a number (in radians) |
sinh | numeric_operation | Hyperbolic sine function |
sqrt | numeric_operation | Returns the square root of a number |
tan | numeric_operation | Computes the tangent of a number (in radians) |
tanh | numeric_operation | Hyperbolic tangent function |
to_degrees | numeric_operation | Converts radians to degrees |
to_radians | numeric_operation | Converts degrees to radians |
trunc | numeric_operation | Returns the integer part of a number |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-numeric | https://github.com/inexorgame/inexor-rgf-plugin-numeric |
Usage
Plugin: Random
Generate random numbers, strings, UUIDs or booleans.
Entity Types
Name | Property | Data Type | Socket Type | Note |
---|---|---|---|---|
pseudo_random_number | trigger | bool | input | |
seed | number | input | fixed, u64 | |
result | number | output | ||
random_bool | trigger | bool | input | |
result | bool | output | ||
random_number | trigger | bool | input | |
result | number | output | ||
random_integer_within_range | trigger | bool | input | |
low | number | input | Inclusive | |
high | number | input | Exclusive | |
result | number | output | ||
random_string | trigger | bool | input | |
length | number | input | ||
result | string | output | ||
random_uuid | trigger | bool | input | |
result | string | output | The generated UUID as string representation |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-random | https://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 Expression | sec | min | hour | day of month | month | day of week | year | Description |
---|---|---|---|---|---|---|---|---|
* * * * * * * | * | * | * | * | * | * | * | Runs every second |
0 2,14,26 * * * * | 0 | 2,14,26 | * | * | * | * | Run once per hour but only on day 5 through 10 of the month | |
0 0 * 5-10 * * | 0 | 0 | * | 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
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.
Designators
Designator | Description |
---|---|
P | P is the duration designator (for period) placed at the start of the duration representation. |
Y | Y is the year designator that follows the value for the number of years. |
M | M is the month designator that follows the value for the number of months. |
W | W is the week designator that follows the value for the number of weeks. |
D | D is the day designator that follows the value for the number of days. |
T | T is the time designator that precedes the time components of the representation. |
H | H is the hour designator that follows the value for the number of hours. |
M | M is the minute designator that follows the value for the number of minutes. |
S | S is the second designator that follows the value for the number of seconds. |
Examples
Duration | Description |
---|---|
PT10S | Represents a duration of ten seconds. |
PT1M30S | Represents a duration of one minute and thirty seconds. |
PT1H | Represents a duration of one hour. |
P1DT12H | Represents a duration of one day and twelve hours. |
P3Y6M4DT12H30M5S | Represents a duration of three years, six months, four days, twelve hours, thirty minutes, and five seconds. |
Entity Types
Name | Component | Property | Data Type | Socket Type | Description |
---|---|---|---|---|---|
scheduled_job | schedule | string | input | Cron Expression | |
generator | trigger | bool | output | ||
timer | duration | number or string | input | Duration in milliseconds or as ISO8601 Duration Format | |
generator | trigger | bool | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repositories
Name | Repository |
---|---|
inexor-rgf-plugin-scheduler | https://github.com/inexorgame/inexor-rgf-plugin-scheduler |
tokio-cron-scheduler | https://github.com/mvniekerk/tokio-cron-scheduler |
cron expression parser | https://github.com/zslayton/cron |
ISO8601 duration parser | https://github.com/PoiScript/iso8601-duration |
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
Name | Property | Data Type | Socket Type |
---|---|---|---|
string_bool_operation | lhs | string | input |
result | bool | output | |
string_comparison | lhs | string | input |
rhs | string | input | |
result | bool | output | |
string_gate | lhs | string | input |
rhs | string | input | |
result | string | output | |
string_number_operation | lhs | string | input |
result | number | output | |
string_operation | lhs | string | input |
result | string | output | |
string_string_number_gate | lhs | string | input |
rhs | string | input | |
result | number | output |
Entity Types / Behaviours
Name | Component | Description |
---|---|---|
camel_case | string_operation | Converts the input to camel case |
capitalize | string_operation | Converts the first character of the input to upper case and convert the rest of the input to lower case |
char_count | string_number_operation | Counts the characters |
char_count_graphemes | string_number_operation | Counts the graphemes in the input string taking care of surrogate pairs and combining marks |
chop_after | string_operation | Returns everything after the given search |
chop_after_last | string_operation | Returns everything after the last given search |
chop_before | string_operation | Returns everything before the given search |
chop_before_last | string_operation | Returns everything before the last given search |
chop_remove_prefix | string_operation | Extracts the prefix from the input |
chop_remove_suffix | string_operation | Extracts the suffix from the input |
concat | string_gate | Concatenate lhs with rhs |
contains | string_comparison | Returns true, if lhs contains rhs |
count_substrings | string_string_number_gate | Counts the number of substring appearances in the input string |
count_unique_words | string_string_number_gate | Counting occurrences of unique words in the input string. This function respects unicode |
count_words | string_string_number_gate | Counts the number of words in the input string |
decapitalize | string_operation | Converts the first character of the input to lower case and convert the rest of the input to lower case |
ends_with | string_comparison | Returns true, if lhs ends with rhs |
escape_html | string_operation | Escapes HTML special characters |
escape_regexp | string_operation | Escapes the regular expression special characters |
is_alpha | string_bool_operation | Checks whether the input string contains only alpha characters |
is_alpha_digit | string_bool_operation | Checks whether the input string contains contains only alpha and digit characters |
is_blank | string_bool_operation | Checks whether the input string is empty or contains only whitespaces |
is_camel_case | string_bool_operation | Checks whether the input string is camelCased |
is_capitalize | string_bool_operation | Checks whether the input string is capitalized and the rest of the input string is lower case |
is_decapitalize | string_bool_operation | Checks whether the input string is decapitalized and the rest of the input string is converted to lower case |
is_digit | string_bool_operation | Checks whether the input string contains only digit characters |
is_empty | string_bool_operation | Checks whether the input string is empty |
is_kebab_case | string_bool_operation | Checks whether the input string is kebab-cased |
is_lower_first | string_bool_operation | Checks whether the input string has the first character in lower case |
is_lowercase | string_bool_operation | Checks whether the input string has only lower case characters |
is_numeric | string_bool_operation | Checks whether the input string is numeric |
is_pascal_case | string_bool_operation | Checks whether the input string is PascalCased |
is_shouty_kebab_case | string_bool_operation | Checks whether the input string is SHOUTY-KEBAB-CASED |
is_shouty_snake_case | string_bool_operation | Checks whether the input string is SHOUTY_SNAKE_CASED |
is_snake_case | string_bool_operation | Checks whether the input string is snake_cased |
is_train_case | string_bool_operation | Checks whether the input string is Train-Cased |
is_title_case | string_bool_operation | Checks whether the input string is a titlecased string and there is at least one character |
is_upper_first | string_bool_operation | Checks whether the input string has the first character in upper case |
is_uppercase | string_bool_operation | Checks whether the input string has only upper case characters |
kebab_case | string_operation | Converts the input to kebab case |
lower_first | string_operation | Converts the first character of the input to lower case |
lowercase | string_operation | Converts the input to lower case |
pascal_case | string_operation | Converts the input to pascal case |
shouty_kebab_case | string_operation | Converts the input to SHOUTY kebab case |
shouty_snake_case | string_operation | Converts the input to SHOUTY snake case |
snake_case | string_operation | Converts the input to snake case |
starts_with | string_comparison | Returns true, if lhs starts with rhs |
string_length | string_number_operation | Returns the length of the input string |
strip_html_tags | string_operation | Strips all HTML tags |
swap_case | string_operation | Converts the input to swap case |
templating | Renders a template | |
title_case | string_operation | Converts the input to title case |
train_case | string_operation | Converts the input to train case |
trim | string_operation | Removes whitespace at the beginning and end of a string |
trim_end | string_operation | Removes whitespace at the end of a string |
trim_start | string_operation | Removes whitespace at the beginning of a string |
unescape_html | string_operation | Unescapes HTML special characters |
upper_first | string_operation | Converts the first character of the input to upper case |
uppercase | string_operation | Converts 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
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-string | https://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.
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
Name | Property | Data Type | Socket Type |
---|---|---|---|
SystemCommand | name | string | none |
current_dir | string | none | |
command | string | none | |
spawn | array | input | |
stdin | string | input | |
stdout | string | output | |
stderr | string | output |
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
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-system-command | https://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
Labels
Each entity instance which represents a system environment variable has a label.
System Env | Label |
---|---|
$HOME | /org/inexor/system/env/home |
$PATH | /org/inexor/system/env/path |
Entity Types
Name | Properties | Data Type | Socket Type |
---|---|---|---|
EnvVar | name | string | none |
label | string | none | |
value | string | output |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repository
Name | Repository |
---|---|
inexor-rgf-plugin-system-environment | https://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
Components
Name | Properties | DataType | SocketType | Description |
---|---|---|---|---|
weighted | weight | number | none | The weight of a relation between two entity instances (Range from 0.0 to 1.0) |
Entity Types
Name | Components | Properties | DataType | SocketType | Description |
---|---|---|---|---|---|
category | named | name | string | none | The name of the category |
describable | description | string | none | The description of the category | |
tag | named | name | string | none | The tag name |
Relation Types
Name | Description | Components | Source Entity Type | Target Entity Type |
---|---|---|---|---|
categorized_as | weighted | * | category | |
has_subcategory | weighted | category | category | |
tagged_with | weighted | * | tag |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repositories
Name | Repository |
---|---|
inexor-rgf-plugin-taxonomy | https://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
Component | Properties | Data Type | Socket Type | Description |
---|---|---|---|---|
value_boolean | value | boolean | output | A boolean value |
value_number | value | number | output | A numeric value |
value_string | value | string | output | A string value |
value_array | value | array | output | A array value |
value_object | value | object | output | A object value |
state_boolean | state | boolean | none | A boolean state |
set_state | boolean | input | ||
state_number | state | number | none | A numeric state |
set_state | number | input | ||
state_string | state | string | none | A string state |
set_state | string | input | ||
state_array | state | array | none | A array state |
set_state | array | input | ||
state_object | state | object | none | A object state |
set_state | object | input | ||
value_debugger_debug | Debugger for values (log level debug) | |||
value_debugger_trace | Debugger for values (log level trace) | |||
state_debugger_debug | Debugger for states (log level debug) | |||
state_debugger_trace | Debugger for states (log level trace) |
Entity Types
Name | Components | Description |
---|---|---|
value_array | value_array | A array value |
value_boolean | value_boolean | A boolean value |
value_number | value_number | A numeric value |
value_string | value_string | A string value |
value_object | value_object | A object value |
state_array | value_array | A array state |
state_array | ||
state_boolean | value_boolean | A boolean state |
state_boolean | ||
state_number | value_number | A numeric state |
state_boolean | ||
state_string | value_string | A string state |
state_boolean | ||
state_object | value_object | A object state |
state_boolean |
Platform Compatibility
Platform | Compatibility |
---|---|
Linux | ✓ |
MacOS | ✓ |
Windows | ✓ |
Repositories
Name | Repository |
---|---|
inexor-rgf-plugin-value | https://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
.
Component | Property | Data Type | Socket Type |
---|---|---|---|
Event | event | Any | Output |
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 Type | Component | Property | Data Type | Socket Type |
---|---|---|---|---|
System Event | event | event | Any | Output |
labeled | label | String | None |
Types of Events
Label | Description | Payload |
---|---|---|
/org/inexor/events/type/component/created | Triggered if a component has been created | Name of the created component |
/org/inexor/events/type/component/deleted | Triggered if a component has been deleted | Name of the deleted component |
/org/inexor/events/type/entity/created | Triggered if an entity type has been created | Name of the created entity type |
/org/inexor/events/type/entity/deleted | Triggered if an entity type has been deleted | Name of the deleted entity type |
/org/inexor/events/type/relation/created | Triggered if a relation type has been created | Name of the created relation type |
/org/inexor/events/type/relation/deleted | Triggered if a relation type has been deleted | Name of the deleted relation type |
/org/inexor/event/type/changed | Triggered if the type system has changed | |
/org/inexor/events/instance/entity/created | Triggered if an entity instance has been created | UUID of the created entity instance |
/org/inexor/events/instance/entity/deleted | Triggered if an entity instance has been deleted | UUID of the deleted entity instance |
/org/inexor/events/instance/relation/created | Triggered if a relation instance has been created | Edge key of the created relation instance |
/org/inexor/events/instance/relation/deleted | Triggered if a relation instance has been deleted | Edge key of the deleted relation instance |
/org/inexor/events/flow/created | Triggered if a flow has been created | UUID of the created flow |
/org/inexor/events/flow/deleted | Triggered if a flow has been deleted | UUID of the deleted flow |
GraphQL Subscription
It is possible to subscribe to these events via GraphQL subscription.
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
Property | Value | Description |
---|---|---|
shutdown | true | Boolean: Shutdown immediately |
shutdown | 5 | Numeric: 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
Name | Description |
---|---|
Generators & Actions | Generators and Actions |
Conditions | Conditions |
Time Series | Time Series |
Generators and Actions
What is a generator?
A generator produces boolean true
s 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 componentfs_notify
firestrigger
when the file has been modified on the filesystem - A
action
: The componentload_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.
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() } }
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() } }
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() } }
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() } }
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.
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.
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.
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 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
Run the application
cargo run
or:
cargo run --release
Checkout new version and rebuild
- Fetch the latest version from git:
git pull
- (Optional) If you want a completely clean build
cargo clean
- Update dependencies
cargo update
- Build the application
cargo build
- Repeat this for all plugins
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"
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)
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
For more information about release profiles, please refer to:
For more information about rust compiler optimization levels, please have a look at
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
Crate | Description |
---|---|
mdbook | Create book from markdown files. Like Gitbook but implemented in Rust |
mdbook-mermaid | A preprocessor for mdbook to add mermaid support |
mdbook-admonish | A preprocessor for mdbook to add Material Design admonishments |
mdbook-preprocessor-graphql-playground | A preprocessor for mdbook to add GraphQL playgrounds |
Build only
mdbook build
Build and watch documentation locally
mdbook serve --open