summaryrefslogtreecommitdiff
path: root/platform-velocity
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-02-01 01:10:47 +0900
committerSho Sakuma <me@m1sk9.dev>2026-02-01 01:10:47 +0900
commit8427756383f8b382d9ed615fe758a12a8b2eeb79 (patch)
treed08b5d06068a80800ab6c656468e1c712ba97634 /platform-velocity
parent77c4f168c0ed9ea77dc667e67bc5db21aa4097fe (diff)
downloadLunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.tar.gz
LunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.tar.bz2
LunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.zip
feat: Velocity intergration
Diffstat (limited to 'platform-velocity')
-rw-r--r--platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/LunaticChat.kt55
-rw-r--r--platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt161
-rw-r--r--platform-velocity/src/main/resources/velocity-plugin.json11
3 files changed, 227 insertions, 0 deletions
diff --git a/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/LunaticChat.kt b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/LunaticChat.kt
new file mode 100644
index 0000000..1d7fb90
--- /dev/null
+++ b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/LunaticChat.kt
@@ -0,0 +1,55 @@
+package dev.m1sk9.lunaticChat.velocity
+
+import com.google.inject.Inject
+import com.velocitypowered.api.event.Subscribe
+import com.velocitypowered.api.event.proxy.ProxyInitializeEvent
+import com.velocitypowered.api.event.proxy.ProxyShutdownEvent
+import com.velocitypowered.api.plugin.Plugin
+import com.velocitypowered.api.proxy.ProxyServer
+import dev.m1sk9.lunaticChat.velocity.messaging.PluginMessageHandler
+import org.slf4j.Logger
+
+/**
+ * LunaticChat Velocity plugin
+ *
+ * Handles plugin messaging in coordination with Paper servers.
+ */
+@Plugin(
+ id = "lunaticchat",
+ name = "LunaticChat",
+ version = "0.8.0",
+ description = "Next-generation channel chat plugin for Paper/Velocity",
+ url = "https://lc.m1sk9.dev",
+ authors = ["m1sk9"],
+)
+class LunaticChat
+ @Inject
+ constructor(
+ private val server: ProxyServer,
+ private val logger: Logger,
+ ) {
+ private var messageHandler: PluginMessageHandler? = null
+
+ @Subscribe
+ fun onProxyInitialization(event: ProxyInitializeEvent) {
+ logger.info("Initializing LunaticChat Velocity plugin")
+
+ // Initialize plugin message handler
+ messageHandler =
+ PluginMessageHandler(
+ plugin = this@LunaticChat,
+ server = server,
+ logger = logger,
+ pluginVersion = "0.8.0",
+ )
+ messageHandler?.initialize()
+
+ logger.info("LunaticChat Velocity plugin initialized successfully")
+ }
+
+ @Subscribe
+ fun onProxyShutdown(event: ProxyShutdownEvent) {
+ logger.info("Shutting down LunaticChat Velocity plugin")
+ messageHandler?.shutdown()
+ }
+ }
diff --git a/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt
new file mode 100644
index 0000000..b235ba1
--- /dev/null
+++ b/platform-velocity/src/main/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandler.kt
@@ -0,0 +1,161 @@
+package dev.m1sk9.lunaticChat.velocity.messaging
+
+import com.velocitypowered.api.event.Subscribe
+import com.velocitypowered.api.event.connection.PluginMessageEvent
+import com.velocitypowered.api.proxy.ProxyServer
+import com.velocitypowered.api.proxy.ServerConnection
+import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier
+import dev.m1sk9.lunaticChat.engine.protocol.PluginMessage
+import dev.m1sk9.lunaticChat.engine.protocol.PluginMessageCodec
+import dev.m1sk9.lunaticChat.engine.protocol.ProtocolVersion
+import org.slf4j.Logger
+
+/**
+ * Handles plugin messages from Paper servers
+ */
+class PluginMessageHandler(
+ private val plugin: Any,
+ private val server: ProxyServer,
+ private val logger: Logger,
+ private val pluginVersion: String,
+) {
+ companion object {
+ private val CHANNEL = MinecraftChannelIdentifier.create("lunaticchat", "main")
+ }
+
+ /**
+ * Initialize
+ */
+ fun initialize() {
+ server.channelRegistrar.register(CHANNEL)
+ server.eventManager.register(plugin, this)
+ logger.info("Plugin message handler registered for channel: $CHANNEL")
+ }
+
+ /**
+ * Plugin message event
+ */
+ @Subscribe
+ fun onPluginMessage(event: PluginMessageEvent) {
+ if (event.identifier != CHANNEL) return
+
+ val source = event.source
+ if (source !is ServerConnection) {
+ logger.warn("Received plugin message from non-server source: ${source::class.simpleName}")
+ return
+ }
+
+ try {
+ val message = PluginMessageCodec.decode(event.data)
+
+ when (message) {
+ is PluginMessage.Handshake -> {
+ handleHandshake(source, message)
+ }
+ is PluginMessage.StatusRequest -> {
+ handleStatusRequest(source)
+ }
+ else -> {
+ logger.warn("Unexpected message type from Paper: ${message::class.simpleName}")
+ }
+ }
+ } catch (e: Exception) {
+ logger.error("Failed to decode plugin message: ${e.message}", e)
+ }
+ }
+
+ /**
+ * Handles handshake message
+ */
+ private fun handleHandshake(
+ connection: ServerConnection,
+ handshake: PluginMessage.Handshake,
+ ) {
+ logger.info(
+ "Received handshake from ${connection.serverInfo.name}: " +
+ "Plugin=${handshake.pluginVersion}, Protocol=${handshake.protocolMajor}.${handshake.protocolMinor}.${handshake.protocolPatch}",
+ )
+
+ // Plugin version check (exact match required)
+ val versionMatch = handshake.pluginVersion == pluginVersion
+ if (!versionMatch) {
+ val error = "Plugin version mismatch: Paper=${handshake.pluginVersion}, Velocity=$pluginVersion"
+ logger.error(error)
+ sendHandshakeResponse(connection, false, error)
+ return
+ }
+
+ // Protocol version check (MAJOR.MINOR must match)
+ val protocolCompatible =
+ ProtocolVersion.isCompatible(
+ handshake.protocolMajor,
+ handshake.protocolMinor,
+ )
+ if (!protocolCompatible) {
+ val error =
+ "Protocol version incompatible: Paper=${handshake.protocolMajor}.${handshake.protocolMinor}.${handshake.protocolPatch}, " +
+ "Velocity=${ProtocolVersion.version}"
+ logger.error(error)
+ sendHandshakeResponse(connection, false, error)
+ return
+ }
+
+ // Success
+ logger.info("Handshake successful with ${connection.serverInfo.name}")
+ sendHandshakeResponse(connection, true, null)
+ }
+
+ /**
+ * Sends handshake response
+ */
+ private fun sendHandshakeResponse(
+ connection: ServerConnection,
+ compatible: Boolean,
+ error: String?,
+ ) {
+ val response =
+ PluginMessage.HandshakeResponse(
+ compatible = compatible,
+ velocityVersion = pluginVersion,
+ error = error,
+ )
+
+ val data = PluginMessageCodec.encode(response)
+
+ // Send
+ connection.sendPluginMessage(CHANNEL, data)
+
+ if (compatible) {
+ logger.info("Sent successful handshake response to ${connection.serverInfo.name}")
+ } else {
+ logger.warn("Sent failed handshake response to ${connection.serverInfo.name}: $error")
+ }
+ }
+
+ /**
+ * Handles status request
+ */
+ private fun handleStatusRequest(connection: ServerConnection) {
+ logger.info("Received status request from ${connection.serverInfo.name}")
+
+ val response =
+ PluginMessage.StatusResponse(
+ velocityVersion = pluginVersion,
+ protocolVersion = ProtocolVersion.version,
+ online = true,
+ )
+
+ val data = PluginMessageCodec.encode(response)
+ connection.sendPluginMessage(CHANNEL, data)
+
+ logger.info("Sent status response to ${connection.serverInfo.name}")
+ }
+
+ /**
+ * Shutdown
+ */
+ fun shutdown() {
+ server.eventManager.unregisterListener(plugin, this)
+ logger.info("Plugin message handler unregistered")
+ }
+}
diff --git a/platform-velocity/src/main/resources/velocity-plugin.json b/platform-velocity/src/main/resources/velocity-plugin.json
new file mode 100644
index 0000000..27186fd
--- /dev/null
+++ b/platform-velocity/src/main/resources/velocity-plugin.json
@@ -0,0 +1,11 @@
+{
+ "id": "lunaticchat",
+ "name": "LunaticChat",
+ "version": "${version}",
+ "description": "Next-generation channel chat plugin for Paper/Velocity",
+ "url": "https://lc.m1sk9.dev",
+ "authors": [
+ "m1sk9"
+ ],
+ "main": "dev.m1sk9.lunaticChat.velocity.LunaticChat"
+}