diff --git a/build.gradle.kts b/build.gradle.kts index 4b0c4f0..c3e4e78 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } application { - mainClassName = "com.github.creeper123123321.viaaas.ViaaaSKt" + mainClassName = "com.github.creeper123123321.viaaas.VIAaaSKt" } java { @@ -36,7 +36,7 @@ dependencies { implementation("io.ktor:ktor-server-netty:$ktorVersion") implementation("io.ktor:ktor-websockets:$ktorVersion") implementation("ch.qos.logback:logback-classic:1.2.3") - testCompile("io.ktor:ktor-server-test-host:$ktorVersion") + testImplementation("io.ktor:ktor-server-test-host:$ktorVersion") } val run: JavaExec by tasks diff --git a/src/main/kotlin/com/github/creeper123123321/viaaas/CloudProtocol.kt b/src/main/kotlin/com/github/creeper123123321/viaaas/CloudProtocol.kt index 410c36f..aa38cf5 100644 --- a/src/main/kotlin/com/github/creeper123123321/viaaas/CloudProtocol.kt +++ b/src/main/kotlin/com/github/creeper123123321/viaaas/CloudProtocol.kt @@ -46,10 +46,10 @@ object CloudHeadProtocol : SimpleProtocol() { handler { wrapper: PacketWrapper -> val playerVer = wrapper.passthrough(Type.VAR_INT) val addr = wrapper.passthrough(Type.STRING) // Server Address - wrapper.passthrough(Type.UNSIGNED_SHORT) + val receivedPort = wrapper.passthrough(Type.UNSIGNED_SHORT) val nextState = wrapper.passthrough(Type.VAR_INT) - val parsed = ViaaaSAddress().parse(addr) + val parsed = VIAaaSAddress().parse(addr, VIAaaSConfig.hostName) logger.info("connecting ${wrapper.user().channel!!.remoteAddress()} ($playerVer) to ${parsed.realAddress}:${parsed.port} (${parsed.protocol})") @@ -64,7 +64,7 @@ object CloudHeadProtocol : SimpleProtocol() { val frontForwarder = wrapper.user().channel!!.pipeline().get(CloudSideForwarder::class.java) try { var srvResolvedAddr = parsed.realAddress - var srvResolvedPort = parsed.port + var srvResolvedPort = if (parsed.port != 0) parsed.port else receivedPort if (srvResolvedPort == 25565) { try { // https://github.com/GeyserMC/Geyser/blob/99e72f35b308542cf0dbfb5b58816503c3d6a129/connector/src/main/java/org/geysermc/connector/GeyserConnector.java diff --git a/src/main/kotlin/com/github/creeper123123321/viaaas/ViaaaS.kt b/src/main/kotlin/com/github/creeper123123321/viaaas/VIAaaS.kt similarity index 65% rename from src/main/kotlin/com/github/creeper123123321/viaaas/ViaaaS.kt rename to src/main/kotlin/com/github/creeper123123321/viaaas/VIAaaS.kt index 42829f7..5fdf0c4 100644 --- a/src/main/kotlin/com/github/creeper123123321/viaaas/ViaaaS.kt +++ b/src/main/kotlin/com/github/creeper123123321/viaaas/VIAaaS.kt @@ -10,6 +10,7 @@ import io.ktor.routing.* import io.ktor.server.netty.* import io.ktor.websocket.* import io.netty.bootstrap.ServerBootstrap +import io.netty.channel.ChannelOption import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.socket.nio.NioServerSocketChannel import kotlinx.coroutines.channels.consumeEach @@ -17,27 +18,27 @@ import us.myles.ViaVersion.ViaManager import us.myles.ViaVersion.api.Via import us.myles.ViaVersion.api.data.MappingDataLoader import us.myles.ViaVersion.api.protocol.ProtocolVersion +import us.myles.ViaVersion.util.Config import java.io.File import java.net.InetAddress import java.time.Duration import java.util.concurrent.ConcurrentHashMap import kotlin.system.exitProcess - fun main(args: Array) { - val args = args.mapIndexed { i, content -> i to content }.toMap() + File("config/https.jks").apply { + parentFile.mkdirs() + if (!exists()) generateCertificate(this) + } + Via.init(ViaManager.builder() .injector(CloudInjector) .loader(CloudLoader) .commandHandler(CloudCommands) .platform(CloudPlatform).build()) - MappingDataLoader.enableMappingsCache() - Via.getManager().init() - CloudRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml"))) - CloudBackwards.init(File("config/viabackwards.yml")) val boss = NioEventLoopGroup() @@ -45,8 +46,9 @@ fun main(args: Array) { val future = ServerBootstrap().group(boss, worker) .channel(NioServerSocketChannel::class.java) .childHandler(ChannelInit) - .bind(InetAddress.getByName(args[0] ?: "::"), args[1]?.toIntOrNull() ?: 25565) - + .childOption(ChannelOption.IP_TOS, 0x18) + .childOption(ChannelOption.TCP_NODELAY, true) + .bind(InetAddress.getByName(VIAaaSConfig.bindAddress), VIAaaSConfig.port) println("Binded minecraft into " + future.sync().channel().localAddress()) Thread { EngineMain.main(arrayOf()) }.start() @@ -72,13 +74,32 @@ fun main(args: Array) { exitProcess(0) // todo what's stucking? } -class ViaaaSAddress { +fun Application.mainWeb() { + ViaWebApp().apply { main() } +} + +object VIAaaSConfig : Config(File("config/viaaas.yml")) { + init { + reloadConfig() + } + + override fun getUnsupportedOptions() = emptyList().toMutableList() + override fun getDefaultConfigURL() = VIAaaSConfig::class.java.classLoader.getResource("viaaas.yml")!! + override fun handleConfig(p0: MutableMap?) { + } + + val port: Int get() = this.getInt("port", 25565) + val bindAddress: String get() = this.getString("bind-address", "localhost")!! + val hostName: String get() = this.getString("host-name", "viaaas.localhost")!! +} + +class VIAaaSAddress { var protocol = 0 var viaSuffix: String? = null var realAddress: String? = null - var port: Int = 25565 - var online: Boolean = false - fun parse(address: String): ViaaaSAddress { + var port = 0 + var online = false + fun parse(address: String, viaHostName: String): VIAaaSAddress { val parts = address.split('.') var foundDomain = false var foundOptions = false @@ -112,7 +133,8 @@ class ViaaaSAddress { if (foundOptions) { realAddrPart = true } - } else if (part.equals("viaaas", ignoreCase = true)) { + } else if (parts.filterIndexed { a, _ -> a <= i } + .joinToString(".").equals(viaHostName, ignoreCase = true)) { foundDomain = true } if (realAddrPart) { @@ -131,96 +153,4 @@ class ViaaaSAddress { } return this } -} - -fun Application.mainWeb() { - ViaWebApp().apply { main() } -} - -class ViaWebApp { - data class WebSession(val id: String) - - val server = WebDashboardServer() - - fun Application.main() { - install(DefaultHeaders) - install(CallLogging) - install(WebSockets) { - pingPeriod = Duration.ofMinutes(1) - } - - routing { - webSocket("/ws") { - server.connected(this) - - try { - incoming.consumeEach { frame -> - if (frame is Frame.Text) { - server.onMessage(this, frame.readText()) - } - } - } finally { - server.disconnected(this) - } - } - - static { - defaultResource("auth.html", "web") - resources("web") - } - - } - } -} - -class WebDashboardServer { - val clients = ConcurrentHashMap() - suspend fun connected(ws: WebSocketSession) { - clients[ws] = WebClient(ws, WebLogin()) - } - - suspend fun onMessage(ws: WebSocketSession, msg: String) { - val client = clients[ws]!! - client.state.onMessage(client, msg) - } - - suspend fun disconnected(ws: WebSocketSession) { - val client = clients[ws]!! - client.state.disconnected(client) - clients.remove(ws) - } -} - - -data class WebClient(val ws: WebSocketSession, val state: WebState) { -} - - -interface WebState { - fun onMessage(webClient: WebClient, msg: String) - fun disconnected(webClient: WebClient) -} - -class WebLogin : WebState { - override fun onMessage(webClient: WebClient, msg: String) { - TODO("Not yet implemented") - } - - override fun disconnected(webClient: WebClient) { - TODO("Not yet implemented") - } -} - - -object CertificateGenerator { - @JvmStatic - fun main(args: Array) { - val jksFile = File("build/temporary.jks").apply { - parentFile.mkdirs() - } - - if (!jksFile.exists()) { - generateCertificate(jksFile) // Generates the certificate - } - } -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/creeper123123321/viaaas/ViaWeb.kt b/src/main/kotlin/com/github/creeper123123321/viaaas/ViaWeb.kt new file mode 100644 index 0000000..2a39149 --- /dev/null +++ b/src/main/kotlin/com/github/creeper123123321/viaaas/ViaWeb.kt @@ -0,0 +1,93 @@ +package com.github.creeper123123321.viaaas + +import io.ktor.application.* +import io.ktor.features.* +import io.ktor.http.cio.websocket.* +import io.ktor.http.cio.websocket.WebSockets +import io.ktor.http.content.* +import io.ktor.routing.* +import io.ktor.websocket.* +import kotlinx.coroutines.channels.consumeEach +import java.time.Duration +import java.util.concurrent.ConcurrentHashMap + +// todo https://minecraft.id/documentation + +class ViaWebApp { + val server = WebDashboardServer() + + fun Application.main() { + install(DefaultHeaders) + install(CallLogging) + install(WebSockets) { + pingPeriod = Duration.ofMinutes(1) + } + + routing { + webSocket("/ws") { + server.connected(this) + try { + incoming.consumeEach { frame -> + if (frame is Frame.Text) { + server.onMessage(this, frame.readText()) + } + } + } finally { + server.disconnected(this) + } + } + + static { + defaultResource("index.html", "web") + resources("web") + } + } + } +} + +class WebDashboardServer { + val clients = ConcurrentHashMap() + suspend fun connected(ws: WebSocketSession) { + val loginState = WebLogin() + val client = WebClient(ws, loginState) + clients[ws] = client + loginState.start(client) + } + + suspend fun onMessage(ws: WebSocketSession, msg: String) { + val client = clients[ws]!! + client.state.onMessage(client, msg) + } + + suspend fun disconnected(ws: WebSocketSession) { + val client = clients[ws]!! + client.state.disconnected(client) + clients.remove(ws) + } +} + + +data class WebClient(val ws: WebSocketSession, val state: WebState) { +} + +interface WebState { + suspend fun start(webClient: WebClient) + suspend fun onMessage(webClient: WebClient, msg: String) + suspend fun disconnected(webClient: WebClient) +} + +class WebLogin : WebState { + override suspend fun start(webClient: WebClient) { + webClient.ws.send("test") + webClient.ws.flush() + TODO("Not yet implemented") + } + + override suspend fun onMessage(webClient: WebClient, msg: String) { + TODO("Not yet implemented") + } + + override suspend fun disconnected(webClient: WebClient) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 032ecf5..0d011ba 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -5,12 +5,12 @@ ktor { } application { - modules = [ com.github.creeper123123321.viaaas.ViaaaSKt.mainWeb ] + modules = [ com.github.creeper123123321.viaaas.VIAaaSKt.mainWeb ] } security { ssl { - keyStore = build/temporary.jks + keyStore = config/https.jks keyAlias = mykey keyStorePassword = changeit privateKeyPassword = changeit diff --git a/src/main/resources/viaaas.yml b/src/main/resources/viaaas.yml new file mode 100644 index 0000000..cf36a3f --- /dev/null +++ b/src/main/resources/viaaas.yml @@ -0,0 +1,7 @@ +# See application.conf in resources for https interface options +# Port used for binding Minecraft port +port: 25565 +# Address to bind +bind-address: localhost +# Host name of this instance, that will be used in the virtual host +host-name: viaaas.localhost \ No newline at end of file diff --git a/src/main/resources/web/auth.html b/src/main/resources/web/auth.html index b376c10..1f744f7 100644 --- a/src/main/resources/web/auth.html +++ b/src/main/resources/web/auth.html @@ -1,43 +1,46 @@ - - + + - + + + VIAaaS + +

WebSocket connection status: ?

+

DO NOT TYPE YOUR CREDENTIALS IF YOU DON'T TRUST THE CONNECTION TO THIS VIAAAS INSTANCE!

+

Minecraft Login:

+

Minecraft Password:

+ \ No newline at end of file diff --git a/src/main/resources/web/index.html b/src/main/resources/web/index.html new file mode 100644 index 0000000..cd5509b --- /dev/null +++ b/src/main/resources/web/index.html @@ -0,0 +1,15 @@ + + + + + + VIAaaS + + + +

VIAaaS

+

Authentication management

+ + \ No newline at end of file