EventBus is a lightweight, flexible, and high-performance event system built in Kotlin. It provides synchronous event handling, priority-based event dispatching, powerful filtering mechanisms, and dynamic event branches to help manage event-driven architectures efficiently.
β Synchronous Event Dispatching
β Thread-Safe Event Handling (using ConcurrentSkipListSet)
β Priority-Based Listener Execution
β Advanced Event Filtering (exact(), hierarchy(), custom filters with logical operators)
β Event Branches (Detachable/Reattachable listener groups with hierarchy support)
β Lambda & Reified Type Support for concise event subscription
β Integrated Logging System (SLF4J)
β Debug Mode for Event Tracking
This library is available on GitHub Packages.
repositories {
maven {
url = uri("https://maven.pkg.github.com/LevelyStudio/EventBus")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_USER")
password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation("gg.levely.system:eventbus:2.1.0")
}repositories {
maven {
url = uri("https://maven.pkg.github.com/LevelyStudio/EventBus")
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USER")
password = project.findProperty("gpr.token") ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation "gg.levely.system:eventbus:2.1.0"
}GitHub Packages requires authentication. Set up your credentials in gradle.properties or as environment variables.
gpr.user=your-github-username
gpr.token=your-personal-access-tokenexport GITHUB_USER=your-github-username
export GITHUB_TOKEN=your-personal-access-tokenNote: The GitHub token must have
read:packagespermission.
interface GameEventdata class Player(val name: String, val balance: Double)
data class PlayerJoinEvent(val player: Player) : GameEvent
data class PlayerLeaveEvent(val player: Player) : GameEventval eventBus = EventBus<GameEvent>()Using lambda syntax with reified type:
eventBus.subscribe<PlayerJoinEvent> { event ->
println("Player joined: ${event.player.name}")
}Or using the traditional class-based approach:
eventBus.subscribe(PlayerJoinEvent::class.java) { event ->
println("Player joined: ${event.player.name}")
}val player = Player("Alice", 2000.0)
eventBus.publish(PlayerJoinEvent(player))Events can be assigned a priority to control execution order. Higher priority listeners execute first.
eventBus.subscribe<PlayerJoinEvent>(priority = EventPriority.HIGHEST) { event ->
println("High priority handler: ${event.player.name}")
}
eventBus.subscribe<PlayerJoinEvent>(priority = EventPriority.LOW) { event ->
println("Low priority handler: ${event.player.name}")
}| Priority | Weight | Description |
|---|---|---|
HIGHEST |
1000 | Executed first |
HIGH |
500 | High priority |
NORMAL |
0 | Default priority |
LOW |
-500 | Low priority |
LOWEST |
-1000 | Executed last |
You can create custom priorities with specific weights:
val customPriority = EventPriority.of("CRITICAL", 2000)
val beforeNormal = EventPriority.before(EventPriority.NORMAL, gap = 10)
val afterHigh = EventPriority.after(EventPriority.HIGH, gap = 5)You can filter events to control how they are handled using EventFilters.
eventBus.subscribe<PlayerJoinEvent> { event ->
println("Player joined: ${event.player.name}")
}interface TransactionEvent : GameEvent
data class ProcessTransactionEvent(
val source: Player,
val target: Player,
val amount: Double
) : TransactionEvent
// Filter high-value transactions
val filterHighTransaction = EventFilters.exact<ProcessTransactionEvent>() and EventFilters.filter { event ->
event.amount > 1_000_000.0
}
eventBus.subscribe<ProcessTransactionEvent>(filter = filterHighTransaction) { event ->
println("Processing high-value transaction of ${event.amount}")
}| Filter Method | Description |
|---|---|
exact() |
Matches only the exact event type |
hierarchy() |
Matches the event type and its subclasses |
filter() |
Custom filter with a predicate |
all() |
Matches all events |
none() |
Matches no events |
You can combine filters using logical operators:
val complexFilter = EventFilters.exact<ProcessTransactionEvent>() and EventFilters.filter { event ->
event.amount > 500.0
}
val orFilter = filter1 or filter2
val notFilter = !someFilterEvent Branches allow you to create isolated groups of event listeners that can be attached or detached dynamically. This is useful for managing temporary event handlers or modular event systems.
val customBranch = eventBus.branch() {
subscribe<PlayerJoinEvent> { event ->
println("Player joined: ${event.player}")
}
}// Get the branch path
println("Path: ${customBranch.getPath()}")
// Detach the branch (temporarily disable all its listeners)
customBranch.detach()
// Events published while detached won't be handled by this branch
eventBus.publish(PlayerJoinEvent(player))
// Reattach the branch (re-enable all its listeners)
customBranch.reattach()
// Now events will be handled again
eventBus.publish(PlayerJoinEvent(player))val namedBranch = eventBus.branch("my-custom-branch") {
subscribe<PlayerLeaveEvent> { event ->
println("Player left: ${event.player.name}")
}
}Branches can have child branches, creating a tree structure:
val parentBranch = eventBus.branch("parent")
val childBranch = parentBranch.branch("child")
// Get hierarchical path: "parent/child"
println(childBranch.getPath())Note: When a parent branch is detached, all its children are also detached.
EventBus uses SLF4J for logging. Debug logs are available but disabled by default.
To see EventBus debug logs, configure your SLF4J implementation to set the log level to DEBUG for:
gg.levely.system.eventbus.EventBus
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%logger{36}] %level - %msg%n</pattern>
</encoder>
</appender>
<!-- Enable EventBus debug logging -->
<logger name="gg.levely.system.eventbus.EventBus" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import org.slf4j.LoggerFactory
fun enableEventBusDebug() {
val logger = LoggerFactory.getLogger(EventBus::class.java) as Logger
logger.level = Level.DEBUG
}| Type | Description |
|---|---|
PUBLISH |
When an event is published |
SUBSCRIBE |
When a listener is registered |
UNSUBSCRIBE |
When a listener is removed |
Note: EventBus depends only on SLF4J API. You must provide an SLF4J implementation (e.g., Logback, Log4j2, SLF4J-Simple) in your project.