Skip to content

LukynkaCZE/kotlin-bindables

Repository files navigation

kotlin-bindables

bindables library for kotlin inspired by the osu!framework bindable system

what are bindables

Bindables are objects that hold a value and provide following listeners:

  • Bindable<T>
    • value change
  • BindableList<T>
    • value added to list
    • value removed from list
    • value set by index in list
    • list updated
  • BindableMap<K, V>
    • value set
    • value removed
    • map updated

Installation

Kotlin DSL

repositories {
    maven {
        name = "devOS"
        url = uri("https://mvn.devos.one/releases")
    }
}

dependencies {
    implementation("cz.lukynka:kotlin-bindables:2.0")
}

Usage

There are different types of bindables, each with their own events

Bindable

val playerHealth = Bindable<Int>(5)

// Register a listener
playerHealth.valueChanged {
    println("Player health changed from ${it.oldValue} to ${it.newValue}!")
}

// Set the value
playerHealth.value = 20

Binding Bindables together

val x = Bindable<Int>(1)
val y = Bindable<Int>(2)

x.bindTo(y) 
assertEquals(x.value, y.value) // bindTo() immediately sets x's value to y's

y.value = 10
assertEquals(10, x.value) // The value set to y above is propagated to x

x.value = 5
assertEquals(5, y.value) // Same as above - dual-way communication

You may also use:

val x = Bindable<Int>(1)
val y = x.getBoundCopy()

// or

val x = Bindable<Int>(1)
val y = Bindable<Int>(2).withBindTo(x)

Note that a Bindable cannot be bound to self or another bindable that is bound to the first bindable. Neither can you bind again when the bindable is already bound. You will need to call bindable.unbind() first.

BindableList

val playersOnline = BindableList<String>()

// called when item gets added
playersOnline.itemAdded {
    println("Player ${it.item} has joined!")
}

// called when item gets removed
playersOnline.itemRemoved {
    println("Player ${it.item} has left!")
}

// called when item at index is set
playersOnline.itemChanged {
    println("object at index ${it.index} has been changed to ${it.item}!")
}

playersOnline.add("AsoDesu_")
playersOnline.add("LukynkaCZE")
playersOnline.remove("AsoDesu_")
playersOnline.setIndex(1, "KinichAjaw")

BindableMap

val uuidToPlayerName = BindableMap<UUID, String>()

// Is both called from BindableMap.set and BindableMap.add
uuidToPlayerName.itemSet {
    println("Player with uuid ${it.key} (${it.value}) has joined!")
}

// called when item is removed
uuidToPlayerName.itemRemoved {
    println("Player with uuid ${it.key} (${it.value}) has left!")
}

// called when the map is updated 
uuidToPlayerName.mapUpdated {
    println("The player list has been updated!")
}

uuidToPlayerName[UUID.fromString("aeb19a9c-a64a-4255-bb42-e74f05f9d30f")] = "AsoDesu_"
uuidToPlayerName[UUID.fromString("0c9151e4-7083-418d-a29c-bbc58f7c741b")] = "LukynkaCZE"
uuidToPlayerName.remove(UUID.fromString("aeb19a9c-a64a-4255-bb42-e74f05f9d30f"))
uuidToPlayerName.set(UUID.fromString("0c9151e4-7083-418d-a29c-bbc58f7c741b"), "KinichAjaw")

Unregistering Listeners & Disposing

All event functions provide you back with a listener that you can then unregister at a later time:

val playerHealth = Bindable<Int>(5)

val playerHealthChangeListener = playerHealth.valueChanged {
    println("Player health changed from ${it.oldValue} to ${it.newValue}!")
}

playerHealth.value = 20

// later in your program
playerHealth.unregister(playerHealthChangeListener)

You can also dispose a bindable using the dispose method, this will remove all listeners from it and unbind it

val playerHealth = Bindable<Int>(5)

val playerHealthChangeListener = playerHealth.valueChanged {
  println("Player health changed from ${it.oldValue} to ${it.newValue}!")
}

playerHealth.value = 20

//later in your program
playerHealth.dispose()

Bindable Dispatcher

The Bindable dispatcher system is a local event-bus type system that sends notifications of a specified type (T).

val dispatcher = BindableDispatcher<Int>()

dispatcher.subscribe { int -> // subscribe
    println("dispatcher notification: $int")
}

dispatcher.dispatch(69) // dispatch

Like any other bindable, you can call dispose which will unsubscribe all subscribed listeners

Bindable Pool

In more complicated scenarios, you can create a BindablePool class which will keep track of all your bindables:

val bindablePool = BindablePool()

val playerHealth = pool.provideBindable<Double>(20.0)
val playerFood = pool.provideBindable<Double>(20.0)
val playerStamina = pool.provideBindable<Double>(20.0)
val metadata: pool.provideBindableMap<EntityMetadataType, EntityMetadata>()

playerHealth.valueChanged {
    player.sendPacket(UpdateHealthPacket(it.newValue))
}

playerFood.valueChanged {
  player.sendPacket(UpdateFoodPacket(it.newValue))
}

playerStamina.valueChanged {
  player.sendPacket(UpdateStaminaPacket(it.newValue))
}

metadata.mapUpdated {
  sendMetadataPacketToViewers()
  sendSelfMetadataIfPlayer()
}

// later in your program (when player leaves the server for example)
bindablePool.dispose() // remove all listeners registered

You can also manually unregister bindable from a pool:

bindablePool.unregister(playerStamina)

Extras

On all the types, you can also use .triggerUpdate() to call the listeners without actually causing any update to the value(s)

Both BindableList and BindableMap have .addIfNotPresent(value) and .removeIfPresent(value) functions

All the bindable types provide .setSilently(value) which will set the value of the bindable without calling any of the listeners


About

Bindables library for kotlin

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages