🚀 Finishing up the Chat Sync
This commit is contained in:
		@@ -7,8 +7,7 @@ import dev.solsynth.snConnect.models.SnChatMessage
 | 
			
		||||
import dev.solsynth.snConnect.models.WebSocketPacket
 | 
			
		||||
import dev.solsynth.snConnect.services.SnMessageService
 | 
			
		||||
import dev.solsynth.snConnect.services.SnService
 | 
			
		||||
import kotlinx.coroutines.GlobalScope
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import net.md_5.bungee.api.ChatColor
 | 
			
		||||
import net.md_5.bungee.api.chat.ClickEvent
 | 
			
		||||
import net.md_5.bungee.api.chat.ComponentBuilder
 | 
			
		||||
@@ -26,6 +25,8 @@ class SolarNetworkConnect : JavaPlugin() {
 | 
			
		||||
    private var messageService: SnMessageService? = null
 | 
			
		||||
    private var syncChatRooms: List<String> = emptyList()
 | 
			
		||||
    private var destinationChatId: String? = null
 | 
			
		||||
    private var webSocketJob: Job? = null
 | 
			
		||||
    private var messages: Map<String, String> = mapOf()
 | 
			
		||||
 | 
			
		||||
    private fun handleWebSocketPacket(packet: WebSocketPacket) {
 | 
			
		||||
        // logger.info("Received WebSocket packet: type=${packet.type}")
 | 
			
		||||
@@ -79,6 +80,13 @@ class SolarNetworkConnect : JavaPlugin() {
 | 
			
		||||
 | 
			
		||||
        this.saveDefaultConfig()
 | 
			
		||||
 | 
			
		||||
        messages = mapOf(
 | 
			
		||||
            "join" to "➡️ {player} joined the game.",
 | 
			
		||||
            "quit" to "⬅️ {player} left the game.",
 | 
			
		||||
            "death" to "💀 {player} {message}",
 | 
			
		||||
            "advancement" to "🎉 {player} unlocked advancement: {advancement}"
 | 
			
		||||
        ) + (config.getConfigurationSection("messages")?.getValues(false) as? Map<String, String> ?: emptyMap())
 | 
			
		||||
 | 
			
		||||
        if (!setupNetwork()) {
 | 
			
		||||
            logger.severe("Failed to setup Solar Network Network, check your configuration.")
 | 
			
		||||
        }
 | 
			
		||||
@@ -89,7 +97,7 @@ class SolarNetworkConnect : JavaPlugin() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (messageService != null && destinationChatId != null) {
 | 
			
		||||
            server.pluginManager.registerEvents(SnChatListener(messageService!!, destinationChatId!!), this)
 | 
			
		||||
            server.pluginManager.registerEvents(SnChatListener(messageService!!, destinationChatId!!, messages), this)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Bukkit.getPluginCommand("solar")!!.setExecutor(SnCommand(this.sn!!, this.economy))
 | 
			
		||||
@@ -105,6 +113,8 @@ class SolarNetworkConnect : JavaPlugin() {
 | 
			
		||||
 | 
			
		||||
    override fun onDisable() {
 | 
			
		||||
        logger.info(String.format("Disabled Version %s", description.version));
 | 
			
		||||
        sn?.disconnect()
 | 
			
		||||
        webSocketJob?.cancel()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupNetwork(): Boolean {
 | 
			
		||||
@@ -118,7 +128,7 @@ class SolarNetworkConnect : JavaPlugin() {
 | 
			
		||||
        destinationChatId = destination
 | 
			
		||||
        sn = SnService(baseUrl, clientId, clientSecret, botApiKey);
 | 
			
		||||
        messageService = SnMessageService(sn!!)
 | 
			
		||||
        GlobalScope.launch {
 | 
			
		||||
        webSocketJob = GlobalScope.launch {
 | 
			
		||||
            sn!!.connectWebSocketAsFlow().collect { packet ->
 | 
			
		||||
                handleWebSocketPacket(packet)
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
package dev.solsynth.snConnect.listeners
 | 
			
		||||
 | 
			
		||||
import dev.solsynth.snConnect.services.SnMessageService
 | 
			
		||||
import org.bukkit.entity.Player
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
import org.bukkit.event.entity.PlayerDeathEvent
 | 
			
		||||
import org.bukkit.event.player.AsyncPlayerChatEvent
 | 
			
		||||
import org.bukkit.event.player.PlayerAdvancementDoneEvent
 | 
			
		||||
import org.bukkit.event.player.PlayerJoinEvent
 | 
			
		||||
import org.bukkit.event.player.PlayerQuitEvent
 | 
			
		||||
 | 
			
		||||
class SnChatListener(private val messageService: SnMessageService, private val destinationChatId: String) : Listener {
 | 
			
		||||
class SnChatListener(private val messageService: SnMessageService, private val destinationChatId: String, private val messages: Map<String, String>) : Listener {
 | 
			
		||||
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onPlayerChat(event: AsyncPlayerChatEvent) {
 | 
			
		||||
@@ -17,13 +20,30 @@ class SnChatListener(private val messageService: SnMessageService, private val d
 | 
			
		||||
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onPlayerJoin(event: PlayerJoinEvent) {
 | 
			
		||||
        val message = "${event.player.name} joined the game."
 | 
			
		||||
        val template = messages["join"] ?: "➡️ {player} joined the game."
 | 
			
		||||
        val message = template.replace("{player}", event.player.name)
 | 
			
		||||
        messageService.sendMessage(destinationChatId, message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onPlayerQuit(event: PlayerQuitEvent) {
 | 
			
		||||
        val message = "${event.player.name} left the game."
 | 
			
		||||
        val template = messages["quit"] ?: "⬅️ {player} left the game."
 | 
			
		||||
        val message = template.replace("{player}", event.player.name)
 | 
			
		||||
        messageService.sendMessage(destinationChatId, message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onPlayerDeath(event: PlayerDeathEvent) {
 | 
			
		||||
        val template = messages["death"] ?: "💀 {player} {message}"
 | 
			
		||||
        val message = template.replace("{player}", event.entity.name).replace("{message}", event.deathMessage ?: "")
 | 
			
		||||
        messageService.sendMessage(destinationChatId, message)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onPlayerAdvancement(event: PlayerAdvancementDoneEvent) {
 | 
			
		||||
        val advancement = event.advancement.key.toString().substringAfter("minecraft:")
 | 
			
		||||
        val template = messages["advancement"] ?: "🎉 {player} unlocked advancement: {advancement}"
 | 
			
		||||
        val message = template.replace("{player}", event.player.name).replace("{advancement}", advancement)
 | 
			
		||||
        messageService.sendMessage(destinationChatId, message)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
package dev.solsynth.snConnect.services
 | 
			
		||||
 | 
			
		||||
import dev.solsynth.snConnect.models.WebSocketPacket
 | 
			
		||||
import kotlinx.coroutines.channels.awaitClose
 | 
			
		||||
import kotlinx.coroutines.GlobalScope
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.callbackFlow
 | 
			
		||||
import kotlinx.coroutines.flow.channelFlow
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.WebSocket
 | 
			
		||||
@@ -15,6 +17,7 @@ import java.util.logging.Logger
 | 
			
		||||
class SnService(private val baseUrl: String, val clientId: String, val clientSecret: String, val botApiKey: String?) {
 | 
			
		||||
    val client = OkHttpClient.Builder().build();
 | 
			
		||||
    private val logger = Logger.getLogger(SnService::class.java.name)
 | 
			
		||||
    private var websocket: WebSocket? = null
 | 
			
		||||
 | 
			
		||||
    fun getUrl(service: String, segment: String): String {
 | 
			
		||||
        return "$baseUrl/$service$segment"
 | 
			
		||||
@@ -34,40 +37,60 @@ class SnService(private val baseUrl: String, val clientId: String, val clientSec
 | 
			
		||||
        return client.newWebSocket(request, listener)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun connectWebSocketAsFlow(): Flow<WebSocketPacket> = callbackFlow {
 | 
			
		||||
        val url = "${getWsBaseUrl()}/ws";
 | 
			
		||||
    fun connectWebSocketAsFlow(): Flow<WebSocketPacket> = channelFlow {
 | 
			
		||||
        val url = "${getWsBaseUrl()}/ws"
 | 
			
		||||
        val request = Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .apply {
 | 
			
		||||
                botApiKey?.let { header("Authorization", "Bearer $it") }
 | 
			
		||||
            }
 | 
			
		||||
            .build()
 | 
			
		||||
        val websocket = client.newWebSocket(request, object : WebSocketListener() {
 | 
			
		||||
            override fun onOpen(webSocket: WebSocket, response: Response) {
 | 
			
		||||
                logger.info("WebSocket connection opened to $url")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun onMessage(webSocket: WebSocket, text: String) {
 | 
			
		||||
                WebSocketPacket.fromJson(text)?.let { trySend(it) }
 | 
			
		||||
            }
 | 
			
		||||
        fun connect() {
 | 
			
		||||
            websocket = client.newWebSocket(request, object : WebSocketListener() {
 | 
			
		||||
                override fun onOpen(webSocket: WebSocket, response: Response) {
 | 
			
		||||
                    logger.info("WebSocket connection opened to $url")
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
 | 
			
		||||
                val text = bytes.string(Charsets.UTF_8)
 | 
			
		||||
                WebSocketPacket.fromJson(text)?.let { trySend(it) }
 | 
			
		||||
            }
 | 
			
		||||
                override fun onMessage(webSocket: WebSocket, text: String) {
 | 
			
		||||
                    WebSocketPacket.fromJson(text)?.let { trySend(it) }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 | 
			
		||||
                logger.severe("WebSocket connection failed: ${t.message}, response: ${response?.code}")
 | 
			
		||||
                close(t)
 | 
			
		||||
            }
 | 
			
		||||
                override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
 | 
			
		||||
                    val text = bytes.string(Charsets.UTF_8)
 | 
			
		||||
                    WebSocketPacket.fromJson(text)?.let { trySend(it) }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
 | 
			
		||||
                    logger.severe("WebSocket connection failed: ${t.message}, response: ${response?.code}")
 | 
			
		||||
                    websocket = null
 | 
			
		||||
                    GlobalScope.launch {
 | 
			
		||||
                        delay(1000)
 | 
			
		||||
                        connect()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
 | 
			
		||||
                    logger.info("WebSocket connection closed: code=$code, reason=$reason")
 | 
			
		||||
                    websocket = null
 | 
			
		||||
                    GlobalScope.launch {
 | 
			
		||||
                        delay(1000)
 | 
			
		||||
                        connect()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        connect()
 | 
			
		||||
 | 
			
		||||
            override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
 | 
			
		||||
                logger.info("WebSocket connection closed: code=$code, reason=$reason")
 | 
			
		||||
                close()
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        awaitClose {
 | 
			
		||||
            websocket.close(1000, "Flow closed")
 | 
			
		||||
            websocket?.close(1000, "Shutting down")
 | 
			
		||||
            websocket = null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun disconnect() {
 | 
			
		||||
        websocket?.close(1000, "Disconnecting")
 | 
			
		||||
        websocket = null
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,11 @@ sn:
 | 
			
		||||
  client_id: goatcraft
 | 
			
		||||
  client_secret: 12345678
 | 
			
		||||
  bot_secret: 114.514.19198
 | 
			
		||||
  destination_chat_id: some_chat_id
 | 
			
		||||
chat:
 | 
			
		||||
  sync_rooms: []
 | 
			
		||||
  outgoing_room: 00000000-0000-0000-0000-00000000008b
 | 
			
		||||
messages:
 | 
			
		||||
  join: "➡️ {player} joined the game."
 | 
			
		||||
  quit: "⬅️ {player} left the game."
 | 
			
		||||
  death: "💀 {player} {message}"
 | 
			
		||||
  advancement: "🎉 {player} unlocked advancement: {advancement}"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user