kotlin: refactor entire plugin to use mc-kutils, use an object-based configuration system, and convert to okhttp

pull/7/head
ALI Hamza 2019-10-12 11:56:25 +07:00 committed by Gitea
parent caf4dbeb4f
commit 45297d2c64
6 changed files with 115 additions and 85 deletions

@ -2,3 +2,4 @@ build/
gradle/ gradle/
.gradle/ .gradle/
.idea/ .idea/
**/out

@ -0,0 +1,49 @@
package com.terraocean.plugin
import hazae41.minecraft.kutils.bukkit.ConfigFile
import hazae41.minecraft.kutils.get
import java.io.File
import kotlin.reflect.KProperty
object Settings : TerraConfig(instance.dataFolder["config.yml"]) {
val socketURL by item("socket.url", "ws://localhost:8080/ws")
}
object Strings : TerraConfig(instance.dataFolder["strings.yml"]) {
val voteNew by message("vote.new")
val voteWarn by message("vote.warn")
val voteTimeout by message("vote.timeout")
val kickVoiceChannel by message("kick.voiceChannelLeave")
val voteKick by message("kick.voteKick")
val kickRemovedFromWhitelist by message("kick.removedFromWhitelist")
}
open class TerraConfig(file: File) : ConfigFile(file) {
inner class message(val path: String) {
init {
if (!config.contains(path)) {
set(path, "Message not found: $path")
}
}
operator fun getValue(ref: Any?, prop: KProperty<*>): String =
config.getString(path, "Message not found: $path")!!
operator fun setValue(ref: Any?, prop: KProperty<*>, value: String) = set(path, value)
}
inner class item<T>(private val path: String, private val default: T) {
init {
if (!config.contains(path)) {
set(path, default)
}
}
@Suppress("UNCHECKED_CAST")
operator fun getValue(ref: Any?, prop: KProperty<*>): T =
config.get(path, default) as? T ?: run {
throw IllegalStateException("Path $path [value ${config["path"]}]] does not conform to type required")
}
}
}

@ -1,26 +1,21 @@
package com.terraocean.plugin package com.terraocean.plugin
import com.terraocean.plugin.afk.onPlayerMovement
import com.terraocean.plugin.bridge.establishConnection import com.terraocean.plugin.bridge.establishConnection
import io.ktor.util.KtorExperimentalAPI import hazae41.minecraft.kutils.bukkit.*
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import org.bukkit.plugin.java.JavaPlugin
import java.io.File
internal lateinit var instance: TerraOceanPlugin internal lateinit var instance: TerraOceanPlugin
class TerraOceanPlugin: JavaPlugin() { class TerraOceanPlugin: BukkitPlugin() {
@KtorExperimentalAPI
override fun onEnable() { override fun onEnable() {
instance = this instance = this
config.load(File("plugin/terraocean.yml")) Strings // Initialize the config classes
MainScope().launch { Settings
listen(callback = ::onPlayerMovement)
schedule(async = true) {
establishConnection() establishConnection()
} }
logger.info("TerraOcean plugin has started.")
}
override fun onDisable() {
logger.info("TerraOcean plugin has stopped.")
} }
} }

@ -1,8 +1,6 @@
package com.terraocean.plugin.afk package com.terraocean.plugin.afk
import com.terraocean.plugin.bridge.reportPlayerActivity import com.terraocean.plugin.bridge.reportPlayerActivity
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.event.player.PlayerMoveEvent
//activityRequirement is the time it takes for a player to be considered inactive. //activityRequirement is the time it takes for a player to be considered inactive.
@ -11,23 +9,20 @@ const val activityRequirement = 1000 * 30
var lastMovement = HashMap<String, Long>() var lastMovement = HashMap<String, Long>()
var lastRecord = HashMap<String, Long>() var lastRecord = HashMap<String, Long>()
class AFKRecorder: Listener { fun onPlayerMovement(e: PlayerMoveEvent) {
@EventHandler val playerID = e.player.uniqueId.toString()
fun onPlayerMovement(e: PlayerMoveEvent) { val time = System.currentTimeMillis()
val playerID = e.player.uniqueId.toString()
val time = System.currentTimeMillis()
lastMovement[playerID]?.let{ lastActive -> lastMovement[playerID]?.let { lastActive ->
if (time <= lastActive + activityRequirement) { if (time <= lastActive + activityRequirement) {
return@let return@let
}
reportPlayerActivity(e.player.name, lastActive-lastRecord[playerID]!!)
lastRecord[playerID] = time
} }
reportPlayerActivity(e.player.name, lastActive - lastRecord[playerID]!!)
lastRecord[playerID] = time
}
if (lastRecord[playerID] == null) { if (lastRecord[playerID] == null) {
lastRecord[playerID] = time lastRecord[playerID] = time
}
lastMovement[playerID] = time
} }
lastMovement[playerID] = time
} }

@ -1,11 +1,10 @@
package com.terraocean.plugin.bridge package com.terraocean.plugin.bridge
import io.ktor.http.cio.websocket.send import com.terraocean.plugin.instance
import kotlinx.coroutines.MainScope import hazae41.minecraft.kutils.bukkit.schedule
import kotlinx.coroutines.launch
fun reportPlayerActivity(username: String, activity: Long) { fun reportPlayerActivity(username: String, activity: Long) {
MainScope().launch { instance.schedule(async = true) {
webSocketSession.send("active $username $activity") socket.send("active $username $activity")
} }
} }

@ -1,84 +1,75 @@
package com.terraocean.plugin.bridge package com.terraocean.plugin.bridge
import com.terraocean.plugin.Settings
import com.terraocean.plugin.Strings
import com.terraocean.plugin.instance import com.terraocean.plugin.instance
import io.ktor.client.HttpClient import hazae41.minecraft.kutils.bukkit.info
import io.ktor.client.features.websocket.DefaultClientWebSocketSession import hazae41.minecraft.kutils.bukkit.warning
import io.ktor.client.features.websocket.WebSockets import okhttp3.*
import io.ktor.client.features.websocket.ws
import io.ktor.http.cio.websocket.Frame
import io.ktor.http.cio.websocket.readText
import io.ktor.util.KtorExperimentalAPI
import org.bukkit.Bukkit import org.bukkit.Bukkit
import java.util.concurrent.TimeUnit
@KtorExperimentalAPI val client = OkHttpClient.Builder()
val client = HttpClient { .readTimeout(0, TimeUnit.MILLISECONDS)
install(WebSockets) .build()
}
lateinit var webSocketSession: DefaultClientWebSocketSession lateinit var socket: WebSocket
@KtorExperimentalAPI fun establishConnection() {
suspend fun establishConnection() { instance.info("establishing connection to ${Settings.socketURL}...")
client.ws( val request = Request.Builder()
host = "localhost", .url(Settings.socketURL)
port = 8080, .build()
path = "/ws"
) { socket = client.newWebSocket(request, WSListener())
webSocketSession = this
while (true) {
val message = incoming.receive()
if (message is Frame.Text) {
processMessage(message.readText().split(' '))
}
}
}
} }
fun processMessage(msg: List<String>) { fun processMessage(msg: List<String>) {
when (msg[0]) { when (msg[0]) {
"leave" -> { "leave" -> {
Bukkit.getPlayer(msg[1])?.kickPlayer( Bukkit.getPlayer(msg[1])?.kickPlayer(Strings.kickVoiceChannel)
instance.config.getString("voicechannel")
)
} }
"vote" -> { "vote" -> {
for (p in Bukkit.getOnlinePlayers()) { Bukkit.getOnlinePlayers().forEach {
p.sendMessage( it.sendMessage(Strings.voteNew)
instance.config.getString("vote.new") ?: "Message not found: vote.new"
)
} }
} }
"votewarn" -> { "votewarn" -> {
Bukkit.getPlayer(msg[1])?.sendMessage( Bukkit.getPlayer(msg[1])?.sendMessage(Strings.voteWarn)
instance.config.getString("vote.warn") ?: "Message not found: vote.warn"
)
} }
"votetimeout" -> { "votetimeout" -> {
Bukkit.getPlayer(msg[1])?.kickPlayer( Bukkit.getPlayer(msg[1])?.kickPlayer(Strings.voteTimeout)
instance.config.getString("vote.timeout") ?: "Message not found: vote.timeout"
)
} }
"kick" -> { "kick" -> {
Bukkit.getPlayer(msg[1])?.kickPlayer( Bukkit.getPlayer(msg[1])?.kickPlayer(Strings.voteKick)
instance.config.getString("kick.votekick") ?: "Message not found: kick.votekick"
)
} }
"whitelistadd" -> { "whitelistadd" -> {
instance.server.run { instance.server.run {
getOfflinePlayer(msg[1]).isWhitelisted = true getOfflinePlayer(msg[1]).isWhitelisted = true
} }
} }
"whitelistremove" -> { "whitelistremove" -> {
instance.server.run { instance.server.run {
getPlayer(msg[1])?.kickPlayer( getPlayer(msg[1])?.kickPlayer(Strings.kickRemovedFromWhitelist)
instance.config.getString("kick.removedFromWhitelist") ?:
"Message not found: kick.removedFromWhitelist"
)
getOfflinePlayer(msg[1]).isWhitelisted = false getOfflinePlayer(msg[1]).isWhitelisted = false
} }
} }
else -> instance.logger.warning( else -> instance.warning(
"Potentially out of date plugin. Unknown message: " + msg.joinToString(" ") "Potentially out of date plugin. Unknown message: " + msg.joinToString(" ")
) )
} }
} }
class WSListener: WebSocketListener() {
override fun onMessage(webSocket: WebSocket, text: String) {
processMessage(text.split(' '))
}
override fun onOpen(webSocket: WebSocket, response: Response) {
instance.info("Socket connection established to $response.")
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
TODO("Automatic reconnection")
}
}