Skip to content

Latest commit

Β 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸŽ‰ EventBus - Lightweight Event System in Kotlin

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.

πŸš€ Features

βœ” 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

πŸ“¦ Installation

This library is available on GitHub Packages.

πŸ› οΈ Gradle (Kotlin DSL)

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")
}

πŸ› οΈ Gradle (Groovy DSL)

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"
}

πŸ”‘ Authentication

GitHub Packages requires authentication. Set up your credentials in gradle.properties or as environment variables.

Option 1: Add to gradle.properties

gpr.user=your-github-username
gpr.token=your-personal-access-token

Option 2: Use Environment Variables

export GITHUB_USER=your-github-username
export GITHUB_TOKEN=your-personal-access-token

Note: The GitHub token must have read:packages permission.

πŸš€ Getting Started

1️⃣ Define an Event Interface

interface GameEvent

2️⃣ Create Event Classes

data class Player(val name: String, val balance: Double)
data class PlayerJoinEvent(val player: Player) : GameEvent
data class PlayerLeaveEvent(val player: Player) : GameEvent

3️⃣ Create an Event Bus

val eventBus = EventBus<GameEvent>()

4️⃣ Register a Listener

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}")
}

5️⃣ Publish an Event

val player = Player("Alice", 2000.0)
eventBus.publish(PlayerJoinEvent(player))

🎯 Event Priorities

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}")
}

Available Priorities:

Priority Weight Description
HIGHEST 1000 Executed first
HIGH 500 High priority
NORMAL 0 Default priority
LOW -500 Low priority
LOWEST -1000 Executed last

Custom Priorities:

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)

🎭 Event Filtering

You can filter events to control how they are handled using EventFilters.

Using Lambda Filters:

eventBus.subscribe<PlayerJoinEvent> { event ->
    println("Player joined: ${event.player.name}")
}

Advanced Filtering with Custom Conditions:

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}")
}

Available Filter Methods:

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

Combining Filters:

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 = !someFilter

🌿 Event Branches

Event 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.

Creating a Branch:

val customBranch = eventBus.branch() {
    subscribe<PlayerJoinEvent> { event ->
        println("Player joined: ${event.player}")
    }
}

Branch Operations:

// 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))

Named Branches:

val namedBranch = eventBus.branch("my-custom-branch") {
    subscribe<PlayerLeaveEvent> { event ->
        println("Player left: ${event.player.name}")
    }
}

Branch Hierarchy:

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.

πŸ“Š Debug Mode & Logging

EventBus uses SLF4J for logging. Debug logs are available but disabled by default.

πŸ” Enable Debug Logging

To see EventBus debug logs, configure your SLF4J implementation to set the log level to DEBUG for:

gg.levely.system.eventbus.EventBus

With Logback (logback.xml):

<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>

Programmatically (with Logback):

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
}

πŸ“Œ Logged Event Types

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.

πŸ™Œ Credits

About

EventBus is a lightweight and efficient event system in Kotlin, enabling synchronous event handling with priority management, advanced filtering, event branches, and built-in logging. πŸš€

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages