diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-02-01 01:10:47 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-02-01 01:10:47 +0900 |
| commit | 8427756383f8b382d9ed615fe758a12a8b2eeb79 (patch) | |
| tree | d08b5d06068a80800ab6c656468e1c712ba97634 | |
| parent | 77c4f168c0ed9ea77dc667e67bc5db21aa4097fe (diff) | |
| download | LunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.tar.gz LunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.tar.bz2 LunaticChat-8427756383f8b382d9ed615fe758a12a8b2eeb79.zip | |
feat: Velocity intergration
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" +} |
