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
import com.github.creeper123123321.viaaas.command.AspirinCommands
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.handler.FrontEndInit
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.platform.*
import com.github.creeper123123321.viaaas.web.ViaWebApp
import com.github.creeper123123321.viaaas.web.WebDashboardServer
@ -18,6 +19,7 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelFactory
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelOption
import io.netty.channel.EventLoopGroup
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()
).asJsonObject.get("version").asString
val viaWebServer = WebDashboardServer()
var runningServer = true
var serverFinishing = CompletableFuture<Unit>()
var finishedFuture = CompletableFuture<Unit>()
val httpClient = HttpClient {
install(UserAgent) {
agent = "VIAaaS/$viaaasVer"
@ -74,34 +77,64 @@ fun eventLoopGroup(): EventLoopGroup {
return NioEventLoopGroup()
}
fun channelServerSocketFactory(): ChannelFactory<ServerSocketChannel> {
if (VIAaaSConfig.isNativeTransportMc) {
if (Epoll.isAvailable()) return ChannelFactory { EpollServerSocketChannel() }
if (KQueue.isAvailable()) return ChannelFactory { KQueueServerSocketChannel() }
fun channelServerSocketFactory(eventLoop: EventLoopGroup): ChannelFactory<ServerSocketChannel> {
return when (eventLoop) {
is EpollEventLoopGroup -> ChannelFactory { EpollServerSocketChannel() }
is KQueueEventLoopGroup -> ChannelFactory { KQueueServerSocketChannel() }
else -> ChannelFactory { NioServerSocketChannel() }
}
return ChannelFactory { NioServerSocketChannel() }
}
fun channelSocketFactory(): ChannelFactory<SocketChannel> {
if (VIAaaSConfig.isNativeTransportMc) {
if (Epoll.isAvailable()) return ChannelFactory { EpollSocketChannel() }
if (KQueue.isAvailable()) return ChannelFactory { KQueueSocketChannel() }
fun channelSocketFactory(eventLoop: EventLoopGroup): ChannelFactory<SocketChannel> {
return when (eventLoop) {
is EpollEventLoopGroup -> ChannelFactory { EpollSocketChannel() }
is KQueueEventLoopGroup -> ChannelFactory { KQueueSocketChannel() }
else -> ChannelFactory { NioSocketChannel() }
}
return ChannelFactory { NioSocketChannel() }
}
val parentLoop = eventLoopGroup()
val childLoop = eventLoopGroup()
var chFuture: ChannelFuture? = null
var ktorServer: NettyApplicationEngine? = null
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
if (System.getProperty("io.netty.allocator.maxOrder") == null) {
System.setProperty("io.netty.allocator.maxOrder", "9")
}
// Also stolen from Velocity
System.setOut(IoBuilder.forLogger("STDOUT").setLevel(Level.INFO).buildPrintStream());
System.setErr(IoBuilder.forLogger("STDERR").setLevel(Level.ERROR).buildPrintStream());
System.setOut(IoBuilder.forLogger("STDOUT").setLevel(Level.INFO).buildPrintStream())
System.setErr(IoBuilder.forLogger("STDERR").setLevel(Level.ERROR).buildPrintStream())
}
private fun printSplash() {
println(
"""\\ // // //\\ => //|| //|| /=====/ PROXY
| \\ // // // \\ // || // || //
@ -109,17 +142,21 @@ fun main(args: Array<String>) {
| \\ // // // \\ // || // || //
|<= \\// // // \\ // || // || /====/""".trimMargin()
)
}
private fun generateCert() {
File("config/https.jks").apply {
parentFile.mkdirs()
if (!exists()) generateCertificate(this)
}
}
private fun initVia() {
Via.init(
ViaManagerImpl.builder()
.injector(AspirinInjector)
.loader(AspirinLoader)
.commandHandler(AspirinCommands)
.commandHandler(ViaAspirinCommand)
.platform(AspirinPlatform).build()
)
MappingDataLoader.enableMappingsCache()
@ -127,37 +164,39 @@ fun main(args: Array<String>) {
ProtocolVersion.register(-2, "AUTO")
AspirinRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml")))
AspirinBackwards.init(File("config/viabackwards"))
}
val future = ServerBootstrap()
private fun bindPorts(args: Array<String>) {
chFuture = ServerBootstrap()
.group(parentLoop, childLoop)
.channelFactory(channelServerSocketFactory())
.channelFactory(channelServerSocketFactory(parentLoop))
.childHandler(FrontEndInit)
.childOption(ChannelOption.IP_TOS, 0x18)
.childOption(ChannelOption.TCP_NODELAY, true)
.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 {
viaaasLogger.info("Binded minecraft into " + future.sync().channel().localAddress())
ktorServer = embeddedServer(Netty, commandLineEnvironment(args)) {}.start(false)
} catch (e: Exception) {
runningServer = false
e.printStackTrace()
Via.getManager().connectionManager.connections.forEach {
it.channel?.pipeline()?.get(MinecraftHandler::class.java)?.disconnect("Stopping")
}
ktorServer?.stop(1000, 1000)
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() {

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

View File

@ -35,7 +35,7 @@ object ProtocolDetector {
try {
val ch: ChannelFuture = Bootstrap()
.group(childLoop)
.channelFactory(channelSocketFactory())
.channelFactory(channelSocketFactory(childLoop))
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.IP_TOS, 0x18)
.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.parseProtocol
import com.github.creeper123123321.viaaas.writeFlushClose
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import io.netty.channel.ChannelHandlerContext
import us.myles.ViaVersion.api.Via
import us.myles.ViaVersion.packets.State
import java.util.*
@ -51,8 +51,20 @@ object StatusState : MinecraftConnectionState {
super.disconnect(handler, msg)
val packet = StatusResponse()
packet.json = """{"version": {"name": "VIAaaS", "protocol": -1}, "players": {"max": 0, "online": 0,
| "sample": []}, "description": {"text": ${Gson().toJson("§c$msg")}}}""".trimMargin()
packet.json = JsonObject().also {
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)
}
}

View File

@ -20,10 +20,11 @@ import java.net.InetAddress
import java.net.InetSocketAddress
private fun createBackChannel(handler: MinecraftHandler, socketAddr: InetSocketAddress, state: State): ChannelFuture {
val loop = handler.data.frontChannel.eventLoop()
return Bootstrap()
.handler(BackEndInit(handler.data))
.channelFactory(channelSocketFactory())
.group(handler.data.frontChannel.eventLoop())
.channelFactory(channelSocketFactory(loop.parent()))
.group(loop)
.option(ChannelOption.IP_TOS, 0x18)
.option(ChannelOption.TCP_NODELAY, true)
.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
#######################
#