This repository contains the gamification engine of DinoDev, but it can be used for other projects as well. See also the DinoDev Wiki.
The concept is inspired by the work of Garcia et al. There are three main elements in the gamification engine:
- Events: Events represent user behavior that can be tracked and rewarded. For example, a user reviews a pull request or creates a new issue.
- Event Types: Each event has a type. The type defines the event's properties.
- Rules: Rules define actions which are executed when an event occurs and a condition is met. For example, a user reviews a pull request and receives 10 points.
This engine is completely generic, there are no game elements implemented. You can define your own event types and rules.
This repository contains an event publisher which can be used whenever a pub-sub pattern is needed. It is based on Project Reactor.
The following example assumes a Spring Boot application, but the gamification engine is not bound to Spring Boot.
Define the event type you need using the builder pattern. The following example defines an event type for gaining XP.
public static final EventType XP_GAIN = DefaultEventType.builder()
.setIdentifier("XP_GAIN")
.setDescription("A user gained XP.")
.setDefaultVisibility(EventVisibility.PRIVATE)
.setEventSchema(DefaultSchemaDefinition.builder().setFields(
List.of(
DefaultFieldSchemaDefinition.builder()
.setName("xp")
.setType(AllowedDataType.INTEGER)
.setDescription("The amount of XP gained.")
.setRequired(true)
.build()))
.build())
.setMessageTemplate("gained ${xp} XP!")
.build();
Register the event types in the EventTypeRegistry
.
@Bean
public EventTypeRegistry eventTypeRegistry() {
EventTypeRegistry eventTypeRegistry = new EventTypeRegistry();
eventTypeRegistry.register(XP_GAIN);
eventTypeRegistry.register(ISSUE_CREATED);
// add any other event types here
return eventTypeRegistry;
}
Implement a rule that rewards the user with XP.
public class XpGainRule implements Rule {
@Override
public List<String> getTriggerEventTypeIdentifiers() {
return List.of(ISSUE_CREATED.getIdentifier());
}
@Override
public boolean checkCondition(Event triggerEvent) {
return true; // all events of type ISSUE_CREATED trigger this rule
}
@Override
public synchronized Optional<CreateEventInput> executeAction(Event triggerEvent) {
// add your logic here
return Optional.of(getXpGainEvent(triggerEvent, xpToAdd));
}
private CreateEventInput getXpGainEvent(Event triggerEvent, int xpToAdd) {
return CreateEventInput.builder()
.setEventTypeIdentifier(XP_GAIN.getIdentifier())
.setProjectId(triggerEvent.getProjectId())
.setUserId(triggerEvent.getUserId())
.setEventData(List.of(
new TemplateFieldInput("xp", AllowedDataType.INTEGER, Integer.toString(xpToAdd))))
.setMessage(getXpMessage(triggerEvent, xpToAdd))
.setParentId(triggerEvent.getId())
.build();
}
}
Register the rules in the RuleRegistry
.
The following code registers all bean of type Rule
in the RuleRegistry
.
@Bean
RuleRegistry ruleRegistry(ApplicationContext applicationContext) {
RuleRegistry ruleRegistry = new RuleRegistry();
Map<String, Rule> ruleBeans = applicationContext.getBeansOfType(Rule.class);
// Register each Rule bean
for (Rule rule : ruleBeans.values()) {
ruleRegistry.register(rule);
}
return ruleRegistry;
}
@Bean
public DefaultEventPublisher eventPublisher(EventPersistenceService eventService) {
return new DefaultEventPublisher(eventService);
}
@Bean
public GamificationEngine gamificationEngine(
EventPublisher<Event, CreateEventInput> eventPublisher,
EventTypeRegistry eventTypeRegistry,
RuleRegistry ruleRegistry
) {
return new GamificationEngine(eventPublisher, ruleRegistry, eventTypeRegistry);
}
public class IssueService {
private final EventPublisher<Event, CreateEventInput> eventPublisher;
public IssueService(EventPublisher<Event, CreateEventInput> eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void createIssue(String projectId, String userId) {
// issue creation logic
Event event = Event.builder()
.setEventTypeIdentifier(ISSUE_CREATED.getIdentifier())
.setProjectId(projectId)
.setUserId(userId)
// ....
.build();
eventPublisher.publish(event);
// the gamification engine will execute the rules
}
}