Files
Minikura/plugins/MinikuraVelocity/src/main/kotlin/cafe/kirameki/minikuraVelocity/Main.kt

210 lines
8.0 KiB
Kotlin
Raw Normal View History

2025-01-02 12:16:41 +07:00
package cafe.kirameki.minikuraVelocity
2025-01-02 22:16:40 +07:00
import cafe.kirameki.minikuraVelocity.listeners.ProxyTransferHandler
2025-01-02 12:16:41 +07:00
import cafe.kirameki.minikuraVelocity.models.ReverseProxyServerData
import cafe.kirameki.minikuraVelocity.models.ServerData
import cafe.kirameki.minikuraVelocity.store.ServerDataStore
2025-01-02 22:16:40 +07:00
import cafe.kirameki.minikuraVelocity.utils.ProxyTransferUtils
2025-01-02 12:16:41 +07:00
import cafe.kirameki.minikuraVelocity.utils.createWebSocketClient
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.google.inject.Inject
import com.velocitypowered.api.command.CommandManager
import com.velocitypowered.api.command.CommandMeta
import com.velocitypowered.api.command.SimpleCommand
import com.velocitypowered.api.event.Subscribe
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent
2025-01-02 22:16:40 +07:00
import com.velocitypowered.api.plugin.Dependency
2025-01-02 12:16:41 +07:00
import com.velocitypowered.api.plugin.Plugin
import com.velocitypowered.api.proxy.ProxyServer
import com.velocitypowered.api.proxy.server.RegisteredServer
import com.velocitypowered.api.proxy.server.ServerInfo
import net.kyori.adventure.text.Component
import okhttp3.OkHttpClient
import okhttp3.Request
import org.slf4j.Logger
import java.net.InetSocketAddress
import java.util.concurrent.Executors
2025-01-02 22:16:40 +07:00
import java.util.concurrent.atomic.AtomicBoolean
2025-01-02 12:16:41 +07:00
2025-01-02 22:16:40 +07:00
@Plugin(
id = "minikura-velocity",
name = "MinikuraVelocity",
version = "1.0",
dependencies = [
Dependency(id = "redisbungee")
]
)
2025-01-02 12:16:41 +07:00
class Main @Inject constructor(private val logger: Logger, private val server: ProxyServer) {
private val servers: MutableMap<String, RegisteredServer> = HashMap()
private val client = OkHttpClient()
private val apiKey: String = System.getenv("MINIKURA_API_KEY") ?: ""
private val apiUrl: String = System.getenv("MINIKURA_API_URL") ?: "http://localhost:3000"
private val websocketUrl: String = System.getenv("MINIKURA_WEBSOCKET_URL") ?: "ws://localhost:3000/ws"
2025-01-02 22:16:40 +07:00
private var acceptingTransfers = AtomicBoolean(false)
2025-01-02 12:16:41 +07:00
@Subscribe
fun onProxyInitialization(event: ProxyInitializeEvent?) {
logger.info("Minikura-Velocity is initializing...")
2025-01-02 22:16:40 +07:00
ProxyTransferUtils.server = server
ProxyTransferUtils.plugin = this
ProxyTransferUtils.logger = logger
ProxyTransferUtils.acceptingTransfers = acceptingTransfers
val client = createWebSocketClient(this, logger, server, websocketUrl)
2025-01-02 12:16:41 +07:00
client.connect()
val commandManager: CommandManager = server.commandManager
2025-01-02 22:16:40 +07:00
val refreshCommandMeta: CommandMeta = commandManager.metaBuilder("refresh")
2025-01-02 12:16:41 +07:00
.plugin(this)
.build()
2025-01-02 22:16:40 +07:00
val refreshCommand = SimpleCommand { p ->
2025-01-02 12:16:41 +07:00
val source = p.source()
2025-01-02 22:16:40 +07:00
source.sendMessage(Component.text("Refreshing server list..."))
Executors.newSingleThreadExecutor().submit {
fetchServers()
fetchReverseProxyServers()
source.sendMessage(Component.text("Server list refreshed successfully!"))
2025-01-02 12:16:41 +07:00
}
}
2025-01-02 22:16:40 +07:00
val serversCommandMeta: CommandMeta = commandManager.metaBuilder("server")
2025-01-02 12:16:41 +07:00
.plugin(this)
.build()
2025-01-02 22:16:40 +07:00
val endCommandMeta: CommandMeta = commandManager.metaBuilder("end")
.plugin(this)
.build()
2025-01-02 12:16:41 +07:00
val migrateCommandMeta: CommandMeta = commandManager.metaBuilder("migrate")
.plugin(this)
.build()
val migrateCommand = SimpleCommand { p ->
val source = p.source()
val args = p.arguments()
if (args.isEmpty()) {
source.sendMessage(Component.text("Please specify a server to migrate players to."))
return@SimpleCommand
}
val targetServerName = args[0]
val targetServer = ServerDataStore.getReverseProxyServer(targetServerName)
if (targetServer == null) {
source.sendMessage(Component.text("Server '$targetServerName' not found."))
return@SimpleCommand
}
2025-01-02 22:16:40 +07:00
ProxyTransferUtils.migratePlayersToServer(targetServer)
2025-01-02 12:16:41 +07:00
source.sendMessage(Component.text("Migrating players to server '$targetServerName'..."))
}
2025-01-02 22:16:40 +07:00
commandManager.register(serversCommandMeta, ServerCommand.createServerCommand(server))
commandManager.register(endCommandMeta, EndCommand.createEndCommand(server))
commandManager.register(refreshCommandMeta, refreshCommand)
2025-01-02 12:16:41 +07:00
commandManager.register(migrateCommandMeta, migrateCommand)
2025-01-02 22:16:40 +07:00
val connectionHandler = ProxyTransferHandler(servers, logger)
2025-01-02 12:16:41 +07:00
server.eventManager.register(this, connectionHandler)
2025-01-02 22:16:40 +07:00
Executors.newSingleThreadExecutor().submit {
fetchServers()
fetchReverseProxyServers()
acceptingTransfers.set(true)
logger.info("Ready to accept player new connections/proxy transfers.")
}
2025-01-02 12:16:41 +07:00
logger.info("Minikura-Velocity has been initialized.")
}
private fun fetchReverseProxyServers() {
val request = Request.Builder()
.url("$apiUrl/reverse_proxy_servers")
.header("Authorization", "Bearer $apiKey")
.build()
2025-01-02 22:16:40 +07:00
try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val fetchedServers = parseReverseProxyServersData(responseBody)
server.scheduler.buildTask(this, Runnable {
synchronized(ServerDataStore) {
ServerDataStore.clearReverseProxyServers()
ServerDataStore.addAllReverseProxyServers(fetchedServers)
}
}).schedule()
} else {
logger.error("Failed to fetch reverse proxy servers: ${response.message}")
2025-01-02 12:16:41 +07:00
}
2025-01-02 22:16:40 +07:00
} catch (e: Exception) {
logger.error("Error fetching reverse proxy servers: ${e.message}", e)
2025-01-02 12:16:41 +07:00
}
}
private fun fetchServers() {
server.allServers.forEach { server.unregisterServer(it.serverInfo) }
val request = Request.Builder()
.url("$apiUrl/servers")
.header("Authorization", "Bearer $apiKey")
.build()
2025-01-02 22:16:40 +07:00
try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val responseBody = response.body?.string()
val fetchedServers = parseServersData(responseBody)
server.scheduler.buildTask(this, Runnable {
synchronized(ServerDataStore) {
ServerDataStore.clearServers()
ServerDataStore.addAllServers(fetchedServers)
}
populateServers(fetchedServers)
}).schedule()
} else {
logger.error("Failed to fetch servers: ${response.message}")
2025-01-02 12:16:41 +07:00
}
2025-01-02 22:16:40 +07:00
} catch (e: Exception) {
logger.error("Error fetching servers: ${e.message}", e)
2025-01-02 12:16:41 +07:00
}
}
private fun parseReverseProxyServersData(responseBody: String?): List<ReverseProxyServerData> {
if (responseBody.isNullOrEmpty()) return emptyList()
val gson = Gson()
val reverseProxyServerListType = object : TypeToken<List<ReverseProxyServerData>>() {}.type
return gson.fromJson(responseBody, reverseProxyServerListType)
}
private fun parseServersData(responseBody: String?): List<ServerData> {
if (responseBody.isNullOrEmpty()) return emptyList()
val gson = Gson()
val serverListType = object : TypeToken<List<ServerData>>() {}.type
return gson.fromJson(responseBody, serverListType)
}
private fun populateServers(serversData: List<ServerData>) {
servers.clear()
for (data in serversData) {
val serverInfo = ServerInfo(data.name, InetSocketAddress(data.address, data.port))
val registeredServer = server.createRawRegisteredServer(serverInfo)
servers[data.name] = registeredServer
this.server.registerServer(registeredServer.serverInfo)
}
}
}