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

pull/6/head
ALI Hamza 2019-10-12 11:56:25 +07:00
parent 864d06c311
commit f4a56553cc
No known key found for this signature in database
GPG Key ID: 7C608266BF384ADC
6 changed files with 115 additions and 85 deletions

@ -2,3 +2,4 @@ build/
gradle/
.gradle/
.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
import com.terraocean.plugin.afk.onPlayerMovement
import com.terraocean.plugin.bridge.establishConnection
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import org.bukkit.plugin.java.JavaPlugin
import java.io.File
import hazae41.minecraft.kutils.bukkit.*
internal lateinit var instance: TerraOceanPlugin
class TerraOceanPlugin: JavaPlugin() {
@KtorExperimentalAPI
class TerraOceanPlugin: BukkitPlugin() {
override fun onEnable() {
instance = this
config.load(File("plugin/terraocean.yml"))
MainScope().launch {
Strings // Initialize the config classes
Settings
listen(callback = ::onPlayerMovement)
schedule(async = true) {
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
import com.terraocean.plugin.bridge.reportPlayerActivity
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerMoveEvent
//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 lastRecord = HashMap<String, Long>()
class AFKRecorder: Listener {
@EventHandler
fun onPlayerMovement(e: PlayerMoveEvent) {
val playerID = e.player.uniqueId.toString()
val time = System.currentTimeMillis()
fun onPlayerMovement(e: PlayerMoveEvent) {
val playerID = e.player.uniqueId.toString()
val time = System.currentTimeMillis()
lastMovement[playerID]?.let{ lastActive ->
if (time <= lastActive + activityRequirement) {
return@let
}
reportPlayerActivity(e.player.name, lastActive-lastRecord[playerID]!!)
lastRecord[playerID] = time
lastMovement[playerID]?.let { lastActive ->
if (time <= lastActive + activityRequirement) {
return@let
}
reportPlayerActivity(e.player.name, lastActive - lastRecord[playerID]!!)
lastRecord[playerID] = time
}
if (lastRecord[playerID] == null) {
lastRecord[playerID] = time
}
lastMovement[playerID] = time
if (lastRecord[playerID] == null) {
lastRecord[playerID] = time
}
lastMovement[playerID] = time
}

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

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