Skip to content

Commit

Permalink
Add persistent cooldowns + yaml wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt-MX committed Jul 11, 2024
1 parent 615f444 commit a92232b
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ open class DeclarativeCommandBuilder(
this.coolDownCallback = Optional.ofNullable(block)
}

infix fun description(desc: String) = apply {
this.description = desc
}

infix fun permission(block: StorageCommandContext<*>.() -> Boolean) = apply {
this.permission = Optional.of(block)
}
Expand Down
20 changes: 9 additions & 11 deletions api/src/main/kotlin/com/mattmx/ktgui/cooldown/ActionCoolDown.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import java.util.*
import kotlin.math.max
import kotlin.math.min

class ActionCoolDown<T>(
open class ActionCoolDown<T : Any>(
duration: Duration
) {
) : CoolDown<T> {
var shouldRefreshIfAlreadyOnCoolDown = false
private val expire = duration.toMillis()
private val cache = Collections.synchronizedMap(hashMapOf<Any, Long>())
Expand All @@ -16,11 +16,13 @@ class ActionCoolDown<T>(
register(this)
}

fun removeAnyUsers(vararg user: Any) = user.forEach { cache.remove(it) }
override fun removeAnyUsers(vararg user: Any) = user.forEach { cache.remove(it) }

fun removeUser(user: T) = cache.remove(user)
override fun removeUser(user: Any) {
cache.remove(user)
}

fun test(user: T): Boolean {
override fun test(user: T): Boolean {
val currentMillis = System.currentTimeMillis()
val returnValue = if (!cache.containsKey(user)) {
true
Expand All @@ -34,16 +36,12 @@ class ActionCoolDown<T>(
return returnValue
}

fun timeRemaining(user: T): Long {
override fun millisRemaining(user: T): Long {
return max(-1L, expire - (System.currentTimeMillis() - cache.getOrDefault(user, 0)))
}

fun durationRemaining(user: T) : Duration {
return Duration.ofMillis(timeRemaining(user))
}

fun test(user: T, block: (Duration) -> Unit): Unit? {
val timeLeft = timeRemaining(user)
val timeLeft = millisRemaining(user)
val valid = timeLeft < 0

if (valid || shouldRefreshIfAlreadyOnCoolDown)
Expand Down
17 changes: 17 additions & 0 deletions api/src/main/kotlin/com/mattmx/ktgui/cooldown/CoolDown.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.mattmx.ktgui.cooldown

import java.time.Duration

interface CoolDown<T : Any> {

fun test(user: T) : Boolean

fun removeAnyUsers(vararg user: Any) = user.forEach { removeUser(it) }

fun removeUser(user: Any)

fun millisRemaining(user: T) : Long

fun durationRemaining(user: T) = Duration.ofMillis(millisRemaining(user))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mattmx.ktgui.cooldown

import java.time.Duration

open class PersistentCoolDown<T : Any>(
duration: Duration,
val impl: Impl
) : CoolDown<T> {
var shouldRefreshIfAlreadyOnCoolDown = false
private val expire = duration.toMillis()

override fun test(user: T): Boolean {
val currentMillis = System.currentTimeMillis()
val returnValue = impl.get(user)?.let { currentMillis - it >= expire } ?: true

if (returnValue || shouldRefreshIfAlreadyOnCoolDown) {
impl.set(user, currentMillis)
}

return returnValue
}

override fun removeUser(user: Any) =impl.set(user, null)
override fun millisRemaining(user: T) = impl.get(user) ?: 0L

interface Impl {
fun set(user: Any, lastExecuted: Long?)
fun get(user: Any) : Long?
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.mattmx.ktgui.cooldown

import com.mattmx.ktgui.scheduling.asyncRepeat
import com.mattmx.ktgui.utils.minutes
import org.bukkit.configuration.file.FileConfiguration
import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.configuration.file.YamlConstructor
import java.io.File
import java.time.Duration

open class YamlPersistentCoolDown<T : Any>(
path: String,
file: File,
duration: Duration,
val yaml: YamlConfiguration =
if (file.exists()) YamlConfiguration.loadConfiguration(file)
else YamlConfiguration()
) : PersistentCoolDown<T>(duration, object : Impl {
override fun set(user: Any, lastExecuted: Long?) {
yaml.set("$path.$user", lastExecuted)
}

override fun get(user: Any): Long? {
return yaml.getString("$path.$user")?.toLongOrNull()
}
}) {
init {
asyncRepeat(5.minutes) {
yaml.save(file)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.mattmx.ktgui.yaml

class FileConfigurationMap {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.mattmx.ktgui.yaml

import org.bukkit.configuration.file.YamlConfiguration
import java.io.File
import java.time.Duration
import java.util.*

class FileConfigurationWrapper(
val file: File,
val autoSaveOptions: Options = Options()
) : YamlConfiguration() {
var lastAutoSaved = System.currentTimeMillis()
var isDirty: Boolean = false
private set

init {
load(file)
}

fun markDirty() {
isDirty = true
}

fun tickAutoSave() : Boolean {
if (!shouldSave()) return false
save()
return true
}

fun shouldSave() = isDirty
&& autoSaveOptions.autoSaveTicks.isPresent
&& System.currentTimeMillis() - lastAutoSaved >= autoSaveOptions.autoSaveTicks.get().toMillis()

fun save() = save(file)

class Options {
var autoSaveTicks = Optional.of(Duration.ofSeconds(30L))
}
}
26 changes: 26 additions & 0 deletions api/src/main/kotlin/com/mattmx/ktgui/yaml/Warp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mattmx.ktgui.yaml

import com.mattmx.ktgui.commands.declarative.runs
import com.mattmx.ktgui.utils.not
import org.bukkit.Location
import org.bukkit.entity.Player

class Warp(
val id: String,
var displayName: String,
var location: Location,
var directCommandWarp: Boolean = true
) {

fun registerDirectWarpCommand() {
(id).runs<Player> {
teleport(sender)
}
}

fun teleport(player: Player) {
player.sendMessage(!WarpsPlugin.get().msg_teleporting)
player.teleportAsync(location)
}

}
91 changes: 91 additions & 0 deletions api/src/main/kotlin/com/mattmx/ktgui/yaml/WarpsPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.mattmx.ktgui.yaml

import com.mattmx.ktgui.GuiManager
import com.mattmx.ktgui.commands.declarative.arg.impl.booleanArgument
import com.mattmx.ktgui.commands.declarative.arg.impl.multiChoiceArgument
import com.mattmx.ktgui.commands.declarative.arg.impl.stringArgument
import com.mattmx.ktgui.commands.declarative.div
import com.mattmx.ktgui.commands.declarative.invoke
import com.mattmx.ktgui.commands.declarative.runs
import com.mattmx.ktgui.utils.not
import com.mattmx.ktgui.utils.placeholders
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin

class WarpsPlugin : JavaPlugin() {
val warps by yaml {
getKeys(false)
.mapNotNull { k -> getObject(k, Warp::class.java)?.let { k to it } }
.toMap(HashMap())
}

val lang by yaml()
val msg_set_warp: String by lang("&aSet warp %warp_id% to location (%x%, %y%, %z%, %pitch%, %yaw%) in world '%world%'")
val msg_del_warp: String by lang("&aDeleted warp %warp_id%")
val msg_invalid_warp_id: String by lang("&cInvalid warp ID")
val msg_teleporting: String by lang("&aTeleporting...")
val msg_modify_warp_display: String by lang("&aSet display of %warp_id% to %warp_display%")
val msg_modify_warp_location: String by lang("&aMoved display of %warp_id% to your position")
val msg_modify_warp_directCommandWarp: String by lang("&aSet direct command of %warp_id% to %warp_directCommandWarp")

override fun onEnable() {
instance = this
GuiManager.init(this)

val newWarpId by stringArgument()
newWarpId matches "[0-9a-z_]{3,16}".toRegex(RegexOption.IGNORE_CASE)
newWarpId invalid { reply(!msg_invalid_warp_id) }

("set-warp" / newWarpId).runs<Player> {
val warp = Warp(
newWarpId(),
newWarpId(),
sender.location.clone()
)

warps[warp.id] = warp

reply(msg_set_warp placeholders sender)
} register this

val existingWarp by multiChoiceArgument(warps)
existingWarp invalid { reply(!msg_invalid_warp_id) }

("modify-warp" / existingWarp) {
val display by stringArgument()
("display" / display).runs<Player> {
existingWarp().displayName = display()
reply(!msg_modify_warp_display)
}

("move-here").runs<Player> {
existingWarp().location = sender.location.clone()
reply(!msg_modify_warp_location)
}

val enabled by booleanArgument()
("direct-command" / enabled).runs<Player> {
val newState = enabled()
existingWarp().directCommandWarp = newState
reply(!msg_modify_warp_directCommandWarp)
} description "Should this warp have a direct command? e.g /warp spawn -> /spawn"
}

("warp" / existingWarp).runs<Player> {
existingWarp().teleport(sender)
} register this

("del-warp" / existingWarp).runs<Player> {
reply(msg_del_warp placeholders sender)
} register this

warps.values
.filter(Warp::directCommandWarp)
.forEach(Warp::registerDirectWarpCommand)
}

companion object {
private lateinit var instance: WarpsPlugin
fun get() = instance
}
}
59 changes: 59 additions & 0 deletions api/src/main/kotlin/com/mattmx/ktgui/yaml/yml.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.mattmx.ktgui.yaml

import org.bukkit.plugin.java.JavaPlugin
import java.io.File
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

fun JavaPlugin.yaml() : ReadOnlyProperty<Any?, FileConfigurationWrapper> {
var instance: FileConfigurationWrapper? = null
return ReadOnlyProperty { ref: Any?, prop: KProperty<*> ->
if (instance != null) return@ReadOnlyProperty instance!!

instance = yaml(prop.name + ".yml")
return@ReadOnlyProperty instance!!
}
}

fun <T : Any> JavaPlugin.yaml(get: FileConfigurationWrapper.() -> T) : ReadOnlyProperty<Any?, T> {
var yamlFile: FileConfigurationWrapper? = null
var instance: T? = null

// todo replace with ReadWriteProperty, list/map wrapper object
return ReadOnlyProperty { ref: Any?, prop: KProperty<*> ->
if (yamlFile != null)
return@ReadOnlyProperty instance!!

yamlFile = yaml(prop.name + ".yml")
instance = get(yamlFile!!)
return@ReadOnlyProperty instance!!
}
}

fun JavaPlugin.yaml(path: String): FileConfigurationWrapper {
val file = File("$dataFolder/path")

if (!file.exists()) {
kotlin.runCatching {
saveResource(path, false)
}.getOrNull() ?: file.createNewFile()
}

return FileConfigurationWrapper(file)
}

inline operator fun <reified T : Any> FileConfigurationWrapper.getValue(thisRef: Any?, property: KProperty<*>): T? {
return getObject(pathFromPropertyName(property.name), T::class.java)
}

inline operator fun <reified T : Any> FileConfigurationWrapper.invoke(default: T) =
ReadOnlyProperty { thisRef: Any?, prop ->
val path = pathFromPropertyName(prop.name)
val inConfig = getObject(path, T::class.java)
return@ReadOnlyProperty if (inConfig == null) {
set(path, default)
default
} else inConfig
}

fun pathFromPropertyName(name: String) = name.replace("_", ".")

0 comments on commit a92232b

Please sign in to comment.