1.0.0.3 - new modules
Added utility, menu, thirdparty and exposed modules A few little fixes
This commit is contained in:
1 parent
161f9aa268
commit
3c87e2d6a1
63 files changed
+5334
-12
No files matched your search
+352
@@ -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--
|
||||
}
|
||||
}
|
||||
+175
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+238
@@ -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() }
|
||||
+270
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -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
|
||||
+122
@@ -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",
|
||||
)
|
||||
+25
@@ -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<*>>()
|
||||
Reference in new issue
Block a user