Events
The event bus is the primary communication mechanism in Gnosis Engine. It allows components to interact without being tightly coupled.
Event Structure
Every event in Gnosis is represented by a GnosisEvent object, which contains:
- Id (
string): A unique identifier for the event type (e.g.,REQUEST_PLAYER_DAMAGE,FACT_PLAYER_DIED). - Data (
GnosisNode): An optional payload containing event-specific data. This data is stored in the sharedGnosisStore. - DryRun (
bool): When set totrue, the event bus runs the rule pipeline but does not invoke regular subscribers or commit state changes. This is useful for simulations (e.g., predicting the outcome of an action).
The REQUEST / FACT Pattern
Gnosis Engine services follow a naming convention to distinguish between intent and finality. While it is a good idea to maintain these conventions for consistency and seamless integration with the Rule System, the developer is free to create any kind of event ID regardless of the REQUEST/FACT pattern.
1. REQUEST Events
A REQUEST event is published before any state change occurs. Its purpose is to allow Interceptor Rules to modify the payload.
- Naming:
REQUEST_<SERVICE>_<ACTION>(e.g.,REQUEST_CURRENCY_ADD). - Behavior: The service publishes the request, waits for the rules to finish, and then reads the (potentially modified) data from the
FinalEvent.Data. - Use Case: Buffs, debuffs, shields, or armor that should modify a value (like damage or cost) before it’s applied.
2. FACT Events
A FACT event is published after the state has been successfully updated. It is immutable and informs the engine that something has officially happened.
- Naming:
FACT_<SERVICE>_<ACTION>(e.g.,FACT_CURRENCY_ADDED). - Behavior: C# systems and Observer Rules listen to facts to trigger side effects that shouldn’t modify the original action.
- Use Case: Playing VFX/SFX, updating UI, unlocking achievements, or spawning particles.
GnosisEventBus
The GnosisEventBus (implementing IGnosisEventBus) manages subscriptions and publication.
Example: The Full Pipeline
// 1. Publish the REQUEST
var requestPayload = store.CreateObject();
requestPayload.Set("amount", 10);
var request = new GnosisEvent("REQUEST_HEALTH_DAMAGE", requestPayload, false);
var result = eventBus.Publish(request);
// 2. Get the final amount (a rule might have reduced it to 5)
int finalAmount = (int)result.FinalEvent.Data["amount"];
// 3. Apply to state... then publish the FACT
var factPayload = store.CreateObject();
factPayload.Set("finalAmount", finalAmount);
eventBus.Publish(new GnosisEvent("FACT_HEALTH_DAMAGED", factPayload, false));Subscribing to Events
Subscribers are simple Action<GnosisEvent> delegates. You can subscribe with a priority to control the order of execution.
// Example: Subscribing to an event
eventBus.Subscribe("FACT_HEALTH_DAMAGED", OnPlayerDamaged);
void OnPlayerDamaged(GnosisEvent evt) {
var amount = (int)evt.Data["finalAmount"];
Debug.Log($"Player took {amount} damage!");
}Best Practices
- Use UpperCase & Snake_Case: For consistency and better visibility in JSON files, use full uppercase with underscores (e.g.,
FACT_PLAYER_HEALED). - Preferred Structure: When possible, follow the
<TYPE>_<SERVICE>_<ACTION>structure (e.g.,REQUEST_SHOP_PURCHASE). - Keep Payloads Flat: While
GnosisNodesupports deep nesting, flat payloads are easier to work with in rules. - DryRun for UI Previews: Use
DryRun: trueto see how rules would modify an event before actually committing the action. - Prefer Events over Direct Calls: Use events for cross-system communication (e.g., Game Logic -> Achievement System).