1.0.0.3 - new modules

Added utility, menu, thirdparty and exposed modules
A few little fixes
This commit is contained in:
redtea committed 2024-03-28 10:12:38 +03:00
1 parent 161f9aa268
commit 3c87e2d6a1
63 files changed
+5334 -12

No files matched your search

@@ -0,0 +1,352 @@
package tech.carcadex.kotlinbukkitkit.utility.collections
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
import tech.carcadex.kotlinbukkitkit.extensions.scheduler
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
public typealias OnExpireCallback<T> = (T) -> Unit
public fun <E> Plugin.expirationListOf(): ExpirationList<E> = ExpirationListImpl(this)
public fun <E> WithPlugin<*>.expirationListOf(): ExpirationList<E> = plugin.expirationListOf<E>()
public fun <E> expirationListOf(expireTime: Int, plugin: Plugin, vararg elements: E): ExpirationList<E> = plugin.expirationListOf<E>().apply { elements.forEach { add(it, expireTime) } }
public fun <E> Plugin.expirationListOf(expireTime: Int, vararg elements: E): ExpirationList<E> = expirationListOf(expireTime, this, elements = *elements)
public fun <E> WithPlugin<*>.expirationListOf(expireTime: Int, vararg elements: E): ExpirationList<E> = plugin.expirationListOf(expireTime, *elements)
public fun <E> expirationListOf(expireTime: Int, plugin: Plugin, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = plugin.expirationListOf<E>().apply { elements.forEach { (element, onExpire) -> add(element, expireTime, onExpire) } }
public fun <E> Plugin.expirationListOf(expireTime: Int, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = expirationListOf(expireTime, this, elements = *elements)
public fun <E> WithPlugin<*>.expirationListOf(expireTime: Int, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = plugin.expirationListOf(expireTime, *elements)
public interface ExpirationList<E> : MutableIterable<E> {
/**
* Returns the count of elements in this list.
*/
public val size: Int
/**
* Returns `true` if the list contains no elements, `false` otherwise.
*/
public fun isEmpty(): Boolean
/**
* Returns the time missing to expire or `null` if the list don't contain the element
*/
public fun missingTime(element: E): Int?
/**
* Checks if the specified element is contained in this list.
*/
public operator fun contains(element: E): Boolean
/**
* Returns the element at the specified index in the list.
*/
public operator fun get(index: Int): E?
/**
* Returns the index of the first occurrence of the e element in the list, or -1 if the specified
* element is not contained in the list.
*/
public fun indexOf(element: E): Int
/**
* Return the first element in the list or `null` if the list is empty
*/
public fun first(): E?
/**
* Return the last element in the list or `null` if the list is empty
*/
public fun last(): E?
/**
* Removes all elements from this list.
*/
public fun clear()
/**
* Add the element to the list with an expiration time.
*
* [expireTime] in seconds
* callback [onExpire] is called when the element expires.
*/
public fun add(element: E, expireTime: Int, onExpire: OnExpireCallback<E>? = null)
/**
* Add the element in the start of list with an expiration time.
*
* [expireTime] in seconds
* callback [onExpire] is called when the element expires.
*/
public fun addFirst(element: E, expireTime: Int, onExpire: OnExpireCallback<E>? = null)
/**
* Removes an element at the specified [index] from the list.
*
* @return the element that has been removed or `null` if
* the element is not contained in the list.
*/
public fun removeAt(index: Int): E?
/**
* Removes an element from the list
*
* @return `true` if the element was removed.
*/
public fun remove(element: E): Boolean
/**
* Removes the first element from the list.
*
* @return the element if was removed.
*/
public fun removeFirst(): E?
/**
* Removes the last element from the list.
*
* @return the element if was removed.
*/
public fun removeLast(): E?
}
private class ExpirationNode<E>(var element: E, val expireTime: Int) {
var next: ExpirationNode<E>? = null
var previous: ExpirationNode<E>? = null
var onExpire: OnExpireCallback<E>? = null
val startTime: Long = System.currentTimeMillis()
}
public class ExpirationListImpl<E>(private val plugin: Plugin) : ExpirationList<E> {
private var head: ExpirationNode<E>? = null
private var tail: ExpirationNode<E>? = null
private var task: BukkitTask? = null
private var emptyCount: Byte = 0
override val size: Int get() = _size
private var _size: Int = 0
private set
override fun isEmpty(): Boolean = _size == 0
override fun missingTime(element: E): Int? {
return getNodeByElement(element)
?.let { it.expireTime - ((System.currentTimeMillis() - it.startTime) / 1000) }
?.toInt()
}
override operator fun contains(element: E): Boolean = indexOf(element) > -1
override operator fun get(index: Int): E? {
return getNode(index)?.element
}
override fun first(): E? = head?.element
override fun last(): E? = tail?.element
override operator fun iterator(): MutableIterator<E> {
return object : MutableIterator<E> {
private val nodeIterator = nodeIterator()
override fun hasNext() = nodeIterator.hasNext()
override fun next() = nodeIterator.next().element
override fun remove() = nodeIterator.remove()
}
}
override fun indexOf(element: E): Int {
if (head == null) return -1
var node = head
var count = 0
do {
if (node?.element === element) {
return count
}
node = node?.next
count++
} while (node != null)
return -1
}
override fun clear() {
head = null
tail = null
_size = 0
}
override fun add(element: E, expireTime: Int, onExpire: OnExpireCallback<E>?) {
if (expireTime <= 0) throw IllegalArgumentException("expireTime need to be greater then 0")
val newNode = ExpirationNode(element, expireTime).also { it.onExpire = onExpire }
if (head == null) {
head = newNode
tail = newNode
} else {
tail?.next = newNode
tail = newNode
}
_size++
generateTask()
}
override fun addFirst(element: E, expireTime: Int, onExpire: OnExpireCallback<E>?) {
if (expireTime <= 0) throw IllegalArgumentException("expireTime need to be greater then 0")
val newNode = ExpirationNode(element, expireTime).also { it.onExpire = onExpire }
if (head == null) {
head = newNode
tail = newNode
} else {
head?.previous = newNode
head = newNode
}
_size++
generateTask()
}
override fun removeAt(index: Int): E? {
return getNode(index)?.also {
removeNode(it)
}?.element
}
override fun remove(element: E): Boolean = getNodeByElement(element)?.let { true.apply { removeNode(it) } } ?: false
override fun removeFirst(): E? {
val next = head?.next
val headElement = head?.element
if (next == null) {
tail = null
head = null
} else {
head = next
next.previous = null
}
return headElement
}
override fun removeLast(): E? {
val previous = tail?.previous
val tailElement = tail?.element
if (previous == null) {
tail = null
head = null
} else {
tail = previous
previous.next = null
}
return tailElement
}
private fun getNode(index: Int): ExpirationNode<E>? {
if (index < 0 || index >= _size) return null
val mid = _size / 2
return if (index > mid) {
getFromSpecificSide(index - mid, tail) { it?.previous }
} else {
getFromSpecificSide(index, head) { it?.next }
}
}
private fun getNodeByElement(element: E): ExpirationNode<E>? {
if (head == null) return null
var node = head
do {
if (node?.element === element) {
return node
}
node = node?.next
} while (node != null)
return null
}
private inline fun getFromSpecificSide(
count: Int,
start: ExpirationNode<E>?,
next: (ExpirationNode<E>?) -> ExpirationNode<E>?,
): ExpirationNode<E> {
var index = 0
var current = start
while (index != count) {
current = next(current)
index++
}
return current!!
}
private fun nodeIterator(): MutableIterator<ExpirationNode<E>> {
return object : MutableIterator<ExpirationNode<E>> {
private var current = head
override fun hasNext(): Boolean {
return current != null
}
override fun next(): ExpirationNode<E> {
val aux = current
current = current?.next
return aux!!
}
override fun remove() {
if (current != null) {
removeNode(current!!)
}
}
}
}
private fun checkTime(current: Long, node: ExpirationNode<E>) = ((current - node.startTime) / 1000) - node.expireTime >= 0
private fun generateTask() {
if (task == null) {
task = scheduler {
if (isEmpty()) {
emptyCount++
} else {
emptyCount = 0
val current = System.currentTimeMillis()
for (node in nodeIterator()) {
if (checkTime(current, node)) {
if (node.onExpire != null) node.onExpire?.invoke(node.element)
removeNode(node)
}
}
}
if (emptyCount > 9) {
cancel()
task = null
}
}.runTaskTimer(plugin, 0L, 20L)
}
}
private fun removeNode(node: ExpirationNode<E>) {
if (node === head && node === tail) {
head = null
tail = null
} else if (node === head) {
head = node.next?.apply { previous = null }
} else if (node === tail) {
tail = node.previous?.apply { next = null }
} else {
node.apply {
previous?.next = next
next?.previous = previous
}
}
_size--
}
}
@@ -0,0 +1,175 @@
package tech.carcadex.kotlinbukkitkit.utility.collections
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
import tech.carcadex.kotlinbukkitkit.extensions.scheduler
import tech.carcadex.kotlinbukkitkit.utility.extensions.now
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitTask
import java.util.*
public typealias OnExpireMapCallback<K, V> = (K, V) -> Unit
public fun <K, V> Plugin.expirationMapOf(): ExpirationMap<K, V> = ExpirationMapImpl(this)
public fun <K, V> WithPlugin<*>.expirationMapOf(): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>()
public fun <K, V> expirationMapOf(expireTime: Long, plugin: Plugin, vararg elements: Pair<K, V>): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>().apply { elements.forEach { (key, value) -> put(key, value, expireTime) } }
public fun <K, V> Plugin.expirationMapOf(expireTime: Long, vararg elements: Pair<K, V>): ExpirationMap<K, V> = expirationMapOf(expireTime, this, elements = *elements)
public fun <K, V> WithPlugin<*>.expirationMapOf(expireTime: Long, vararg elements: Pair<K, V>): ExpirationMap<K, V> = plugin.expirationMapOf(expireTime, *elements)
public fun <K, V> expirationMapOf(expireTime: Long, plugin: Plugin, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>().apply { elements.forEach { (key, value, onExpire) -> put(key, value, expireTime, onExpire) } }
public fun <K, V> Plugin.expirationMapOf(expireTime: Long, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = expirationMapOf(expireTime, this, elements = *elements)
public fun <K, V> WithPlugin<*>.expirationMapOf(expireTime: Long, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = plugin.expirationMapOf(expireTime, *elements)
public interface ExpirationMap<K, V> : MutableMap<K, V>, WithPlugin<Plugin> {
/**
* Returns the missing time on seconds to expire the key,
* -1 if was not specified the expiration time before(permanent key) or
* `null` if the key is not contained in the map.
*/
public fun missingTime(key: K): Long?
/**
* Set expiration time to the key and returns `true` if the key is found
* or false if the key is not contained in the map.
*/
public fun expire(key: K, time: Long): Boolean
/**
* Set expiration time to the key and returns `true` if the key is found
* or false if the key is not contained in the map.
*
* [time] in seconds.
* [callback] is called when the key expires.
*/
public fun expire(key: K, time: Long, callback: OnExpireMapCallback<K, V>): Boolean
/**
* Associates the specified [value] with the specified [key] in the map
* with an expiration time.
*
* @return the previous value associated with the key, or `null` if the key was not present in the map.
*/
public fun put(key: K, value: V, time: Long): V?
/**
* Associates the specified [value] with the specified [key] in the map
* with an expiration time.
*
* [time] in seconds.
* [callback] is called when the key expires.
*
* @return the previous value associated with the key, or `null` if the key was not present in the map.
*/
public fun put(key: K, value: V, time: Long, callback: OnExpireMapCallback<K, V>): V?
}
public class ExpirationMapImpl<K, V>(
override val plugin: Plugin,
private val initialMap: MutableMap<K, V> = WeakHashMap(),
) : ExpirationMap<K, V>, MutableMap<K, V> by initialMap {
private val putTime: MutableMap<K, Long> = mutableMapOf()
private val expiration: MutableMap<K, Long> = mutableMapOf()
private val whenExpire: MutableMap<K, OnExpireMapCallback<K, V>> = mutableMapOf()
override fun missingTime(key: K): Long? {
return if (containsKey(key)) {
(expiration.get(key) ?: return -1) - ((now() - putTime.getOrPut(key) { now() }) / 1000)
} else {
null
}
}
private fun ex(key: K, time: Long) {
putTime.put(key, now())
expiration.put(key, time)
}
private fun whenEx(key: K, callback: OnExpireMapCallback<K, V>) {
whenExpire.put(key, callback)
}
override fun expire(key: K, time: Long): Boolean {
if (containsKey(key)) {
ex(key, time)
return true
} else {
return false
}
}
override fun expire(key: K, time: Long, callback: OnExpireMapCallback<K, V>): Boolean {
if (expire(key, time)) {
whenEx(key, callback)
return true
} else {
return false
}
}
override fun put(key: K, value: V): V? {
val result = initialMap.put(key, value)
generateTask()
return result
}
override fun put(key: K, value: V, time: Long): V? {
if (time <= 0) throw IllegalArgumentException("expiration time can't be negative or zero")
val result = put(key, value)
ex(key, time)
return result
}
override fun put(key: K, value: V, time: Long, callback: OnExpireMapCallback<K, V>): V? {
if (time <= 0) throw IllegalArgumentException("expiration time can't be negative or zero")
val result = put(key, value)
ex(key, time)
whenEx(key, callback)
return result
}
override fun remove(key: K): V? {
val result = initialMap.remove(key)
if (result != null) {
putTime.remove(key)
expiration.remove(key)
whenExpire.remove(key)
}
return result
}
private fun checkTime(current: Long, key: K) = ((current - (putTime.getOrPut(key) { current })) / 1000) - (expiration.get(key) ?: 0) >= 0
private var task: BukkitTask? = null
private var emptyCount: Byte = 0
private fun generateTask() {
if (task == null) {
task = scheduler {
if (isEmpty()) {
emptyCount++
} else {
emptyCount = 0
val current = now()
for ((key, value) in entries) {
if (checkTime(current, key)) {
whenExpire.remove(key)?.invoke(key, value)
initialMap.remove(key)
putTime.remove(key)
expiration.remove(key)
}
}
}
if (emptyCount > 5) {
cancel()
task = null
}
}.runTaskTimer(plugin, 0L, 20L)
}
}
}
@@ -0,0 +1,238 @@
package tech.carcadex.kotlinbukkitkit.utility.collections
public typealias ObservableListener = (ObservableAction) -> Unit
public enum class ObservableAction { ADD, SET, REPLACE, REMOVE, CLEAR }
public fun <T> observableListOf(mutableList: MutableList<T>): ObservableList<T> = mutableList.asObservable()
public fun <T> observableSetOf(mutableSet: MutableSet<T>): ObservableSet<T> = mutableSet.asObservable()
public fun <K, V> observableMapOf(mutableMap: MutableMap<K, V>): ObservableMap<K, V> = mutableMap.asObservable()
public fun <T> MutableList<T>.asObservable(): ObservableList<T> = ObservableList(this)
public fun <T> MutableSet<T>.asObservable(): ObservableSet<T> = ObservableSet(this)
public fun <K, V> MutableMap<K, V>.asObservable(): ObservableMap<K, V> = ObservableMap(this)
public class ObservableList<T>(
private val list: MutableList<T>,
) : MutableList<T> by list, ObservableCollection<T> {
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
override fun add(element: T): Boolean {
runListeners(ObservableAction.ADD)
return list.add(element)
}
override fun add(index: Int, element: T) {
runListeners(ObservableAction.ADD)
return list.add(index, element)
}
override fun addAll(index: Int, elements: Collection<T>): Boolean {
runListeners(ObservableAction.ADD)
return list.addAll(index, elements)
}
override fun addAll(elements: Collection<T>): Boolean {
runListeners(ObservableAction.ADD)
return list.addAll(elements)
}
override fun clear() {
list.clear()
runListeners(ObservableAction.CLEAR)
}
override fun listIterator(): MutableListIterator<T> {
return ObservableMutableListIterator(
list.listIterator(),
::runListeners,
)
}
override fun listIterator(index: Int): MutableListIterator<T> {
return ObservableMutableListIterator(
list.listIterator(index),
::runListeners,
)
}
override fun remove(element: T): Boolean {
return list.remove(element).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun removeAll(elements: Collection<T>): Boolean {
return list.removeAll(elements).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun removeAt(index: Int): T {
return list.removeAt(index).apply {
runListeners(ObservableAction.REMOVE)
}
}
override fun retainAll(elements: Collection<T>): Boolean {
return list.retainAll(elements).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun set(index: Int, element: T): T {
return list.set(index, element).apply {
runListeners(ObservableAction.SET)
}
}
}
public class ObservableMutableListIterator<T>(
private val iterator: MutableListIterator<T>,
private val runListeners: ObservableListener,
) : MutableListIterator<T> by iterator {
override fun add(element: T) {
iterator.add(element)
runListeners(ObservableAction.ADD)
}
override fun remove() {
iterator.remove()
runListeners(ObservableAction.REMOVE)
}
override fun set(element: T) {
iterator.set(element)
runListeners(ObservableAction.SET)
}
}
public class ObservableSet<T>(
private val set: MutableSet<T>,
) : MutableSet<T> by set, ObservableCollection<T> {
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
override fun add(element: T): Boolean {
return set.add(element).ifTrue {
runListeners(ObservableAction.ADD)
}
}
override fun addAll(elements: Collection<T>): Boolean {
return set.addAll(elements).ifTrue {
runListeners(ObservableAction.ADD)
}
}
override fun clear() {
set.clear()
runListeners(ObservableAction.CLEAR)
}
override fun iterator(): MutableIterator<T> {
return ObservableMutableIterator(
set.iterator(),
::runListeners,
)
}
override fun remove(element: T): Boolean {
return set.remove(element).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun removeAll(elements: Collection<T>): Boolean {
return set.removeAll(elements).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun retainAll(elements: Collection<T>): Boolean {
return set.retainAll(elements).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
}
public class ObservableMutableIterator<T>(
private val iterator: MutableIterator<T>,
private val runListeners: ObservableListener,
) : MutableIterator<T> by iterator {
override fun remove() {
iterator.remove()
runListeners(ObservableAction.REMOVE)
}
}
public class ObservableMap<K, V>(
private val map: MutableMap<K, V>,
) : MutableMap<K, V> by map, ObservableHolder {
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
override fun clear() {
map.clear()
runListeners(ObservableAction.CLEAR)
}
override fun put(key: K, value: V): V? {
return put(key, value).apply {
runListeners(ObservableAction.ADD)
}
}
override fun putAll(from: Map<out K, V>) {
putAll(from)
runListeners(ObservableAction.ADD)
}
override fun putIfAbsent(key: K, value: V): V? {
return map.putIfAbsent(key, value).also {
if (it == null) runListeners(ObservableAction.ADD)
}
}
override fun remove(key: K): V? {
return map.remove(key)?.also {
runListeners(ObservableAction.REMOVE)
}
}
override fun remove(key: K, value: V): Boolean {
return map.remove(key, value).ifTrue {
runListeners(ObservableAction.REMOVE)
}
}
override fun replace(key: K, first: V, second: V): Boolean {
return map.replace(key, first, second).ifTrue {
runListeners(ObservableAction.REPLACE)
}
}
override fun replace(key: K, value: V): V? {
return map.replace(key, value)?.also {
runListeners(ObservableAction.REPLACE)
}
}
}
public interface ObservableCollection<T> : MutableCollection<T>, ObservableHolder
public interface ObservableHolder {
public val listeners: MutableList<ObservableListener>
public fun observe(listener: ObservableListener) {
listeners.add(listener)
}
public fun runListeners(action: ObservableAction) {
for (listener in listeners) {
listener(action)
}
}
}
private inline fun Boolean.ifTrue(block: () -> Unit) = apply { block() }
@@ -0,0 +1,270 @@
package tech.carcadex.kotlinbukkitkit.utility.collections
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
import tech.carcadex.kotlinbukkitkit.extensions.KListener
import tech.carcadex.kotlinbukkitkit.extensions.event
import tech.carcadex.kotlinbukkitkit.extensions.registerEvents
import tech.carcadex.kotlinbukkitkit.extensions.unregisterListener
import org.bukkit.entity.Player
import org.bukkit.event.player.PlayerKickEvent
import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.plugin.Plugin
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.HashSet
public typealias WhenPlayerQuitCollectionCallback = Player.() -> Unit
public typealias WhenPlayerQuitMapCallback<V> = Player.(V) -> Unit
// List
public fun Plugin.onlinePlayerListOf(): OnlinePlayerList = OnlinePlayerList(this)
public fun WithPlugin<*>.onlinePlayerListOf(): OnlinePlayerList = plugin.onlinePlayerListOf()
public fun onlinePlayerListOf(vararg players: Player, plugin: Plugin): OnlinePlayerList = OnlinePlayerList(plugin).apply { addAll(players) }
public fun Plugin.onlinePlayerListOf(vararg players: Player): OnlinePlayerList = onlinePlayerListOf(*players, plugin = this)
public fun WithPlugin<*>.onlinePlayerListOf(vararg players: Player): OnlinePlayerList = plugin.onlinePlayerListOf(*players)
public fun onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>, plugin: Plugin): OnlinePlayerList = OnlinePlayerList(plugin).apply { pair.forEach { (player, whenPlayerQuit) -> add(player, whenPlayerQuit) } }
public fun Plugin.onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerList = onlinePlayerListOf(*pair, plugin = this)
public fun WithPlugin<*>.onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerList = plugin.onlinePlayerListOf(*pair)
// Set
public fun Plugin.onlinePlayerSetOf(): OnlinePlayerSet = OnlinePlayerSet(this)
public fun WithPlugin<*>.onlinePlayerSetOf(): OnlinePlayerSet = plugin.onlinePlayerSetOf()
public fun onlinePlayerSetOf(vararg players: Player, plugin: Plugin): OnlinePlayerSet = OnlinePlayerSet(plugin).apply { addAll(players) }
public fun Plugin.onlinePlayerSetOf(vararg players: Player): OnlinePlayerSet = onlinePlayerSetOf(*players, plugin = this)
public fun WithPlugin<*>.onlinePlayerSetOf(vararg players: Player): OnlinePlayerSet = plugin.onlinePlayerSetOf(*players)
public fun onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>, plugin: Plugin): OnlinePlayerSet = OnlinePlayerSet(plugin).apply { pair.forEach { (player, whenPlayerQuit) -> add(player, whenPlayerQuit) } }
public fun Plugin.onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerSet = onlinePlayerSetOf(*pair, plugin = this)
public fun WithPlugin<*>.onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerSet = plugin.onlinePlayerSetOf(*pair)
// Map
public fun <V> Plugin.onlinePlayerMapOf(): OnlinePlayerMap<V> = OnlinePlayerMap<V>(this)
public fun <V> WithPlugin<*>.onlinePlayerMapOf(): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf<V>()
public fun <V> onlinePlayerMapOf(vararg pair: Pair<Player, V>, plugin: Plugin): OnlinePlayerMap<V> = OnlinePlayerMap<V>(plugin).apply { putAll(pair) }
public fun <V> Plugin.onlinePlayerMapOf(vararg pair: Pair<Player, V>): OnlinePlayerMap<V> = onlinePlayerMapOf(*pair, plugin = this)
public fun <V> WithPlugin<*>.onlinePlayerMapOf(vararg pair: Pair<Player, V>): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf(*pair)
public fun <V> onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>, plugin: Plugin): OnlinePlayerMap<V> = OnlinePlayerMap<V>(plugin).apply { triple.forEach { (player, value, whenPlayerQuit) -> put(player, value, whenPlayerQuit) } }
public fun <V> Plugin.onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>): OnlinePlayerMap<V> = onlinePlayerMapOf(*triple, plugin = this)
public fun <V> WithPlugin<*>.onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf(*triple)
public class OnlinePlayerList(override val plugin: Plugin) : LinkedList<Player>(), OnlinePlayerCollection {
private val whenQuit: MutableList<Pair<Player, WhenPlayerQuitCollectionCallback>> = LinkedList()
override fun add(player: Player, whenPlayerQuitCallback: Player.() -> Unit): Boolean {
if (super<OnlinePlayerCollection>.add(player, whenPlayerQuitCallback)) {
whenQuit.add(player to whenPlayerQuitCallback)
return true
} else {
return false
}
}
override fun add(element: Player): Boolean {
if (isEmpty()) registerEvents(plugin)
return super<LinkedList>.add(element)
}
override fun quit(player: Player): Boolean {
if (super.quit(player)) {
val iterator = whenQuit.iterator()
for (pair in iterator) {
if (pair.first == player) {
iterator.remove()
pair.second.invoke(pair.first)
}
}
return true
} else {
return false
}
}
override fun removeFirst(): Player {
return super.removeFirst().also {
checkRegistration()
}
}
override fun removeLastOccurrence(p0: Any?): Boolean {
return super.removeLastOccurrence(p0).also {
if (it) checkRegistration()
}
}
override fun removeAt(p0: Int): Player {
return super.removeAt(p0).also {
checkRegistration()
}
}
override fun remove(element: Player): Boolean {
if (super.remove(element)) {
checkRegistration()
return true
} else {
return false
}
}
override fun removeLast(): Player {
return super.removeLast().also {
checkRegistration()
}
}
}
public class OnlinePlayerSet(override val plugin: Plugin) : HashSet<Player>(), OnlinePlayerCollection {
private val whenQuit: MutableMap<Player, WhenPlayerQuitCollectionCallback> = mutableMapOf()
override fun add(player: Player, whenPlayerQuitCallback: WhenPlayerQuitCollectionCallback): Boolean {
if (super<OnlinePlayerCollection>.add(player, whenPlayerQuitCallback)) {
whenQuit.put(player, whenPlayerQuitCallback)
checkRegistration()
return true
} else {
return false
}
}
override fun add(element: Player): Boolean {
return super<HashSet>.add(element).also {
if (it) checkRegistration()
}
}
override fun remove(element: Player): Boolean {
return super.remove(element).also {
if (it) checkRegistration()
}
}
override fun quit(player: Player): Boolean {
if (super.quit(player)) {
whenQuit.remove(player)?.also { block ->
block.invoke(player)
}
return true
} else {
return false
}
}
}
public interface OnlinePlayerCollection : MutableCollection<Player>, KListener<Plugin> {
/**
* Adds a new Player to the collection with a callback for when the player quits the server.
*/
public fun add(player: Player, whenPlayerQuit: WhenPlayerQuitCollectionCallback): Boolean {
return add(player).also {
if (it) checkRegistration()
}
}
/**
* Removes the player from the collection, calling the [WhenPlayerQuitCollectionCallback] provided.
*/
public fun quit(player: Player): Boolean {
return remove(player).also {
if (it) checkRegistration()
}
}
/**
* Clear the collection calling all [WhenPlayerQuitCollectionCallback] from the Players.
*/
public fun clearQuiting() {
toMutableList().forEach {
quit(it)
}
}
}
internal fun OnlinePlayerCollection.checkRegistration() {
if (size == 1) {
event<PlayerQuitEvent> { quit(player) }
event<PlayerKickEvent> { quit(player) }
} else if (size == 0) {
unregisterListener()
}
}
public class OnlinePlayerMap<V>(override val plugin: Plugin) : HashMap<Player, V>(), KListener<Plugin> {
private val whenQuit: HashMap<Player, WhenPlayerQuitMapCallback<V>> = hashMapOf()
/**
* Puts a Player to the map with a [value] and a callback for when the player quits the server.
*/
public fun put(key: Player, value: V, whenPlayerQuit: WhenPlayerQuitMapCallback<V>): V? {
whenQuit.put(key, whenPlayerQuit)
return put(key, value).also {
checkRegistration()
}
}
/**
* Removes the player from the map, calling the [WhenPlayerQuitMapCallback] provided.
*/
public fun quit(player: Player) {
remove(player)?.also {
whenQuit.remove(player)?.also { block ->
block.invoke(player, it)
}
checkRegistration()
}
}
/**
* Clear the map calling all [WhenPlayerQuitMapCallback] from the Players.
*/
public fun clearQuiting() {
keys.toMutableList().forEach {
quit(it)
}
}
override fun remove(key: Player): V? {
return super.remove(key).also {
checkRegistration()
}
}
override fun remove(key: Player, value: V): Boolean {
return super.remove(key, value).also {
checkRegistration()
}
}
private fun checkRegistration() {
if (size == 1) {
event<PlayerQuitEvent> { quit(player) }
event<PlayerKickEvent> { quit(player) }
} else if (size == 0) {
unregisterListener()
}
}
}
@@ -0,0 +1,28 @@
package tech.carcadex.kotlinbukkitkit.utility.extensions
public fun Collection<String>.containsIgnoreCase(
element: String,
): Boolean = any { it.equals(element, true) }
public fun <T> MutableCollection<T>.clear(onRemove: (T) -> Unit) {
toMutableList().forEach {
remove(it)
onRemove(it)
}
}
public fun Array<String>.containsIgnoreCase(
element: String,
): Boolean = any { it.equals(element, true) }
public fun <V> Map<String, V>.containsKeyIgnoreCase(
key: String,
): Boolean = keys.containsIgnoreCase(key)
public fun <V> Map<String, V>.getIgnoreCase(
key: String,
): V? = entries.find { it.key.equals(key, true) }?.value
public fun <K, V> MutableMap<K, V>.clear(onRemove: (K, V) -> Unit) {
keys.toMutableSet().forEach { onRemove(it, remove(it)!!) }
}
@@ -0,0 +1,19 @@
package tech.carcadex.kotlinbukkitkit.utility.extensions
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
public fun now(): Long = System.currentTimeMillis()
public fun nowNano(): Long = System.nanoTime()
public val Long.ticks: Duration get() = toDouble().ticks
public val Int.ticks: Duration get() = toDouble().ticks
public val Double.ticks: Duration get() = tickToMilliseconds(this).milliseconds
public val Duration.inTicks: Double get() = millisecondsToTick(toDouble(DurationUnit.MILLISECONDS))
public fun Duration.toLongTicks(): Long = inTicks.toLong()
private fun tickToMilliseconds(value: Double): Double = value * 50.0
private fun millisecondsToTick(value: Double): Double = value / 50.0
@@ -0,0 +1,122 @@
package tech.carcadex.kotlinbukkitkit.utility.extensions
import tech.carcadex.kotlinbukkitkit.utility.types.BlockPos
import tech.carcadex.kotlinbukkitkit.utility.types.ChunkPos
import tech.carcadex.kotlinbukkitkit.utility.types.VectorComparable
import tech.carcadex.kotlinbukkitkit.utility.types.asBlockPos
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitBlock
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitChunk
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitLocation
import tech.carcadex.kotlinbukkitkit.utility.types.asPos
import org.bukkit.Chunk
import org.bukkit.Location
import org.bukkit.block.Block
// BUKKIT THINGS
public operator fun PosRange<*, BlockPos>.contains(other: Location): Boolean = contains(other.asPos())
public operator fun PosRange<*, BlockPos>.contains(other: Block): Boolean = contains(other.asPos())
public operator fun PosRange<*, ChunkPos>.contains(other: Chunk): Boolean = contains(other.asPos())
public operator fun Location.rangeTo(other: Location): PosRange<Location, BlockPos> {
return PosRange(this.asBlockPos(), other.asBlockPos()) {
RangeIteratorWithFactor<Location, BlockPos>(
this,
other,
{ it.asBukkitLocation(world) },
{ it.asBlockPos() },
)
}
}
public operator fun Block.rangeTo(other: Block): PosRange<Block, BlockPos> {
return PosRange(this.asPos(), other.asPos()) {
RangeIteratorWithFactor<Block, BlockPos>(
this,
other,
{ it.asBukkitBlock(world) },
{ it.asPos() },
)
}
}
public operator fun Chunk.rangeTo(other: Chunk): PosRange<Chunk, ChunkPos> {
return PosRange(this.asPos(), other.asPos()) {
RangeIteratorWithFactor<Chunk, ChunkPos>(
this,
other,
{ it.asBukkitChunk(world) },
{ it.asPos() },
)
}
}
public class PosRange<T, POS : VectorComparable<POS>>(
public val first: POS,
public val last: POS,
private val buildIterator: () -> Iterator<T>,
) : ClosedRange<POS>, Iterable<T> {
override val endInclusive: POS get() = last
override val start: POS get() = first
override fun contains(value: POS): Boolean {
val firstAxis = first.axis()
val lastAxis = last.axis()
return value.axis().withIndex().all { (index, it) ->
it >= firstAxis[index] && it <= lastAxis[index]
}
}
override fun iterator(): Iterator<T> = buildIterator()
}
public class PosRangeIterator<T : VectorComparable<T>>(
first: T,
last: T,
private val factor: (axis: IntArray) -> T,
) : Iterator<T> {
private val firstAxis = first.axis()
private val lastAxis = last.axis()
private val closedAxisRanges = firstAxis.mapIndexed { index, it ->
IntProgression.fromClosedRange(it.toInt(), lastAxis[index].toInt(), 1)
}
private val iteratorAxis = closedAxisRanges.map { it.iterator() }.toTypedArray()
private val actualAxis = iteratorAxis.toList().subList(0, iteratorAxis.size - 1)
.map { it.nextInt() }
.toTypedArray()
override fun hasNext(): Boolean {
return iteratorAxis.any { it.hasNext() }
}
override fun next(): T {
val lastIndex = iteratorAxis.size - 1
val last = iteratorAxis[lastIndex]
if (last.hasNext()) {
val axis = IntArray(actualAxis.size) { actualAxis[it] } + last.nextInt()
return factor(axis)
}
for (i in lastIndex - 1 downTo 0) {
val axis = iteratorAxis[i]
if (axis.hasNext()) {
actualAxis[i] = axis.nextInt()
iteratorAxis[i + 1] = closedAxisRanges[i + 1].iterator()
break
}
}
return next()
}
}
public class RangeIteratorWithFactor<T, POS : VectorComparable<POS>>(
start: T,
end: T,
private val factor: (POS) -> T,
private val posFactor: (T) -> POS,
) : Iterator<T> {
public val iterator: PosRangeIterator<POS> = PosRangeIterator(posFactor(start), posFactor(end), posFactor(start)::factor)
override fun hasNext(): Boolean = iterator.hasNext()
override fun next(): T = factor(iterator.next())
}
@@ -0,0 +1,19 @@
package tech.carcadex.kotlinbukkitkit.utility.extensions
import kotlin.random.Random
public fun <E> Collection<E>.randomOrNull(): E? = runCatching { random() }.getOrNull()
public fun <E> Collection<E>.randomIndex(): Int = if (size > 0) Random.nextInt(size) else -1
public fun <E> Collection<E>.randomize(): MutableList<E> {
val oldList = toMutableList()
val newList = mutableListOf<E>()
for (i in 0 until size) {
val index = oldList.randomIndex()
newList.add(oldList.get(index))
oldList.removeAt(index)
}
return newList
}
@@ -0,0 +1,34 @@
package tech.carcadex.kotlinbukkitkit.utility.extensions
private val unicodeRegex = "((\\\\u)([0-9]{4}))".toRegex()
public fun String.javaUnicodeToCharacter(): String = unicodeRegex.replace(this) {
String(charArrayOf(it.destructured.component3().toInt(16).toChar()))
}
public fun <T> T.print(): T = also { println(it) }
public fun String.centralize(
length: Int,
spacer: String = " ",
prefix: String = "",
suffix: String = "",
): String {
if (this.length >= length) return this
val part = prefix + spacer.repeat((length - this.length) / 2) + suffix
return part + this + part
}
public val TRUE_CASES: Array<String> = arrayOf("true")
get() = field.clone()
public val FALSE_CASES: Array<String> = arrayOf("false")
get() = field.clone()
public fun String.toBooleanOrNull(
trueCases: Array<String> = TRUE_CASES,
falseCases: Array<String> = FALSE_CASES,
): Boolean? = when {
trueCases.any { it.equals(this, true) } -> true
falseCases.any { it.equals(this, true) } -> false
else -> null
}
@@ -0,0 +1,87 @@
package tech.carcadex.kotlinbukkitkit.utility.types
import tech.carcadex.kotlinbukkitkit.utility.extensions.PosRange
import tech.carcadex.kotlinbukkitkit.utility.extensions.PosRangeIterator
import org.bukkit.Chunk
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.block.Block
import kotlin.math.sqrt
public fun blockPosOf(x: Int, y: Int, z: Int): BlockPos = BlockPos(x, y, z)
public fun locationPosOf(x: Double, y: Double, z: Double, yaw: Float = 0f, pitch: Float = 0f): LocationPos = LocationPos(x, y, z, yaw, pitch)
public fun chunkPosOf(x: Int, z: Int): ChunkPos = ChunkPos(x, z)
public fun Location.asPos(): LocationPos = LocationPos(x, y, z, yaw, pitch)
public fun LocationPos.asBukkitBlock(world: World): Block = world.getBlockAt(x.toInt(), y.toInt(), z.toInt())
public fun LocationPos.asBukkitLocation(world: World): Location = Location(world, x, y, z)
public fun LocationPos.asBlockPos(): BlockPos = BlockPos(x.toInt(), y.toInt(), z.toInt())
public fun Block.asPos(): BlockPos = BlockPos(x, y, z)
public fun Location.asBlockPos(): BlockPos = BlockPos(blockX, blockY, blockZ)
public fun BlockPos.asBukkitBlock(world: World): Block = world.getBlockAt(x, y, z)
public fun BlockPos.asBukkitLocation(world: World?): Location = Location(world, x.toDouble(), y.toDouble(), z.toDouble())
public fun BlockPos.asLocationPos(): LocationPos = LocationPos(x.toDouble(), y.toDouble(), z.toDouble(), 0f, 0f)
public fun BlockPos.asChunkPos(): ChunkPos = ChunkPos(x shr 4, z shr 4)
public fun Chunk.asPos(): ChunkPos = ChunkPos(x, z)
public fun ChunkPos.asBukkitChunk(world: World): Chunk = world.getChunkAt(x, z)
public data class BlockPos(
var x: Int,
var y: Int,
var z: Int,
) : VectorComparable<BlockPos> {
override fun axis(): DoubleArray = doubleArrayOf(x.toDouble(), y.toDouble(), z.toDouble())
override fun factor(axis: IntArray): BlockPos = BlockPos(axis[0], axis[1], axis[2])
}
public data class LocationPos(
var x: Double,
var y: Double,
var z: Double,
val yaw: Float,
val pitch: Float,
) : VectorComparable<LocationPos> {
override fun axis(): DoubleArray = doubleArrayOf(x, y, z)
override fun factor(axis: IntArray): LocationPos = LocationPos(axis[0].toDouble(), axis[1].toDouble(), axis[2].toDouble(), yaw, pitch)
}
public data class ChunkPos(
var x: Int,
var z: Int,
) : VectorComparable<ChunkPos> {
override fun axis(): DoubleArray = doubleArrayOf(x.toDouble(), z.toDouble())
override fun factor(axis: IntArray): ChunkPos = ChunkPos(axis[0], axis[1])
}
public interface VectorComparable<T : VectorComparable<T>> : Comparable<T> {
public fun axis(): DoubleArray
public fun factor(axis: IntArray): T
public operator fun rangeTo(other: T): PosRange<T, T> {
return PosRange(this as T, other) { PosRangeIterator(this, other, ::factor) }
}
override fun compareTo(other: T): Int {
val selfAxis = axis()
val otherAxis = other.axis()
val pairAxis = selfAxis.mapIndexed { index, axis -> axis to otherAxis[index] }
val (d1, d2) = calculatePythagoras(*pairAxis.toTypedArray())
return d1.compareTo(d2)
}
}
private fun Pair<Int, Int>.toDouble() = first.toDouble() to second.toDouble()
private fun Pair<Double, Double>.toInt() = first.toInt() to second.toInt()
private fun calculatePythagoras(vararg positions: Pair<Double, Double>): Pair<Double, Double> {
val pow = positions.map { (x1, x2) -> (x1 * x1) to (x2 * x2) }
val x1Sum = pow.sumOf { (x, _) -> x }
val x2Sum = pow.sumOf { (_, x) -> x }
val d1 = sqrt(x1Sum)
val d2 = sqrt(x2Sum)
return d1 to d2
}
@@ -0,0 +1,114 @@
package tech.carcadex.kotlinbukkitkit.utility.types
import kotlinx.serialization.Serializable
public const val DEFAULT_SPACER: Char = ' '
public const val DEFAULT_FORMAT_STYLE: String = "%YEAR %MONTH %WEEK %DAY %HOUR %MIN %SEC"
public val Long.formatter: TimeFormat get() = TimeFormat(this)
public val Int.formatter: TimeFormat get() = TimeFormat(this.toLong())
public val TimeFormat.PTBR: String get() = format(PTBRFormat)
public val TimeFormat.EN: String get() = format(ENFormat)
@Serializable
public data class FormatLang(
val second: String,
val seconds: String,
val minute: String,
val minutes: String,
val hour: String,
val hours: String,
val day: String,
val days: String,
val week: String,
val weeks: String,
val month: String,
val months: String,
val year: String,
val years: String,
)
/**
* @param time in second
*/
@JvmInline
public value class TimeFormat(private val time: Long) {
public fun format(
lang: FormatLang,
formatStyle: String = DEFAULT_FORMAT_STYLE,
formatSpacer: Char = DEFAULT_SPACER,
): String {
val seconds = time % 60
val minutes = time / 60 % 60
val hours = time / 3600 % 24
val days = time / 86400 % 7
val weeks = time / 604800 % 4
val months = time / 2419200 % 12
val years = time / 29030400
var formated = formatStyle.replace("%SEC", if (seconds > 0) "$seconds ${if (seconds > 1) lang.seconds else lang.second}" else "", true)
formated = formated.replace("%MIN", if (minutes > 0) "$minutes ${if (minutes > 1) lang.minutes else lang.minute}" else "", true)
formated = formated.replace("%HOUR", if (hours > 0) "$hours ${if (hours > 1) lang.hours else lang.hour}" else "", true)
formated = formated.replace("%DAY", if (days > 0) "$days ${if (days > 1) lang.days else lang.day}" else "", true)
formated = formated.replace("%WEEK", if (weeks > 0) "$weeks ${if (weeks > 1) lang.weeks else lang.week}" else "", true)
formated = formated.replace("%MONTH", if (months > 0) "$months ${if (months > 1) lang.months else lang.month}" else "", true)
formated = formated.replace("%YEAR", if (years > 0) "$years ${if (years > 1) lang.years else lang.year}" else "", true)
formated = formated.replace("$formatSpacer$formatSpacer", "$formatSpacer")
return formated.trim { it == formatSpacer }
}
}
public val PTBRFormat: FormatLang = FormatLang(
second = "segundo",
seconds = "segundos",
minute = "minuto",
minutes = "minutos",
hour = "hora",
hours = "horas",
day = "dia",
days = "dias",
week = "semana",
weeks = "semanas",
month = "mes",
months = "meses",
year = "ano",
years = "anos",
)
public val ENFormat: FormatLang = FormatLang(
second = "second",
seconds = "seconds",
minute = "minute",
minutes = "minutes",
hour = "hour",
hours = "hours",
day = "day",
days = "days",
week = "week",
weeks = "weeks",
month = "month",
months = "months",
year = "year",
years = "years",
)
@@ -0,0 +1,25 @@
package tech.carcadex.kotlinbukkitkit.utility.utils
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import kotlin.reflect.KClass
public class HashcodeComparator<T> : Comparator<T> {
override fun compare(p0: T, p1: T): Int {
return p0.hashCode().compareTo(p1.hashCode())
}
}
public object PluginComparator : Comparator<Plugin> {
override fun compare(p0: Plugin, p1: Plugin): Int {
return p0.name.compareTo(p1.name)
}
}
public object PlayerComparator : Comparator<Player> {
override fun compare(p0: Player, p1: Player): Int {
return p0.uniqueId.compareTo(p1.uniqueId)
}
}
public val KClassComparator: HashcodeComparator<KClass<*>> = HashcodeComparator<KClass<*>>()