Implement teleport cooldowns

This commit is contained in:
Gabriel Tofvesson 2021-09-24 17:49:02 +02:00
parent b5e431c7f6
commit 6079ac4852
4 changed files with 115 additions and 69 deletions

View File

@ -4,6 +4,18 @@ import java.util.*
import kotlin.Comparator
typealias Comparison<V> = (V) -> Int
typealias Cooldown = Pair<OfflinePlayer, Long>
fun Cooldown.isExpired(currentTime: Long) = second < currentTime
val COMPARATOR_COOLDOWN_PLAYER = Comparator<Cooldown> { a, b -> a.first.uniqueId.compareTo(b.first.uniqueId) }
val COMPARATOR_COOLDOWN_EXPIRY = Comparator<Cooldown> { a, b -> a.second.compareTo(b.second) }
val OfflinePlayer.COMPARISON_COOLDOWN: Comparison<Cooldown>
get() = { uniqueId.compareTo(it.first.uniqueId) }
val Long.COMPARISON_COOLDOWN: Comparison<Cooldown>
get() = { compareTo(it.second) }
val COMPARATOR_PLAYER = Comparator<OfflinePlayer> { a, b -> a.uniqueId.compareTo(b.uniqueId) }
val COMPARATOR_LOCATION = Comparator<Location> { a, b -> a.compareByOrder(b, { world!!.uid }, Location::getBlockX, Location::getBlockY, Location::getBlockZ) }

View File

@ -32,42 +32,41 @@ class MultiSortedList<E> constructor(
SortedList(list, it)
}
override fun add(element: E): Boolean {
override fun add(element: E) = add(element, true)
override fun add(element: E, searchForward: Boolean): Boolean {
extraLists.values.forEach {
it.add(element)
it.add(element, searchForward)
}
return super.add(element)
return super.add(element, searchForward)
}
override fun add(index: Int, element: E) {
add(element)
override fun add(index: Int, element: E) = add(index, element, true)
override fun add(index: Int, element: E, searchForward: Boolean) {
add(element, searchForward)
}
override fun addAll(elements: Collection<E>): Boolean {
override fun addAll(elements: Collection<E>) = addAll(elements, true)
override fun addAll(elements: Collection<E>, searchForward: Boolean): Boolean {
for (element in elements)
add(element)
add(element, searchForward)
return elements.isNotEmpty()
}
override fun addAll(index: Int, elements: Collection<E>) = addAll(elements)
override fun containsAll(elements: Collection<E>): Boolean {
for (element in elements)
if (!contains(element))
return false
override fun addAll(index: Int, elements: Collection<E>) = addAll(index, elements, true)
override fun addAll(index: Int, elements: Collection<E>, searchForward: Boolean) = addAll(elements, searchForward)
return true
}
fun contains(element: E, comparator: Comparator<E>, searchForward: Boolean = true) =
if (comparator == this.comparator) super.contains(element, searchForward)
else extraLists[comparator]!!.contains(element, searchForward)
fun contains(element: E, comparator: Comparator<E>) =
if (comparator == this.comparator) contains(element)
else extraLists[comparator]!!.contains(element)
override fun remove(element: E): Boolean {
if (super.remove(element)) {
override fun remove(element: E) = remove(element, true)
override fun remove(element: E, searchForward: Boolean): Boolean {
if (super.remove(element, searchForward)) {
extraLists.values.forEach {
it.remove(element)
it.remove(element, searchForward)
}
return true
@ -76,31 +75,32 @@ class MultiSortedList<E> constructor(
return false
}
override fun removeAt(index: Int) = removeAt(index, comparator)
override fun removeAll(elements: Collection<E>): Boolean {
override fun removeAll(elements: Collection<E>) = removeAll(elements, true)
override fun removeAll(elements: Collection<E>, searchForward: Boolean): Boolean {
var result = false
for (element in elements)
result = remove(element) || result
result = remove(element, searchForward) || result
return result
}
fun removeAt(index: Int, comparator: Comparator<in E>): E {
override fun removeAt(index: Int) = removeAt(index, comparator)
fun removeAt(index: Int, comparator: Comparator<in E>, searchForward: Boolean = true): E {
if (comparator == this.comparator) {
val result = super.removeAt(index)
extraLists.values.forEach {
it.remove(result)
it.remove(result, searchForward)
}
return result
} else {
val result = extraLists[comparator]!!.removeAt(index)
extraLists.forEach { (comp, list) -> if (comparator != comp) list.remove(result) }
super.remove(result)
extraLists.forEach { (comp, list) -> if (comparator != comp) list.remove(result, searchForward) }
super.remove(result, searchForward)
return result
}
@ -115,7 +115,7 @@ class MultiSortedList<E> constructor(
* @return Iterable object of all matching elements, or null if none exist
*/
fun getAll(comparator: Comparator<in E>, comparison: (E) -> Int): Iterable<E>? {
var index = binSearch(comparator, comparison)
var index = search(comparator, comparison)
if (index < 0) return null
val result = LinkedList<E>()
@ -133,18 +133,18 @@ class MultiSortedList<E> constructor(
}
fun findValueOrNull(comparator: Comparator<in E>, comparison: (E) -> Int): E? {
val index = binSearch(comparator, comparison)
val index = search(comparator, comparison)
if (index < 0) return null
return get(index, comparator)
}
fun binSearch(element: E, comparator: Comparator<in E>) =
if (comparator == this.comparator) binarySearch(element, comparator)
else extraLists[comparator]!!.binarySearch(element, comparator)
fun search(element: E, comparator: Comparator<in E>, searchStartToEnd: Boolean = true) =
if (comparator == this.comparator) search(element, searchStartToEnd)
else extraLists[comparator]!!.search(element, searchStartToEnd)
fun binSearch(comparator: Comparator<in E>, comparison: (E) -> Int) =
if (comparator == this.comparator) binarySearch(comparison = comparison)
else extraLists[comparator]!!.binarySearch(comparison = comparison)
fun search(comparator: Comparator<in E>, comparison: (E) -> Int) =
if (comparator == this.comparator) search(comparison)
else extraLists[comparator]!!.search(comparison)
}

View File

@ -27,8 +27,8 @@ class PortalManager(private val data: ConfigurationSection, private val config:
private var portals = MultiSortedList(::ArrayList, COMPARATOR_PORTAL_LOCATION_OWNER, COMPARATOR_PORTAL_UID, COMPARATOR_PORTAL_OWNER_NAME, COMPARATOR_PORTAL_LINKS)
private var invitations = MultiSortedList(::ArrayList, COMPARATOR_INVITE_RECIPIENT, COMPARATOR_INVITE_PORTAL)
private val cooldowns = LinkedList<Pair<OfflinePlayer, Long>>()
private val cooldownsLookup = SortedList<OfflinePlayer>(ArrayList(), COMPARATOR_PLAYER)
// Player-based list needs to handle random access efficiently, whereas expiry list will always be accessed sequentially
private val cooldowns = MultiSortedList(ArrayList(), ::LinkedList, COMPARATOR_COOLDOWN_PLAYER, COMPARATOR_COOLDOWN_EXPIRY)
private var cooldownTime = DEFAULT_COOLDOWN
@ -48,7 +48,7 @@ class PortalManager(private val data: ConfigurationSection, private val config:
var msb = field.mostSignificantBits.toULong()
// Start sequential search at the resulting index if it is populated
val index = portals.binSearch(COMPARATOR_PORTAL_UID) {
val index = portals.search(COMPARATOR_PORTAL_UID) {
compareValues(
{ msb } to { it.id.mostSignificantBits.toULong() },
{ lsb } to { it.id.leastSignificantBits.toULong() }
@ -139,14 +139,14 @@ class PortalManager(private val data: ConfigurationSection, private val config:
compareValues(
recipient::getUniqueId to it.recipient::getUniqueId,
portals.get(
portals.binSearch(COMPARATOR_PORTAL_UID, it.portalID.COMPARISON_PORTAL_ID), COMPARATOR_PORTAL_UID
portals.search(COMPARATOR_PORTAL_UID, it.portalID.COMPARISON_PORTAL_ID), COMPARATOR_PORTAL_UID
).owner::getUniqueId to sender::getUniqueId
)
}
fun invitePlayer(player: OfflinePlayer, portal: Portal): Boolean {
// Player is already invited or already has a pending invitation
if (player in portal.accessExclusions || invitations.binSearch(COMPARATOR_INVITE_RECIPIENT) {
if (player in portal.accessExclusions || invitations.search(COMPARATOR_INVITE_RECIPIENT) {
compareValues(player::getUniqueId to it.recipient::getUniqueId, portal::id to it::portalID)
} >= 0)
return false
@ -157,7 +157,7 @@ class PortalManager(private val data: ConfigurationSection, private val config:
}
fun cancelInvite(player: OfflinePlayer, portal: Portal): Boolean {
val index = invitations.binSearch(COMPARATOR_INVITE_RECIPIENT) {
val index = invitations.search(COMPARATOR_INVITE_RECIPIENT) {
compareValues(
player::getUniqueId to it.recipient::getUniqueId,
portal::id to it::portalID
@ -192,7 +192,7 @@ class PortalManager(private val data: ConfigurationSection, private val config:
invitations.getAll(comparator, comparison)?.forEach(invitations::remove)
fun removePortal(owner: OfflinePlayer, name: String): Boolean {
val index = portals.binSearch(COMPARATOR_PORTAL_OWNER_NAME) {
val index = portals.search(COMPARATOR_PORTAL_OWNER_NAME) {
compareValues(owner::getUniqueId to it.owner::getUniqueId, { name } to it::name)
}
@ -209,7 +209,7 @@ class PortalManager(private val data: ConfigurationSection, private val config:
}
fun getPortal(owner: OfflinePlayer, name: String): Portal? {
val index = portals.binSearch(COMPARATOR_PORTAL_OWNER_NAME) {
val index = portals.search(COMPARATOR_PORTAL_OWNER_NAME) {
compareValues(owner::getUniqueId to it.owner::getUniqueId, { name } to it::name)
}
@ -223,24 +223,20 @@ class PortalManager(private val data: ConfigurationSection, private val config:
private fun popCooldowns() {
val time = System.currentTimeMillis()
while (true) {
val front = cooldowns.first
if (front.second < time) {
cooldowns.removeFirst()
cooldownsLookup.remove(front.first)
}
while (cooldowns.isNotEmpty()) {
val front = cooldowns.get(0, COMPARATOR_COOLDOWN_EXPIRY)
if (front.isExpired(time)) cooldowns.removeAt(0, COMPARATOR_COOLDOWN_EXPIRY)
else break
}
}
private fun isOnCooldown(player: OfflinePlayer): Boolean {
popCooldowns()
return cooldownsLookup.contains(player)
return cooldowns.search(COMPARATOR_COOLDOWN_PLAYER, player.COMPARISON_COOLDOWN) >= 0
}
private fun triggerCooldown(player: OfflinePlayer) {
cooldowns.addLast(Pair(player, System.currentTimeMillis() + cooldownTime))
cooldowns.add(Pair(player, System.currentTimeMillis() + cooldownTime), false)
}
@EventHandler

View File

@ -1,9 +1,10 @@
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.RandomAccess
open class SortedList<E> constructor(
private val underlying: MutableList<E> = ArrayList(),
protected val comparator: Comparator<in E>
val comparator: Comparator<in E>
): MutableList<E> by underlying {
companion object {
fun <T: Comparable<T>> ofComparable(
@ -17,38 +18,46 @@ open class SortedList<E> constructor(
underlying.sortWith(comparator)
}
override fun add(element: E): Boolean {
val index = underlying.binarySearch(element, comparator)
open fun add(element: E, searchForward: Boolean): Boolean {
val index = search(element, searchForward)
underlying.add(if (index < 0) -(index + 1) else index, element)
return true
}
override fun add(index: Int, element: E) {
add(element)
}
override fun add(element: E) = add(element, true)
override fun addAll(elements: Collection<E>): Boolean {
open fun add(index: Int, element: E, searchForward: Boolean) {
add(element, searchForward)
}
override fun add(index: Int, element: E) = add(index, element, true)
override fun addAll(elements: Collection<E>): Boolean = addAll(elements, true)
open fun addAll(elements: Collection<E>, searchForward: Boolean): Boolean {
for (element in elements)
add(element)
add(element, searchForward)
return elements.isNotEmpty()
}
override fun addAll(index: Int, elements: Collection<E>) = addAll(elements)
override fun contains(element: E) = underlying.binarySearch(element, comparator) >= 0
open fun addAll(index: Int, elements: Collection<E>, searchForward: Boolean) = addAll(elements, searchForward)
override fun addAll(index: Int, elements: Collection<E>) = addAll(index, elements, true)
open fun contains(element: E, searchForward: Boolean) = search(element, searchForward) >= 0
override fun contains(element: E) = contains(element, true)
override fun containsAll(elements: Collection<E>): Boolean {
override fun containsAll(elements: Collection<E>) = containsAll(elements, true)
open fun containsAll(elements: Collection<E>, searchForward: Boolean): Boolean {
for (element in elements)
if (!contains(element))
if (!contains(element, searchForward))
return false
return true
}
override fun remove(element: E): Boolean {
val index = underlying.binarySearch(element, comparator)
override fun remove(element: E) = remove(element, true)
open fun remove(element: E, searchForward: Boolean): Boolean {
val index = search(element, searchForward)
if (index >= 0) {
underlying.removeAt(index)
@ -59,12 +68,41 @@ open class SortedList<E> constructor(
}
override fun removeAt(index: Int) = underlying.removeAt(index)
override fun removeAll(elements: Collection<E>): Boolean {
override fun removeAll(elements: Collection<E>) = removeAll(elements, true)
open fun removeAll(elements: Collection<E>, searchForward: Boolean): Boolean {
var result = false
for (element in elements)
result = remove(element) || result
result = remove(element, searchForward) || result
return result
}
fun isRandomAccess() = underlying is RandomAccess
fun search(element: E, searchStartToEnd: Boolean = true) =
if (isRandomAccess()) binarySearch(element, comparator)
else {
// Sequential search, because it's probably faster than binary search
val index = if (searchStartToEnd) indexOfFirst { comparator.compare(element, it) >= 0 }
else indexOfLast { comparator.compare(element, it) <= 0 }
if (index < 0 && searchStartToEnd) -size
else if (index < 0) -1
else if (comparator.compare(element, this[index]) == 0) index
else -(index + 1)
}
fun search(comparison: Comparison<E>, searchStartToEnd: Boolean = true) =
if (isRandomAccess()) binarySearch(comparison = comparison)
else {
// Sequential search, because it's probably faster than binary search
val index = if (searchStartToEnd) indexOfFirst { comparison(it) >= 0 }
else indexOfLast { comparison(it) <= 0 }
if (index < 0 && searchStartToEnd) -size
else if (index < 0) -1
else if (comparison(this[index]) == 0) index
else -(index + 1)
}
}