mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 03:39:37 +00:00 
			
		
		
		
	Add anti-idle feature (Closes #21).
This commit is contained in:
		@ -87,6 +87,7 @@ subprojects {
 | 
				
			|||||||
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
 | 
					    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
 | 
				
			||||||
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
					    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Core libraries.
 | 
				
			||||||
    implementation("io.insert-koin:koin-core:3.1.4")
 | 
					    implementation("io.insert-koin:koin-core:3.1.4")
 | 
				
			||||||
    testImplementation("io.insert-koin:koin-test:3.1.4")
 | 
					    testImplementation("io.insert-koin:koin-test:3.1.4")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,4 +3,5 @@ dependencies {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  implementation("software.amazon.awssdk:s3:2.17.102")
 | 
					  implementation("software.amazon.awssdk:s3:2.17.102")
 | 
				
			||||||
  implementation("org.quartz-scheduler:quartz:2.3.2")
 | 
					  implementation("org.quartz-scheduler:quartz:2.3.2")
 | 
				
			||||||
 | 
					  implementation("com.google.guava:guava:31.0.1-jre")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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<String> = listOf(),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@ -1,14 +1,88 @@
 | 
				
			|||||||
package cloud.kubelet.foundation.core.features.player
 | 
					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 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.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() {
 | 
					class PlayerFeature : Feature() {
 | 
				
			||||||
 | 
					  private val config by inject<PlayerConfig>()
 | 
				
			||||||
 | 
					  private lateinit var playerActivity: Cache<String, String>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun enable() {
 | 
					  override fun enable() {
 | 
				
			||||||
 | 
					    playerActivity = CacheBuilder.newBuilder()
 | 
				
			||||||
 | 
					      .expireAfterWrite(Duration.ofSeconds(config.antiIdle.idleDuration.toLong()))
 | 
				
			||||||
 | 
					      .removalListener<String, String> 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("survival", "s"), GamemodeCommand(GameMode.SURVIVAL))
 | 
				
			||||||
    registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE))
 | 
					    registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE))
 | 
				
			||||||
    registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
 | 
					    registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
 | 
				
			||||||
    registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
 | 
					    registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
 | 
				
			||||||
    registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand())
 | 
					    registerCommandExecutor(listOf("localweather", "lw"), LocalWeatherCommand())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun module() = org.koin.dsl.module {
 | 
				
			||||||
 | 
					    single {
 | 
				
			||||||
 | 
					      val configPath = Util.copyDefaultConfig<FoundationCorePlugin>(
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								foundation-core/src/main/resources/player.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								foundation-core/src/main/resources/player.yaml
									
									
									
									
									
										Normal file
									
								
							@ -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: []
 | 
				
			||||||
		Reference in New Issue
	
	Block a user