Player count, config reloading, command

This commit is contained in:
creeper123123321 2021-03-28 19:27:32 -03:00
parent be050c3215
commit a0eef99492
14 changed files with 228 additions and 100 deletions

View File

@ -1,9 +1,10 @@
package com.github.creeper123123321.viaaas package com.github.creeper123123321.viaaas
import com.github.creeper123123321.viaaas.command.AspirinCommands
import com.github.creeper123123321.viaaas.command.VIAaaSConsole import com.github.creeper123123321.viaaas.command.VIAaaSConsole
import com.github.creeper123123321.viaaas.command.ViaAspirinCommand
import com.github.creeper123123321.viaaas.config.VIAaaSConfig import com.github.creeper123123321.viaaas.config.VIAaaSConfig
import com.github.creeper123123321.viaaas.handler.FrontEndInit import com.github.creeper123123321.viaaas.handler.FrontEndInit
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.platform.* import com.github.creeper123123321.viaaas.platform.*
import com.github.creeper123123321.viaaas.web.ViaWebApp import com.github.creeper123123321.viaaas.web.ViaWebApp
import com.github.creeper123123321.viaaas.web.WebDashboardServer import com.github.creeper123123321.viaaas.web.WebDashboardServer
@ -18,6 +19,7 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import io.netty.bootstrap.ServerBootstrap import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelFactory import io.netty.channel.ChannelFactory
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelOption import io.netty.channel.ChannelOption
import io.netty.channel.EventLoopGroup import io.netty.channel.EventLoopGroup
import io.netty.channel.epoll.Epoll import io.netty.channel.epoll.Epoll
@ -49,7 +51,8 @@ val viaaasVer = JsonParser.parseString(
AspirinPlatform::class.java.classLoader.getResourceAsStream("viaaas_info.json")!!.reader(Charsets.UTF_8).readText() AspirinPlatform::class.java.classLoader.getResourceAsStream("viaaas_info.json")!!.reader(Charsets.UTF_8).readText()
).asJsonObject.get("version").asString ).asJsonObject.get("version").asString
val viaWebServer = WebDashboardServer() val viaWebServer = WebDashboardServer()
var runningServer = true var serverFinishing = CompletableFuture<Unit>()
var finishedFuture = CompletableFuture<Unit>()
val httpClient = HttpClient { val httpClient = HttpClient {
install(UserAgent) { install(UserAgent) {
agent = "VIAaaS/$viaaasVer" agent = "VIAaaS/$viaaasVer"
@ -74,34 +77,64 @@ fun eventLoopGroup(): EventLoopGroup {
return NioEventLoopGroup() return NioEventLoopGroup()
} }
fun channelServerSocketFactory(): ChannelFactory<ServerSocketChannel> { fun channelServerSocketFactory(eventLoop: EventLoopGroup): ChannelFactory<ServerSocketChannel> {
if (VIAaaSConfig.isNativeTransportMc) { return when (eventLoop) {
if (Epoll.isAvailable()) return ChannelFactory { EpollServerSocketChannel() } is EpollEventLoopGroup -> ChannelFactory { EpollServerSocketChannel() }
if (KQueue.isAvailable()) return ChannelFactory { KQueueServerSocketChannel() } is KQueueEventLoopGroup -> ChannelFactory { KQueueServerSocketChannel() }
else -> ChannelFactory { NioServerSocketChannel() }
} }
return ChannelFactory { NioServerSocketChannel() }
} }
fun channelSocketFactory(): ChannelFactory<SocketChannel> { fun channelSocketFactory(eventLoop: EventLoopGroup): ChannelFactory<SocketChannel> {
if (VIAaaSConfig.isNativeTransportMc) { return when (eventLoop) {
if (Epoll.isAvailable()) return ChannelFactory { EpollSocketChannel() } is EpollEventLoopGroup -> ChannelFactory { EpollSocketChannel() }
if (KQueue.isAvailable()) return ChannelFactory { KQueueSocketChannel() } is KQueueEventLoopGroup -> ChannelFactory { KQueueSocketChannel() }
else -> ChannelFactory { NioSocketChannel() }
} }
return ChannelFactory { NioSocketChannel() }
} }
val parentLoop = eventLoopGroup() val parentLoop = eventLoopGroup()
val childLoop = eventLoopGroup() val childLoop = eventLoopGroup()
var chFuture: ChannelFuture? = null
var ktorServer: NettyApplicationEngine? = null
fun main(args: Array<String>) { fun main(args: Array<String>) {
try {
setupSystem()
printSplash()
generateCert()
initVia()
bindPorts(args)
initFuture.complete(Unit)
VIAaaSConsole.start()
addShutdownHook()
serverFinishing.join()
} catch (e: Exception) {
e.printStackTrace()
} finally {
stopServer()
}
}
private fun addShutdownHook() {
Runtime.getRuntime().addShutdownHook(Thread {
serverFinishing.complete(Unit)
finishedFuture.join()
})
}
private fun setupSystem() {
// Stolen from https://github.com/VelocityPowered/Velocity/blob/dev/1.1.0/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java // Stolen from https://github.com/VelocityPowered/Velocity/blob/dev/1.1.0/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java
if (System.getProperty("io.netty.allocator.maxOrder") == null) { if (System.getProperty("io.netty.allocator.maxOrder") == null) {
System.setProperty("io.netty.allocator.maxOrder", "9") System.setProperty("io.netty.allocator.maxOrder", "9")
} }
// Also stolen from Velocity // Also stolen from Velocity
System.setOut(IoBuilder.forLogger("STDOUT").setLevel(Level.INFO).buildPrintStream()); System.setOut(IoBuilder.forLogger("STDOUT").setLevel(Level.INFO).buildPrintStream())
System.setErr(IoBuilder.forLogger("STDERR").setLevel(Level.ERROR).buildPrintStream()); System.setErr(IoBuilder.forLogger("STDERR").setLevel(Level.ERROR).buildPrintStream())
}
private fun printSplash() {
println( println(
"""\\ // // //\\ => //|| //|| /=====/ PROXY """\\ // // //\\ => //|| //|| /=====/ PROXY
| \\ // // // \\ // || // || // | \\ // // // \\ // || // || //
@ -109,17 +142,21 @@ fun main(args: Array<String>) {
| \\ // // // \\ // || // || // | \\ // // // \\ // || // || //
|<= \\// // // \\ // || // || /====/""".trimMargin() |<= \\// // // \\ // || // || /====/""".trimMargin()
) )
}
private fun generateCert() {
File("config/https.jks").apply { File("config/https.jks").apply {
parentFile.mkdirs() parentFile.mkdirs()
if (!exists()) generateCertificate(this) if (!exists()) generateCertificate(this)
} }
}
private fun initVia() {
Via.init( Via.init(
ViaManagerImpl.builder() ViaManagerImpl.builder()
.injector(AspirinInjector) .injector(AspirinInjector)
.loader(AspirinLoader) .loader(AspirinLoader)
.commandHandler(AspirinCommands) .commandHandler(ViaAspirinCommand)
.platform(AspirinPlatform).build() .platform(AspirinPlatform).build()
) )
MappingDataLoader.enableMappingsCache() MappingDataLoader.enableMappingsCache()
@ -127,37 +164,39 @@ fun main(args: Array<String>) {
ProtocolVersion.register(-2, "AUTO") ProtocolVersion.register(-2, "AUTO")
AspirinRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml"))) AspirinRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml")))
AspirinBackwards.init(File("config/viabackwards")) AspirinBackwards.init(File("config/viabackwards"))
}
val future = ServerBootstrap() private fun bindPorts(args: Array<String>) {
chFuture = ServerBootstrap()
.group(parentLoop, childLoop) .group(parentLoop, childLoop)
.channelFactory(channelServerSocketFactory()) .channelFactory(channelServerSocketFactory(parentLoop))
.childHandler(FrontEndInit) .childHandler(FrontEndInit)
.childOption(ChannelOption.IP_TOS, 0x18) .childOption(ChannelOption.IP_TOS, 0x18)
.childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.TCP_NODELAY, true)
.bind(InetAddress.getByName(VIAaaSConfig.bindAddress), VIAaaSConfig.port) .bind(InetAddress.getByName(VIAaaSConfig.bindAddress), VIAaaSConfig.port)
var ktorServer: NettyApplicationEngine? = null viaaasLogger.info("Binded minecraft into " + chFuture!!.sync().channel().localAddress())
ktorServer = embeddedServer(Netty, commandLineEnvironment(args)) {}.start(false)
}
private fun stopServer() {
try { try {
viaaasLogger.info("Binded minecraft into " + future.sync().channel().localAddress()) Via.getManager().connectionManager.connections.forEach {
ktorServer = embeddedServer(Netty, commandLineEnvironment(args)) {}.start(false) it.channel?.pipeline()?.get(MinecraftHandler::class.java)?.disconnect("Stopping")
} catch (e: Exception) { }
runningServer = false ktorServer?.stop(1000, 1000)
e.printStackTrace() httpClient.close()
listOf<Future<*>?>(
chFuture?.channel()?.close(),
parentLoop.shutdownGracefully(),
childLoop.shutdownGracefully()
)
.forEach { it?.sync() }
(Via.getManager() as ViaManagerImpl).destroy()
} finally {
finishedFuture.complete(Unit)
} }
initFuture.complete(Unit)
VIAaaSConsole.start()
while (runningServer) {
Thread.sleep(1000L)
}
ktorServer?.stop(1000, 1000)
httpClient.close()
listOf<Future<*>>(future.channel().close(), parentLoop.shutdownGracefully(), childLoop.shutdownGracefully())
.forEach { it.sync() }
(Via.getManager() as ViaManagerImpl).destroy()
} }
fun Application.mainWeb() { fun Application.mainWeb() {

View File

@ -1,14 +0,0 @@
package com.github.creeper123123321.viaaas.command
import com.github.creeper123123321.viaaas.command.sub.ConnectionsSubCommand
import com.github.creeper123123321.viaaas.command.sub.EndSubCommand
import com.github.creeper123123321.viaaas.command.sub.VIAaaSSubCommand
import us.myles.ViaVersion.commands.ViaCommandHandler
object AspirinCommands : ViaCommandHandler() {
init {
registerSubCommand(EndSubCommand)
registerSubCommand(VIAaaSSubCommand)
registerSubCommand(ConnectionsSubCommand)
}
}

View File

@ -0,0 +1,9 @@
package com.github.creeper123123321.viaaas.command
import us.myles.ViaVersion.api.command.ViaCommandSender
interface Command {
val info: String
fun suggest(sender: ViaCommandSender, alias: String, args: List<String>): List<String>
fun execute(sender: ViaCommandSender, alias: String, args: List<String>)
}

View File

@ -0,0 +1,14 @@
package com.github.creeper123123321.viaaas.command
import us.myles.ViaVersion.api.command.ViaCommandSender
object EndCommand : Command {
override val info = "Stops VIAaaS"
override fun suggest(sender: ViaCommandSender, alias: String, args: List<String>): List<String> {
return emptyList()
}
override fun execute(sender: ViaCommandSender, alias: String, args: List<String>) {
ViaAspirinCommand.execute(sender, alias, listOf("stop"))
}
}

View File

@ -0,0 +1,21 @@
package com.github.creeper123123321.viaaas.command
import us.myles.ViaVersion.api.command.ViaCommandSender
object HelpCommand : Command {
override val info = "Lists the available commands"
override fun suggest(sender: ViaCommandSender, alias: String, args: List<String>): List<String> {
return emptyList()
}
override fun execute(sender: ViaCommandSender, alias: String, args: List<String>) {
sender.sendMessage(
VIAaaSConsole.commands.entries
.groupBy { it.value }.entries
.joinToString("\n") {
"${it.value.joinToString(", ") { it.key }}: ${it.key.info}"
}
)
}
}

View File

@ -0,0 +1,15 @@
package com.github.creeper123123321.viaaas.command
import us.myles.ViaVersion.api.command.ViaCommandSender
object ReloadCommand : Command {
override val info = "Alias for 'viaversion aasreload'"
override fun suggest(sender: ViaCommandSender, alias: String, args: List<String>): List<String> {
return emptyList()
}
override fun execute(sender: ViaCommandSender, alias: String, args: List<String>) {
ViaAspirinCommand.execute(sender, alias, listOf("aasreload"))
}
}

View File

@ -1,39 +1,30 @@
package com.github.creeper123123321.viaaas.command package com.github.creeper123123321.viaaas.command
import com.github.creeper123123321.viaaas.runningServer import com.github.creeper123123321.viaaas.command.sub.StopSubCommand
import com.github.creeper123123321.viaaas.viaaasLogger import com.github.creeper123123321.viaaas.serverFinishing
import net.minecrell.terminalconsole.SimpleTerminalConsole import net.minecrell.terminalconsole.SimpleTerminalConsole
import org.jline.reader.Candidate import org.jline.reader.Candidate
import org.jline.reader.LineReader import org.jline.reader.LineReader
import org.jline.reader.LineReaderBuilder import org.jline.reader.LineReaderBuilder
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import us.myles.ViaVersion.api.Via
import us.myles.ViaVersion.api.command.ViaCommandSender import us.myles.ViaVersion.api.command.ViaCommandSender
import java.util.* import java.util.*
object VIAaaSConsole : SimpleTerminalConsole(), ViaCommandSender { object VIAaaSConsole : SimpleTerminalConsole(), ViaCommandSender {
val commands = hashMapOf<String, (MutableList<String>?, String, Array<String>) -> Unit>() val commands = hashMapOf<String, Command>()
override fun isRunning(): Boolean = runningServer override fun isRunning(): Boolean = !serverFinishing.isDone
init { init {
commands["viaversion"] = { suggestion, _, args -> commands["viaversion"] = ViaAspirinCommand
if (suggestion == null) { commands["viaver"] = ViaAspirinCommand
Via.getManager().commandHandler.onCommand(this, args) commands["vvcloud"] = ViaAspirinCommand
} else { commands["vvaas"] = ViaAspirinCommand
suggestion.addAll(Via.getManager().commandHandler.onTabComplete(this, args)) commands["vvaspirin"] = ViaAspirinCommand
} commands["viaaas"] = ViaAspirinCommand
} commands["help"] = HelpCommand
commands["viaver"] = commands["viaversion"]!! commands["?"] = HelpCommand
commands["vvcloud"] = commands["viaversion"]!! commands["end"] = EndCommand
commands["vvaas"] = commands["viaversion"]!! commands["reload"] = ReloadCommand
commands["vvaspirin"] = commands["viaversion"]!!
commands["viaaas"] = commands["viaversion"]!!
commands["help"] = { suggestion, _, _ ->
if (suggestion == null) sendMessage(commands.entries.groupBy { it.value }.entries.joinToString(", ") {
it.value.joinToString("/") { it.key }
})
}
commands["?"] = commands["help"]!!
} }
override fun buildReader(builder: LineReaderBuilder): LineReader { override fun buildReader(builder: LineReaderBuilder): LineReader {
@ -47,11 +38,9 @@ object VIAaaSConsole : SimpleTerminalConsole(), ViaCommandSender {
candidates.addAll(commands.keys.filter { it.startsWith(alias, ignoreCase = true) } candidates.addAll(commands.keys.filter { it.startsWith(alias, ignoreCase = true) }
.map { Candidate(it) }) .map { Candidate(it) })
} else { } else {
val cmd = commands[alias.toLowerCase()] val command = commands[alias.toLowerCase()]
if (cmd != null) { if (command != null) {
val suggestions = mutableListOf<String>() candidates.addAll(command.suggest(this, alias, args).map(::Candidate))
cmd(suggestions, alias, args.toTypedArray())
candidates.addAll(suggestions.map(::Candidate))
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -60,16 +49,16 @@ object VIAaaSConsole : SimpleTerminalConsole(), ViaCommandSender {
}) })
} }
override fun runCommand(command: String) { override fun runCommand(commandLine: String) {
val cmd = command.split(" ") val cmd = commandLine.split(" ")
try { try {
val alias = cmd[0].toLowerCase() val alias = cmd[0].toLowerCase()
val args = cmd.subList(1, cmd.size).toTypedArray() val args = cmd.subList(1, cmd.size)
val runnable = commands[alias] val command = commands[alias]
if (runnable == null) { if (command == null) {
sendMessage("unknown command, try 'help'") sendMessage("Unknown command, try 'help'")
} else { } else {
runnable(null, alias, args) command.execute(this, alias, args)
} }
} catch (e: Exception) { } catch (e: Exception) {
sendMessage("Error running command: $e") sendMessage("Error running command: $e")
@ -77,11 +66,9 @@ object VIAaaSConsole : SimpleTerminalConsole(), ViaCommandSender {
} }
public override fun shutdown() { public override fun shutdown() {
viaaasLogger.info("Shutting down...") StopSubCommand.execute(this, emptyArray())
runningServer = false
} }
override fun sendMessage(p0: String) { override fun sendMessage(p0: String) {
LoggerFactory.getLogger(this.name).info(p0) LoggerFactory.getLogger(this.name).info(p0)
} }

View File

@ -0,0 +1,27 @@
package com.github.creeper123123321.viaaas.command
import com.github.creeper123123321.viaaas.command.sub.AspirinReloadSubCommand
import com.github.creeper123123321.viaaas.command.sub.ConnectionsSubCommand
import com.github.creeper123123321.viaaas.command.sub.StopSubCommand
import com.github.creeper123123321.viaaas.command.sub.VIAaaSSubCommand
import us.myles.ViaVersion.api.command.ViaCommandSender
import us.myles.ViaVersion.commands.ViaCommandHandler
object ViaAspirinCommand : ViaCommandHandler(), Command {
override val info = "ViaVersion commands"
init {
registerSubCommand(StopSubCommand)
registerSubCommand(VIAaaSSubCommand)
registerSubCommand(ConnectionsSubCommand)
registerSubCommand(AspirinReloadSubCommand)
}
override fun suggest(sender: ViaCommandSender, alias: String, args: List<String>): List<String> {
return onTabComplete(sender, args.toTypedArray())
}
override fun execute(sender: ViaCommandSender, alias: String, args: List<String>) {
onCommand(sender, args.toTypedArray())
}
}

View File

@ -0,0 +1,16 @@
package com.github.creeper123123321.viaaas.command.sub
import com.github.creeper123123321.viaaas.config.VIAaaSConfig
import us.myles.ViaVersion.api.command.ViaCommandSender
import us.myles.ViaVersion.api.command.ViaSubCommand
object AspirinReloadSubCommand: ViaSubCommand() {
override fun name() = "aasreload"
override fun description() = "Reloads VIAaaS config"
override fun execute(sender: ViaCommandSender, args: Array<String>): Boolean {
VIAaaSConfig.reloadConfig()
sender.sendMessage("Reloaded VIAaaS config. Some configurations may need a restart.")
return true
}
}

View File

@ -1,14 +1,15 @@
package com.github.creeper123123321.viaaas.command.sub package com.github.creeper123123321.viaaas.command.sub
import com.github.creeper123123321.viaaas.command.VIAaaSConsole import com.github.creeper123123321.viaaas.serverFinishing
import us.myles.ViaVersion.api.command.ViaCommandSender import us.myles.ViaVersion.api.command.ViaCommandSender
import us.myles.ViaVersion.api.command.ViaSubCommand import us.myles.ViaVersion.api.command.ViaSubCommand
object EndSubCommand : ViaSubCommand() { object StopSubCommand : ViaSubCommand() {
override fun name() = "stop" override fun name() = "stop"
override fun description(): String = "Stops VIAaaS" override fun description(): String = "Stops VIAaaS"
override fun execute(p0: ViaCommandSender?, p1: Array<out String>?): Boolean { override fun execute(sender: ViaCommandSender, p1: Array<String>): Boolean {
VIAaaSConsole.shutdown() sender.sendMessage("Shutting down...")
serverFinishing.complete(Unit)
return true return true
} }
} }

View File

@ -35,7 +35,7 @@ object ProtocolDetector {
try { try {
val ch: ChannelFuture = Bootstrap() val ch: ChannelFuture = Bootstrap()
.group(childLoop) .group(childLoop)
.channelFactory(channelSocketFactory()) .channelFactory(channelSocketFactory(childLoop))
.option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.IP_TOS, 0x18) .option(ChannelOption.IP_TOS, 0x18)
.handler(object : ChannelInitializer<Channel>() { .handler(object : ChannelInitializer<Channel>() {

View File

@ -8,11 +8,11 @@ import com.github.creeper123123321.viaaas.packet.UnknownPacket
import com.github.creeper123123321.viaaas.packet.status.StatusResponse import com.github.creeper123123321.viaaas.packet.status.StatusResponse
import com.github.creeper123123321.viaaas.parseProtocol import com.github.creeper123123321.viaaas.parseProtocol
import com.github.creeper123123321.viaaas.writeFlushClose import com.github.creeper123123321.viaaas.writeFlushClose
import com.google.gson.Gson
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelHandlerContext
import us.myles.ViaVersion.api.Via
import us.myles.ViaVersion.packets.State import us.myles.ViaVersion.packets.State
import java.util.* import java.util.*
@ -51,8 +51,20 @@ object StatusState : MinecraftConnectionState {
super.disconnect(handler, msg) super.disconnect(handler, msg)
val packet = StatusResponse() val packet = StatusResponse()
packet.json = """{"version": {"name": "VIAaaS", "protocol": -1}, "players": {"max": 0, "online": 0, packet.json = JsonObject().also {
| "sample": []}, "description": {"text": ${Gson().toJson("§c$msg")}}}""".trimMargin() it.add("version", JsonObject().also {
it.addProperty("name", "VIAaaS")
it.addProperty("protocol", -1)
})
it.add("players", JsonObject().also {
it.addProperty("max", 0)
it.addProperty("online", Via.getManager().connectionManager.connections.size)
it.add("sample", JsonArray())
})
it.add("description", JsonObject().also {
it.addProperty("text", "§c$msg")
})
}.toString()
writeFlushClose(handler.data.frontChannel, packet) writeFlushClose(handler.data.frontChannel, packet)
} }
} }

View File

@ -20,10 +20,11 @@ import java.net.InetAddress
import java.net.InetSocketAddress import java.net.InetSocketAddress
private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketAddress, state: State): ChannelFuture { private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketAddress, state: State): ChannelFuture {
val loop = handler.data.frontChannel.eventLoop()
return Bootstrap() return Bootstrap()
.handler(BackEndInit(handler.data)) .handler(BackEndInit(handler.data))
.channelFactory(channelSocketFactory()) .channelFactory(channelSocketFactory(loop.parent()))
.group(handler.data.frontChannel.eventLoop()) .group(loop)
.option(ChannelOption.IP_TOS, 0x18) .option(ChannelOption.IP_TOS, 0x18)
.option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) // We need to show the error before the client timeout .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) // We need to show the error before the client timeout

View File

@ -1,5 +1,5 @@
####################### #######################
# CHANGING THIS CONFIG FILE AT RUNTIME ISN'T SUPPORTED # Some configurations need a restart
# See https://ktor.io/docs/configurations.html#command-line for https interface options # See https://ktor.io/docs/configurations.html#command-line for https interface options
####################### #######################
# #