summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-02-01 01:12:16 +0900
committerSho Sakuma <me@m1sk9.dev>2026-02-01 01:12:16 +0900
commit4793b23aaa6bdcbd24498e0af37c4ba0ba06f6dd (patch)
tree6485ce8c545caab8537569283817ea258c481047
parent14dc6dc4d29b6612f1e5fda5ceda187bc1df9d4d (diff)
downloadLunaticChat-4793b23aaa6bdcbd24498e0af37c4ba0ba06f6dd.tar.gz
LunaticChat-4793b23aaa6bdcbd24498e0af37c4ba0ba06f6dd.tar.bz2
LunaticChat-4793b23aaa6bdcbd24498e0af37c4ba0ba06f6dd.zip
feat: Add Velocity protocol
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt2
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt58
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodec.kt90
-rw-r--r--engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt43
4 files changed, 193 insertions, 0 deletions
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
index 9b6c3c6..72b7246 100644
--- a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/permission/LunaticChatPermissionNode.kt
@@ -47,6 +47,8 @@ sealed class LunaticChatPermissionNode(
object ChatModeToggle : LunaticChatPermissionNode("lunaticchat.command.lc.chatmode.toggle")
+ object VelocityStatus : LunaticChatPermissionNode("lunaticchat.command.lcv.status")
+
object Spy : LunaticChatPermissionNode("lunaticchat.spy")
object NoticeUpdate : LunaticChatPermissionNode("lunaticchat.noticeupdate")
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt
new file mode 100644
index 0000000..4f3a572
--- /dev/null
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessage.kt
@@ -0,0 +1,58 @@
+package dev.m1sk9.lunaticChat.engine.protocol
+
+import kotlinx.serialization.Serializable
+
+/**
+ * Plugin message definitions for Paper-Velocity communication
+ */
+sealed interface PluginMessage {
+ /**
+ * Handshake message (Paper → Velocity)
+ *
+ * @property pluginVersion Plugin version (e.g., "0.7.0")
+ * @property protocolMajor Protocol major version
+ * @property protocolMinor Protocol minor version
+ * @property protocolPatch Protocol patch version
+ */
+ @Serializable
+ data class Handshake(
+ val pluginVersion: String,
+ val protocolMajor: Int,
+ val protocolMinor: Int,
+ val protocolPatch: Int,
+ ) : PluginMessage
+
+ /**
+ * Handshake response message (Velocity → Paper)
+ *
+ * @property compatible true if compatible
+ * @property velocityVersion Velocity plugin version
+ * @property error Error message (when incompatible)
+ */
+ @Serializable
+ data class HandshakeResponse(
+ val compatible: Boolean,
+ val velocityVersion: String,
+ val error: String? = null,
+ ) : PluginMessage
+
+ /**
+ * Status request message (Paper → Velocity)
+ */
+ @Serializable
+ data object StatusRequest : PluginMessage
+
+ /**
+ * Status response message (Velocity → Paper)
+ *
+ * @property velocityVersion Velocity plugin version
+ * @property protocolVersion Protocol version
+ * @property online true if connection is active
+ */
+ @Serializable
+ data class StatusResponse(
+ val velocityVersion: String,
+ val protocolVersion: String,
+ val online: Boolean,
+ ) : PluginMessage
+}
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodec.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodec.kt
new file mode 100644
index 0000000..042a7dc
--- /dev/null
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/PluginMessageCodec.kt
@@ -0,0 +1,90 @@
+package dev.m1sk9.lunaticChat.engine.protocol
+
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.DataInputStream
+import java.io.DataOutputStream
+
+/**
+ * Plugin message encoding/decoding
+ *
+ * Format: [subChannel: UTF][messageJson: UTF]
+ */
+object PluginMessageCodec {
+ private val json = Json { ignoreUnknownKeys = true }
+
+ /**
+ * Sub-channel names
+ */
+ object SubChannel {
+ const val HANDSHAKE = "handshake"
+ const val HANDSHAKE_RESPONSE = "handshake_response"
+ const val STATUS_REQUEST = "status_request"
+ const val STATUS_RESPONSE = "status_response"
+ }
+
+ /**
+ * Encodes message
+ *
+ * @param message Message to encode
+ * @return Encoded byte array
+ */
+ fun encode(message: PluginMessage): ByteArray {
+ val out = ByteArrayOutputStream()
+ val dataOut = DataOutputStream(out)
+
+ val (subChannel, messageJson) =
+ when (message) {
+ is PluginMessage.Handshake -> {
+ SubChannel.HANDSHAKE to json.encodeToString(message)
+ }
+ is PluginMessage.HandshakeResponse -> {
+ SubChannel.HANDSHAKE_RESPONSE to json.encodeToString(message)
+ }
+ is PluginMessage.StatusRequest -> {
+ SubChannel.STATUS_REQUEST to "{}"
+ }
+ is PluginMessage.StatusResponse -> {
+ SubChannel.STATUS_RESPONSE to json.encodeToString(message)
+ }
+ }
+
+ dataOut.writeUTF(subChannel)
+ dataOut.writeUTF(messageJson)
+
+ return out.toByteArray()
+ }
+
+ /**
+ * Decodes message
+ *
+ * @param data Byte array to decode
+ * @return Decoded message
+ * @throws IllegalArgumentException Invalid message format
+ */
+ fun decode(data: ByteArray): PluginMessage {
+ val input = ByteArrayInputStream(data)
+ val dataIn = DataInputStream(input)
+
+ val subChannel = dataIn.readUTF()
+ val messageJson = dataIn.readUTF()
+
+ return when (subChannel) {
+ SubChannel.HANDSHAKE -> {
+ json.decodeFromString<PluginMessage.Handshake>(messageJson)
+ }
+ SubChannel.HANDSHAKE_RESPONSE -> {
+ json.decodeFromString<PluginMessage.HandshakeResponse>(messageJson)
+ }
+ SubChannel.STATUS_REQUEST -> {
+ PluginMessage.StatusRequest
+ }
+ SubChannel.STATUS_RESPONSE -> {
+ json.decodeFromString<PluginMessage.StatusResponse>(messageJson)
+ }
+ else -> throw IllegalArgumentException("Unknown sub-channel: $subChannel")
+ }
+ }
+}
diff --git a/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt
new file mode 100644
index 0000000..a57d3a7
--- /dev/null
+++ b/engine/src/main/kotlin/dev/m1sk9/lunaticChat/engine/protocol/ProtocolVersion.kt
@@ -0,0 +1,43 @@
+package dev.m1sk9.lunaticChat.engine.protocol
+
+/**
+ * LunaticChat protocol version definition
+ *
+ * Manages version of communication protocol between Paper and Velocity.
+ * MAJOR.MINOR must match; differences in PATCH are compatible.
+ */
+object ProtocolVersion {
+ const val MAJOR = 1
+ const val MINOR = 0
+ const val PATCH = 0
+
+ val version: String = "$MAJOR.$MINOR.$PATCH"
+
+ /**
+ * Checks if specified protocol version is compatible
+ *
+ * @param major Major version
+ * @param minor Minor version
+ * @return true if MAJOR and MINOR match
+ */
+ fun isCompatible(
+ major: Int,
+ minor: Int,
+ ): Boolean = MAJOR == major && MINOR == minor
+
+ /**
+ * Checks compatibility from version string
+ *
+ * @param version Version string in "MAJOR.MINOR.PATCH" format
+ * @return true if compatible
+ */
+ fun isCompatible(version: String): Boolean {
+ val parts = version.split(".")
+ if (parts.size != 3) return false
+
+ val major = parts[0].toIntOrNull() ?: return false
+ val minor = parts[1].toIntOrNull() ?: return false
+
+ return isCompatible(major, minor)
+ }
+}