From ba18fcddbc5dfba9433a2f0c87fc8b984a5f00b3 Mon Sep 17 00:00:00 2001 From: Logan Gorence Date: Sat, 29 Jan 2022 06:01:50 +0000 Subject: [PATCH] Add anti-idle feature (Closes #21). --- build.gradle.kts | 1 + foundation-core/build.gradle.kts | 1 + .../core/features/player/PlayerConfig.kt | 17 +++++ .../core/features/player/PlayerFeature.kt | 74 +++++++++++++++++++ .../src/main/resources/player.yaml | 10 +++ 5 files changed, 103 insertions(+) create mode 100644 foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerConfig.kt create mode 100644 foundation-core/src/main/resources/player.yaml diff --git a/build.gradle.kts b/build.gradle.kts index 23535ef..89a67d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -87,6 +87,7 @@ subprojects { implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + // Core libraries. implementation("io.insert-koin:koin-core:3.1.4") testImplementation("io.insert-koin:koin-test:3.1.4") diff --git a/foundation-core/build.gradle.kts b/foundation-core/build.gradle.kts index edc921b..8093d11 100644 --- a/foundation-core/build.gradle.kts +++ b/foundation-core/build.gradle.kts @@ -3,4 +3,5 @@ dependencies { implementation("software.amazon.awssdk:s3:2.17.102") implementation("org.quartz-scheduler:quartz:2.3.2") + implementation("com.google.guava:guava:31.0.1-jre") } diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerConfig.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerConfig.kt new file mode 100644 index 0000000..2634007 --- /dev/null +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerConfig.kt @@ -0,0 +1,17 @@ +package cloud.kubelet.foundation.core.features.player + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PlayerConfig( + @SerialName("anti-idle") + val antiIdle: AntiIdleConfig = AntiIdleConfig(), +) + +@Serializable +data class AntiIdleConfig( + val enabled: Boolean = false, + val idleDuration: Int = 3600, + val ignore: List = listOf(), +) diff --git a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerFeature.kt b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerFeature.kt index 35b6046..4bea4f1 100644 --- a/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerFeature.kt +++ b/foundation-core/src/main/kotlin/cloud/kubelet/foundation/core/features/player/PlayerFeature.kt @@ -1,14 +1,88 @@ package cloud.kubelet.foundation.core.features.player +import cloud.kubelet.foundation.core.FoundationCorePlugin +import cloud.kubelet.foundation.core.Util import cloud.kubelet.foundation.core.abstraction.Feature +import com.charleskorn.kaml.Yaml +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder +import com.google.common.cache.RemovalCause +import net.kyori.adventure.text.Component import org.bukkit.GameMode +import org.bukkit.event.EventHandler +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerKickEvent +import org.bukkit.event.player.PlayerMoveEvent +import org.bukkit.event.player.PlayerQuitEvent +import org.koin.core.component.inject +import java.time.Duration +import kotlin.io.path.inputStream class PlayerFeature : Feature() { + private val config by inject() + private lateinit var playerActivity: Cache + override fun enable() { + playerActivity = CacheBuilder.newBuilder() + .expireAfterWrite(Duration.ofSeconds(config.antiIdle.idleDuration.toLong())) + .removalListener z@{ + if (!config.antiIdle.enabled) return@z + if (it.cause == RemovalCause.EXPIRED) { + if (!config.antiIdle.ignore.contains(it.key!!)) { + plugin.server.scheduler.runTask(plugin) { -> + plugin.server.getPlayer(it.key!!) + ?.kick(Component.text("Kicked for idling"), PlayerKickEvent.Cause.IDLING) + } + } + } + }.build() + + // Expire player activity tokens occasionally. + plugin.server.scheduler.scheduleSyncRepeatingTask(plugin, { + playerActivity.cleanUp() + }, 20, 100) + registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL)) registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE)) registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE)) registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR)) registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand()) } + + override fun module() = org.koin.dsl.module { + single { + val configPath = Util.copyDefaultConfig( + plugin.slF4JLogger, + plugin.pluginDataPath, + "player.yaml", + ) + return@single Yaml.default.decodeFromStream( + PlayerConfig.serializer(), + configPath.inputStream() + ) + } + } + + @EventHandler + private fun onPlayerJoin(e: PlayerJoinEvent) { + if (!config.antiIdle.enabled) return + + playerActivity.put(e.player.name, e.player.name) + } + + @EventHandler + private fun onPlayerQuit(e: PlayerQuitEvent) { + if (!config.antiIdle.enabled) return + + playerActivity.invalidate(e.player.name) + } + + @EventHandler + private fun onPlayerMove(e: PlayerMoveEvent) { + if (!config.antiIdle.enabled) return + + if (e.hasChangedPosition() || e.hasChangedOrientation()) { + playerActivity.put(e.player.name, e.player.name) + } + } } diff --git a/foundation-core/src/main/resources/player.yaml b/foundation-core/src/main/resources/player.yaml new file mode 100644 index 0000000..e06d620 --- /dev/null +++ b/foundation-core/src/main/resources/player.yaml @@ -0,0 +1,10 @@ +# Kicks players idle for longer than a set amount of time. +anti-idle: + # Whether anti-idle kicking is enabled. + enabled: false + + # Number of seconds the player is idle before kick. + idleDuration: 3600 + + # List of usernames to ignore. + ignore: []