Customizing Pragma Engine #
Customization is central to Pragma Engine’s architecture.
This system provides dynamically reloadable configuration, allowing users to configure a running service without requiring a service restart.
The platform start-up parameters accept a config file (or files) read from disk, and periodically polled for changes after. All defined config parameters must be supplied either as a baked-in default or through a configuration file, otherwise the platform will fail to start.
Config overrides and priority
The priority for configuration overrides are:
- Highest priority: config file
- Middle priority: embedded universal config overrides
- Lowest priority: code level default
Config defaults can be specified in code where the configuration values are defined, with the option to specify defaults for development or production. These defaults are overridden if the config value is specified in a higher priority format.
The universal config override file allows a studio to specify overrides without modifying code-level engine defaults. This is helpful when configuring standard values such as studio-specific plugins.
The platform periodically checks the config file for changes, and loads them if found. Not all configuration is safe to be dynamically reloaded, such as changing to a different database host. In these cases a service restart will be required.
Plugins and extension data #
Plugins and extension data are the backbone of Pragma Engine customization. They allow users to implement rich features with custom data and logic within the full fledged capabilities of the engine.
Plugins are defined as a Kotlin interface with an annotation specifying it as a plugin. Generally the engine will define a plugin to support a specific service, and it can then be implemented by users and configured for use within the plugin section of the config file. The engine handles loading and initializing plugins at runtime.
Plugins support custom configuration, allowing users to define their own configuration parameters.
MatchmakingStrategyplugin allows users to define specific matchmaking rules.
ItemInitializerplugin allows users to supply custom instance data for their player inventory data.
Extension data is closely tied to plugins and provide a standard way for users to define custom structured data within the engine.
Extension protobuf types are predefined by the engine and placed in a shared directory with no defined fields. This allows Pragma Engine code to reference, serialize, deserialize, store, and retrieve extension data. Engine code never inspects the internal fields, which allows them to be defined and managed by users with game-specific details.
In this way the engine handles validation, routing, and storage while the user is responsible for defining their game-specific data and features for their game-specific logic.
When a match is completed, the game server sends a
MatchEnd payload to the platform for processing.
MatchEnd includes engine-defined fields for granting inventory and granting rewards. It also includes an
ext field that contains game-specific data defined by the user. In this example, we’ll assume it includes
heroXp fields to support player progression.
When the engine receives the payload, it parses and validates the data and forwards it to various services. The Progression service then defines a plugin that receives the
Users then implement a plugin with strongly typed access to their
heroXp fields. These fields are then used to calculate and save level up events. The engine takes the results of the plugin and opaquely stores the player data in the database.
Custom Services #
Custom services are defined in the same manner as engine services and follow the exact same patterns. Custom services adopt all engine features such as gateway routing, authentication, service-to-service calls, and metrics. Service configuration is standardized so that custom services define and support configuration in the same way Pragma Engine services are configured.
A game developer needs a custom service to implement a specific quest flow for their story-based campaign. Players select quest details during the Lobby flow within the game loop.
The developer implements a lobby plugin after defining the new Quest service. On lobby creation, the plugin makes a service-to-service RPC call to the Quest service to validate the player’s selection.