mirror of
https://github.com/ViaVersion/VIAaaS.git
synced 2025-01-23 21:52:35 +01:00
parent
e80c1384c2
commit
49a39eb592
@ -57,8 +57,8 @@ dependencies {
|
|||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
|
|
||||||
val vvVer = "4.2.2-SNAPSHOT"
|
val vvVer = "4.3.0-22w11a-SNAPSHOT"
|
||||||
val vbVer = "4.2.1-SNAPSHOT"
|
val vbVer = "4.3.0-22w11a-SNAPSHOT"
|
||||||
val vrVer = "ae62eba"
|
val vrVer = "ae62eba"
|
||||||
implementation("com.viaversion:viaversion:$vvVer") { isTransitive = false }
|
implementation("com.viaversion:viaversion:$vvVer") { isTransitive = false }
|
||||||
implementation("com.viaversion:viabackwards:$vbVer") { isTransitive = false }
|
implementation("com.viaversion:viabackwards:$vbVer") { isTransitive = false }
|
||||||
@ -68,7 +68,7 @@ dependencies {
|
|||||||
implementation("io.netty:netty-tcnative-boringssl-static:2.0.50.Final")
|
implementation("io.netty:netty-tcnative-boringssl-static:2.0.50.Final")
|
||||||
implementation("io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.12.Final:linux-x86_64")
|
implementation("io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.12.Final:linux-x86_64")
|
||||||
|
|
||||||
implementation("com.google.guava:guava:31.0.1-jre")
|
implementation("com.google.guava:guava:31.1-jre")
|
||||||
implementation("com.velocitypowered:velocity-native:3.1.0")
|
implementation("com.velocitypowered:velocity-native:3.1.0")
|
||||||
implementation("net.coobird:thumbnailator:0.4.17")
|
implementation("net.coobird:thumbnailator:0.4.17")
|
||||||
implementation("org.powernukkit.fastutil:fastutil-lite:8.1.1")
|
implementation("org.powernukkit.fastutil:fastutil-lite:8.1.1")
|
||||||
|
@ -41,4 +41,9 @@ public class StatusKicked implements ConnectionState {
|
|||||||
public void onInactivated(@NotNull MinecraftHandler handler) {
|
public void onInactivated(@NotNull MinecraftHandler handler) {
|
||||||
ConnectionState.DefaultImpls.onInactivated(this, handler);
|
ConnectionState.DefaultImpls.onInactivated(this, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(@NotNull MinecraftHandler handler) {
|
||||||
|
ConnectionState.DefaultImpls.start(this, handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,7 @@ import com.google.common.primitives.Ints;
|
|||||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||||
import kotlin.text.StringsKt;
|
import kotlin.text.StringsKt;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class AddressParser {
|
public class AddressParser {
|
||||||
@ -19,11 +17,18 @@ public class AddressParser {
|
|||||||
public String username;
|
public String username;
|
||||||
public Boolean online;
|
public Boolean online;
|
||||||
|
|
||||||
public AddressParser parse(String address, String viaHostName) {
|
public AddressParser parse(String rawAddress, List<String> viaHostName) {
|
||||||
address = StringsKt.removeSuffix(address, ".");
|
String address = StringsKt.removeSuffix(rawAddress, ".");
|
||||||
String suffixRemoved = StringsKt.removeSuffix(address, "." + viaHostName);
|
|
||||||
|
|
||||||
if (suffixRemoved.equals(address)) {
|
String suffix = viaHostName.stream()
|
||||||
|
.filter(s -> StringsKt.endsWith("." + address, s, true))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
String suffixRemoved;
|
||||||
|
if (suffix != null) {
|
||||||
|
suffixRemoved = StringsKt.removeSuffix(address, "." + suffix);
|
||||||
|
} else {
|
||||||
serverAddress = address;
|
serverAddress = address;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -43,7 +48,7 @@ public class AddressParser {
|
|||||||
|
|
||||||
serverAddress = String.join(".", Lists.reverse(serverParts));
|
serverAddress = String.join(".", Lists.reverse(serverParts));
|
||||||
viaOptions = String.join(".", Lists.reverse(optionsParts));
|
viaOptions = String.join(".", Lists.reverse(optionsParts));
|
||||||
viaSuffix = viaHostName;
|
viaSuffix = suffix;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -61,7 +66,7 @@ public class AddressParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String arg = part.substring(2);
|
String arg = part.substring(2);
|
||||||
switch (option) {
|
switch (option.toLowerCase(Locale.ROOT)) {
|
||||||
case "o": {
|
case "o": {
|
||||||
parseOnlineMode(arg);
|
parseOnlineMode(arg);
|
||||||
break;
|
break;
|
||||||
|
@ -32,4 +32,7 @@ interface ConnectionState {
|
|||||||
logDisconnect(handler, "-")
|
logDisconnect(handler, "-")
|
||||||
handler.other?.close()
|
handler.other?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun start(handler: MinecraftHandler) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,12 @@ import com.google.common.util.concurrent.RateLimiter
|
|||||||
import com.viaversion.aas.codec.packet.Packet
|
import com.viaversion.aas.codec.packet.Packet
|
||||||
import com.viaversion.aas.codec.packet.handshake.Handshake
|
import com.viaversion.aas.codec.packet.handshake.Handshake
|
||||||
import com.viaversion.aas.config.VIAaaSConfig
|
import com.viaversion.aas.config.VIAaaSConfig
|
||||||
import com.viaversion.aas.fireExceptionCaughtIfOpen
|
|
||||||
import com.viaversion.aas.handler.MinecraftHandler
|
import com.viaversion.aas.handler.MinecraftHandler
|
||||||
import com.viaversion.aas.mcLogger
|
import com.viaversion.aas.mcLogger
|
||||||
import com.viaversion.aas.setAutoRead
|
|
||||||
import com.viaversion.aas.util.AddressParser
|
import com.viaversion.aas.util.AddressParser
|
||||||
import com.viaversion.aas.util.StacklessException
|
import com.viaversion.aas.util.StacklessException
|
||||||
import com.viaversion.viaversion.api.protocol.packet.State
|
import com.viaversion.viaversion.api.protocol.packet.State
|
||||||
import io.netty.channel.ChannelHandlerContext
|
import io.netty.channel.ChannelHandlerContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -52,7 +48,7 @@ class HandshakeState : ConnectionState {
|
|||||||
private fun handleNextState(handler: MinecraftHandler, packet: Handshake) {
|
private fun handleNextState(handler: MinecraftHandler, packet: Handshake) {
|
||||||
handler.data.frontVer = packet.protocolId
|
handler.data.frontVer = packet.protocolId
|
||||||
when (packet.nextState.ordinal) {
|
when (packet.nextState.ordinal) {
|
||||||
1 -> handler.data.state = StatusState
|
1 -> handler.data.state = StatusState()
|
||||||
2 -> handler.data.state = LoginState()
|
2 -> handler.data.state = LoginState()
|
||||||
else -> throw StacklessException("Invalid next state")
|
else -> throw StacklessException("Invalid next state")
|
||||||
}
|
}
|
||||||
@ -63,36 +59,42 @@ class HandshakeState : ConnectionState {
|
|||||||
val extraData = packet.address.substringAfter(0.toChar(), missingDelimiterValue = "").ifEmpty { null }
|
val extraData = packet.address.substringAfter(0.toChar(), missingDelimiterValue = "").ifEmpty { null }
|
||||||
val virtualHostNoExtra = packet.address.substringBefore(0.toChar())
|
val virtualHostNoExtra = packet.address.substringBefore(0.toChar())
|
||||||
|
|
||||||
val parsed = VIAaaSConfig.hostName.map {
|
val parsed = AddressParser().parse(virtualHostNoExtra, VIAaaSConfig.hostName)
|
||||||
AddressParser().parse(virtualHostNoExtra, it)
|
|
||||||
}.sortedBy {
|
|
||||||
it.viaSuffix == null
|
|
||||||
}.first()
|
|
||||||
|
|
||||||
val backProto = parsed.protocol ?: -2
|
val backProto = parsed.protocol ?: -2
|
||||||
val hadHostname = parsed.viaSuffix != null
|
val hadHostname = parsed.viaSuffix != null
|
||||||
|
|
||||||
packet.address = parsed.serverAddress!!
|
val backAddress = parsed.serverAddress!!
|
||||||
packet.port = parsed.port ?: VIAaaSConfig.defaultBackendPort ?: virtualPort
|
val port = parsed.port ?: VIAaaSConfig.defaultBackendPort ?: virtualPort
|
||||||
|
val host = HostAndPort.fromParts(backAddress, port)
|
||||||
|
|
||||||
var frontOnline = parsed.online
|
var frontOnline = parsed.online
|
||||||
if (VIAaaSConfig.forceOnlineMode) frontOnline = true
|
|
||||||
|
val addressFromWeb = VIAaaSConfig.hostName.any { parsed.serverAddress.equals(it, ignoreCase = true) }
|
||||||
|
|
||||||
handler.data.backServerVer = backProto
|
handler.data.backServerVer = backProto
|
||||||
(handler.data.state as? LoginState)?.also {
|
(handler.data.state as? LoginState)?.also {
|
||||||
it.frontOnline = frontOnline
|
it.frontOnline = frontOnline
|
||||||
it.backName = parsed.username
|
it.backName = parsed.username
|
||||||
it.backAddress = HostAndPort.fromParts(packet.address, packet.port)
|
if (!addressFromWeb) {
|
||||||
|
it.backAddress = host
|
||||||
|
}
|
||||||
it.extraData = extraData
|
it.extraData = extraData
|
||||||
}
|
}
|
||||||
|
(handler.data.state as? StatusState)?.also {
|
||||||
|
if (!addressFromWeb) {
|
||||||
|
it.address = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val playerAddr = handler.data.frontHandler.endRemoteAddress
|
val playerAddr = handler.data.frontHandler.endRemoteAddress
|
||||||
mcLogger.debug(
|
mcLogger.debug(
|
||||||
"HS: $playerAddr ${handler.data.state.state.toString().substring(0, 1)} " +
|
"HS: $playerAddr ${handler.data.state.state.name[0]} " +
|
||||||
"$virtualHostNoExtra $virtualPort v${handler.data.frontVer}"
|
"$virtualHostNoExtra $virtualPort v${handler.data.frontVer}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!hadHostname && VIAaaSConfig.requireHostName) {
|
if (!hadHostname && VIAaaSConfig.requireHostName && !addressFromWeb
|
||||||
|
) {
|
||||||
throw StacklessException("Missing parts in hostname")
|
throw StacklessException("Missing parts in hostname")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,20 +106,7 @@ class HandshakeState : ConnectionState {
|
|||||||
checkRateLimit(handler, packet.nextState)
|
checkRateLimit(handler, packet.nextState)
|
||||||
handleVirtualHost(handler, packet)
|
handleVirtualHost(handler, packet)
|
||||||
|
|
||||||
connectStatus(handler, ctx, packet)
|
handler.data.state.start(handler)
|
||||||
}
|
|
||||||
|
|
||||||
private fun connectStatus(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Handshake) {
|
|
||||||
if (packet.nextState == State.STATUS) { // see LoginState for LOGIN
|
|
||||||
handler.data.frontChannel.setAutoRead(false)
|
|
||||||
handler.coroutineScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
connectBack(handler, packet.address, packet.port, packet.nextState)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
ctx.channel().fireExceptionCaughtIfOpen(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect(handler: MinecraftHandler, msg: String) {
|
override fun disconnect(handler: MinecraftHandler, msg: String) {
|
||||||
|
@ -32,7 +32,7 @@ class LoginState : ConnectionState {
|
|||||||
lateinit var frontServerId: String
|
lateinit var frontServerId: String
|
||||||
var frontOnline: Boolean? = null
|
var frontOnline: Boolean? = null
|
||||||
lateinit var frontName: String
|
lateinit var frontName: String
|
||||||
lateinit var backAddress: HostAndPort
|
var backAddress: HostAndPort? = null
|
||||||
lateinit var cryptoKey: KeyPair
|
lateinit var cryptoKey: KeyPair
|
||||||
var extraData: String? = null
|
var extraData: String? = null
|
||||||
var backName: String? = null
|
var backName: String? = null
|
||||||
@ -226,15 +226,24 @@ class LoginState : ConnectionState {
|
|||||||
|
|
||||||
handler.coroutineScope.launch(Dispatchers.IO) {
|
handler.coroutineScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
|
if (backAddress == null) {
|
||||||
|
mcLogger.info("Requesting address info from web for $frontName")
|
||||||
|
val info = AspirinServer.viaWebServer.requestAddressInfo(frontName).await()
|
||||||
|
backAddress = info.backHostAndPort
|
||||||
|
handler.data.backServerVer = info.backVersion
|
||||||
|
frontOnline = info.frontOnline
|
||||||
|
}
|
||||||
|
if (VIAaaSConfig.forceOnlineMode) frontOnline = true
|
||||||
if (frontOnline != null) {
|
if (frontOnline != null) {
|
||||||
when (frontOnline) {
|
when (frontOnline) {
|
||||||
false -> callbackPlayerId.complete(generateOfflinePlayerUuid(frontName))
|
false -> callbackPlayerId.complete(generateOfflinePlayerUuid(frontName))
|
||||||
true -> authenticateOnlineFront(handler.data.frontChannel) // forced
|
true -> authenticateOnlineFront(handler.data.frontChannel) // forced
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
val id = callbackPlayerId.await()
|
val id = callbackPlayerId.await()
|
||||||
mcLogger.info("Login: ${handler.endRemoteAddress} $frontName $id")
|
mcLogger.info("Login: ${handler.endRemoteAddress} $frontName $id")
|
||||||
}
|
}
|
||||||
connectBack(handler, backAddress.host, backAddress.port, State.LOGIN, extraData)
|
connectBack(handler, HostAndPort.fromParts(backAddress!!.host, backAddress!!.port), State.LOGIN, extraData)
|
||||||
loginStart.username = backName!!
|
loginStart.username = backName!!
|
||||||
send(handler.data.backChannel!!, loginStart, true)
|
send(handler.data.backChannel!!, loginStart, true)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
package com.viaversion.aas.handler.state
|
package com.viaversion.aas.handler.state
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort
|
||||||
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 com.viaversion.aas.AspirinServer
|
import com.viaversion.aas.*
|
||||||
import com.viaversion.aas.codec.packet.Packet
|
import com.viaversion.aas.codec.packet.Packet
|
||||||
import com.viaversion.aas.codec.packet.UnknownPacket
|
import com.viaversion.aas.codec.packet.UnknownPacket
|
||||||
import com.viaversion.aas.codec.packet.status.StatusResponse
|
import com.viaversion.aas.codec.packet.status.StatusResponse
|
||||||
import com.viaversion.aas.config.VIAaaSConfig
|
import com.viaversion.aas.config.VIAaaSConfig
|
||||||
import com.viaversion.aas.handler.MinecraftHandler
|
import com.viaversion.aas.handler.MinecraftHandler
|
||||||
import com.viaversion.aas.handler.forward
|
import com.viaversion.aas.handler.forward
|
||||||
import com.viaversion.aas.parseProtocol
|
|
||||||
import com.viaversion.aas.send
|
|
||||||
import com.viaversion.aas.util.StacklessException
|
import com.viaversion.aas.util.StacklessException
|
||||||
import com.viaversion.viaversion.api.protocol.packet.State
|
import com.viaversion.viaversion.api.protocol.packet.State
|
||||||
import io.netty.channel.ChannelHandlerContext
|
import io.netty.channel.ChannelHandlerContext
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object StatusState : ConnectionState {
|
class StatusState : ConnectionState {
|
||||||
override val state: State
|
override val state: State
|
||||||
get() = State.STATUS
|
get() = State.STATUS
|
||||||
|
var address: HostAndPort? = null
|
||||||
|
|
||||||
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
|
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
|
||||||
if (packet is UnknownPacket) throw StacklessException("Invalid packet")
|
if (packet is UnknownPacket) throw StacklessException("Invalid packet")
|
||||||
@ -72,4 +74,19 @@ object StatusState : ConnectionState {
|
|||||||
send(handler.data.frontChannel, packet, flush = true)
|
send(handler.data.frontChannel, packet, flush = true)
|
||||||
handler.data.state = StatusKicked()
|
handler.data.state = StatusKicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun start(handler: MinecraftHandler) {
|
||||||
|
handler.data.frontChannel.setAutoRead(false)
|
||||||
|
handler.coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
if (address != null) {
|
||||||
|
connectBack(handler, address!!, state)
|
||||||
|
} else {
|
||||||
|
handler.disconnect("VIAaaS")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
handler.data.frontChannel.fireExceptionCaughtIfOpen(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ private suspend fun createBackChannel(
|
|||||||
.channel()
|
.channel()
|
||||||
(channel.pipeline()["proxy"] as? ProxyHandler)?.connectFuture()?.suspendAwait()
|
(channel.pipeline()["proxy"] as? ProxyHandler)?.connectFuture()?.suspendAwait()
|
||||||
|
|
||||||
mcLogger.info("+ ${state.name.substring(0, 1)} ${handler.endRemoteAddress} -> $socketAddr")
|
mcLogger.info("+ ${state.name[0]} ${handler.endRemoteAddress} -> $socketAddr")
|
||||||
handler.data.backChannel = channel as SocketChannel
|
handler.data.backChannel = channel as SocketChannel
|
||||||
|
|
||||||
autoDetectVersion(handler, socketAddr)
|
autoDetectVersion(handler, socketAddr)
|
||||||
@ -143,12 +143,11 @@ private suspend fun resolveBackendAddresses(hostAndPort: HostAndPort): List<Inet
|
|||||||
|
|
||||||
suspend fun connectBack(
|
suspend fun connectBack(
|
||||||
handler: MinecraftHandler,
|
handler: MinecraftHandler,
|
||||||
address: String,
|
address: HostAndPort,
|
||||||
port: Int,
|
|
||||||
state: State,
|
state: State,
|
||||||
extraData: String? = null
|
extraData: String? = null
|
||||||
) {
|
) {
|
||||||
val addresses = resolveBackendAddresses(HostAndPort.fromParts(address, port))
|
val addresses = resolveBackendAddresses(address)
|
||||||
|
|
||||||
if (addresses.isEmpty()) throw StacklessException("Hostname has no IP addresses")
|
if (addresses.isEmpty()) throw StacklessException("Hostname has no IP addresses")
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package com.viaversion.aas.web
|
package com.viaversion.aas.web
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort
|
||||||
|
import com.google.common.primitives.Ints
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
import com.viaversion.aas.*
|
import com.viaversion.aas.*
|
||||||
import com.viaversion.aas.util.StacklessException
|
import com.viaversion.aas.util.StacklessException
|
||||||
|
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.forms.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
@ -27,10 +30,11 @@ class WebLogin : WebState {
|
|||||||
|
|
||||||
when (obj.getAsJsonPrimitive("action").asString) {
|
when (obj.getAsJsonPrimitive("action").asString) {
|
||||||
"offline_login" -> handleOfflineLogin(webClient, msg, obj)
|
"offline_login" -> handleOfflineLogin(webClient, msg, obj)
|
||||||
"minecraft_id_login" -> handleMcIdLogin(webClient, msg, obj)
|
"minecraft_id_login" -> handleMcIdLogin(webClient, obj)
|
||||||
"listen_login_requests" -> handleListenLogins(webClient, msg, obj)
|
"listen_login_requests" -> handleListenLogins(webClient, obj)
|
||||||
"unlisten_login_requests" -> handleUnlisten(webClient, msg, obj)
|
"unlisten_login_requests" -> handleUnlisten(webClient, obj)
|
||||||
"session_hash_response" -> handleSessionResponse(webClient, msg, obj)
|
"session_hash_response" -> handleSessionResponse(webClient, obj)
|
||||||
|
"parameters_response" -> handleParametersResponse(webClient, obj)
|
||||||
else -> throw StacklessException("invalid action!")
|
else -> throw StacklessException("invalid action!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +81,7 @@ class WebLogin : WebState {
|
|||||||
webLogger.info("Token gen: ${webClient.id}: offline $username $uuid")
|
webLogger.info("Token gen: ${webClient.id}: offline $username $uuid")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleMcIdLogin(webClient: WebClient, msg: String, obj: JsonObject) {
|
private suspend fun handleMcIdLogin(webClient: WebClient, obj: JsonObject) {
|
||||||
val username = obj["username"].asString
|
val username = obj["username"].asString
|
||||||
val code = obj["code"].asString
|
val code = obj["code"].asString
|
||||||
|
|
||||||
@ -102,7 +106,7 @@ class WebLogin : WebState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleListenLogins(webClient: WebClient, msg: String, obj: JsonObject) {
|
private suspend fun handleListenLogins(webClient: WebClient, obj: JsonObject) {
|
||||||
val token = obj.getAsJsonPrimitive("token").asString
|
val token = obj.getAsJsonPrimitive("token").asString
|
||||||
val user = webClient.server.parseToken(token)
|
val user = webClient.server.parseToken(token)
|
||||||
val response = JsonObject().also {
|
val response = JsonObject().also {
|
||||||
@ -121,7 +125,7 @@ class WebLogin : WebState {
|
|||||||
webClient.ws.send(response.toString())
|
webClient.ws.send(response.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleUnlisten(webClient: WebClient, msg: String, obj: JsonObject) {
|
private suspend fun handleUnlisten(webClient: WebClient, obj: JsonObject) {
|
||||||
val uuid = UUID.fromString(obj.getAsJsonPrimitive("uuid").asString)
|
val uuid = UUID.fromString(obj.getAsJsonPrimitive("uuid").asString)
|
||||||
webLogger.info("Unlisten: ${webClient.id}: $uuid")
|
webLogger.info("Unlisten: ${webClient.id}: $uuid")
|
||||||
val response = JsonObject().also {
|
val response = JsonObject().also {
|
||||||
@ -132,8 +136,26 @@ class WebLogin : WebState {
|
|||||||
webClient.ws.send(response.toString())
|
webClient.ws.send(response.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleSessionResponse(webClient: WebClient, msg: String, obj: JsonObject) {
|
private fun handleSessionResponse(webClient: WebClient, obj: JsonObject) {
|
||||||
val hash = obj["session_hash"].asString
|
val hash = obj["session_hash"].asString
|
||||||
webClient.server.sessionHashCallbacks.getIfPresent(hash)?.complete(Unit)
|
webClient.server.sessionHashCallbacks.getIfPresent(hash)?.complete(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleParametersResponse(webClient: WebClient, obj: JsonObject) {
|
||||||
|
val callback = UUID.fromString(obj["callback"].asString)
|
||||||
|
webClient.server.addressCallbacks[callback].complete(
|
||||||
|
WebServer.AddressInfo(
|
||||||
|
backVersion = obj["version"].asString.let {
|
||||||
|
var protocol = Ints.tryParse(it)
|
||||||
|
if (protocol == null) {
|
||||||
|
val ver = ProtocolVersion.getClosest(it)
|
||||||
|
if (ver != null) protocol = ver.version
|
||||||
|
}
|
||||||
|
protocol ?: -2
|
||||||
|
},
|
||||||
|
backHostAndPort = HostAndPort.fromParts(obj["host"].asString, obj["port"].asInt),
|
||||||
|
frontOnline = obj["frontOnline"].asString.toBooleanStrictOrNull()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,11 @@ import com.google.common.cache.CacheBuilder
|
|||||||
import com.google.common.cache.CacheLoader
|
import com.google.common.cache.CacheLoader
|
||||||
import com.google.common.collect.MultimapBuilder
|
import com.google.common.collect.MultimapBuilder
|
||||||
import com.google.common.collect.Multimaps
|
import com.google.common.collect.Multimaps
|
||||||
|
import com.google.common.net.HostAndPort
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.viaversion.aas.AspirinServer
|
import com.viaversion.aas.*
|
||||||
import com.viaversion.aas.config.VIAaaSConfig
|
import com.viaversion.aas.config.VIAaaSConfig
|
||||||
import com.viaversion.aas.parseUndashedId
|
|
||||||
import com.viaversion.aas.reverseLookup
|
|
||||||
import com.viaversion.aas.util.StacklessException
|
import com.viaversion.aas.util.StacklessException
|
||||||
import com.viaversion.aas.webLogger
|
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
@ -25,6 +23,7 @@ import io.netty.handler.codec.dns.DnsRecordType
|
|||||||
import io.netty.util.ReferenceCountUtil
|
import io.netty.util.ReferenceCountUtil
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.future.asCompletableFuture
|
import kotlinx.coroutines.future.asCompletableFuture
|
||||||
|
import kotlinx.coroutines.future.await
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.SocketAddress
|
import java.net.SocketAddress
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -78,14 +77,63 @@ class WebServer {
|
|||||||
.build<String, CompletableFuture<UUID?>>(CacheLoader.from { name ->
|
.build<String, CompletableFuture<UUID?>>(CacheLoader.from { name ->
|
||||||
CoroutineScope(Dispatchers.IO).async {
|
CoroutineScope(Dispatchers.IO).async {
|
||||||
AspirinServer.httpClient
|
AspirinServer.httpClient
|
||||||
.get("https://api.mojang.com/users/profiles/minecraft/$name").body<JsonObject?>()
|
.get("https://api.mojang.com/users/profiles/minecraft/$name")
|
||||||
?.get("id")?.asString?.let { parseUndashedId(it) }
|
.body<JsonObject?>()?.get("id")?.asString?.let { parseUndashedId(it) }
|
||||||
}.asCompletableFuture()
|
}.asCompletableFuture()
|
||||||
})
|
})
|
||||||
|
|
||||||
val sessionHashCallbacks = CacheBuilder.newBuilder()
|
val sessionHashCallbacks = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(30, TimeUnit.SECONDS)
|
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||||
.build<String, CompletableFuture<Unit>>(CacheLoader.from { _ -> CompletableFuture() })
|
.build<String, CompletableFuture<Unit>>(CacheLoader.from { _ -> CompletableFuture() })
|
||||||
|
val addressCallbacks = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||||
|
.build<UUID, CompletableFuture<AddressInfo>>(CacheLoader.from { _ -> CompletableFuture() })
|
||||||
|
|
||||||
|
data class AddressInfo(val backVersion: Int, val backHostAndPort: HostAndPort, var frontOnline: Boolean? = null)
|
||||||
|
|
||||||
|
suspend fun requestAddressInfo(frontName: String): CompletableFuture<AddressInfo> {
|
||||||
|
var onlineId: UUID? = null
|
||||||
|
try {
|
||||||
|
onlineId = usernameIdCache[frontName].await()
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
webLogger.debug("Couldn't get online uuid for $frontName", e)
|
||||||
|
}
|
||||||
|
val offlineId = generateOfflinePlayerUuid(frontName)
|
||||||
|
|
||||||
|
val callbackId = UUID.randomUUID()
|
||||||
|
val future = addressCallbacks.get(callbackId)
|
||||||
|
CoroutineScope(coroutineContext).apply {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
run sending@{
|
||||||
|
onlineId?.let {
|
||||||
|
if (sendRequestAddress(it, callbackId)) return@sending
|
||||||
|
}
|
||||||
|
if (sendRequestAddress(offlineId, callbackId)) return@sending
|
||||||
|
future.completeExceptionally(StacklessException("Username $frontName not listened. Use web auth to select backend server address."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
delay(20_000)
|
||||||
|
future.completeExceptionally(StacklessException("No response from browser"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun sendRequestAddress(uuid: UUID, callbackId: UUID): Boolean {
|
||||||
|
var sent = false
|
||||||
|
listeners.get(uuid).forEach {
|
||||||
|
it.ws.send(
|
||||||
|
JsonObject().also {
|
||||||
|
it.addProperty("action", "parameters_request")
|
||||||
|
it.addProperty("callback", callbackId.toString())
|
||||||
|
}.toString()
|
||||||
|
)
|
||||||
|
it.ws.flush()
|
||||||
|
sent = true
|
||||||
|
}
|
||||||
|
return sent
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun requestSessionJoin(
|
suspend fun requestSessionJoin(
|
||||||
frontName: String,
|
frontName: String,
|
||||||
|
@ -91,12 +91,32 @@ script-src 'self' https://*.cloudflare.com/ https://alcdn.msauth.net/ https://*.
|
|||||||
<p>WebSocket connection status: <span class="text-white bg-dark" id="connection_status">?</span></p>
|
<p>WebSocket connection status: <span class="text-white bg-dark" id="connection_status">?</span></p>
|
||||||
<p>CORS Proxy status: <span class="text-white bg-dark" id="cors_status">?</span></p>
|
<p>CORS Proxy status: <span class="text-white bg-dark" id="cors_status">?</span></p>
|
||||||
<hr>
|
<hr>
|
||||||
<p>Listening to frontend logins from: <span id="listening"></span></p>
|
<p>Listening to frontend logins from:</p>
|
||||||
|
<div id="listening"></div>
|
||||||
<div id="actions">
|
<div id="actions">
|
||||||
<button id="listen_continue" type="button" class="btn btn-primary">Listen to</button>
|
<button id="listen_continue" type="button" class="btn btn-primary">Listen to <span id="mcIdUsername"></span>
|
||||||
|
</button>
|
||||||
<button id="listen_premium" type="button" class="btn btn-primary">Listen to premium login</button>
|
<button id="listen_premium" type="button" class="btn btn-primary">Listen to premium login</button>
|
||||||
<button id="listen_offline" type="button" class="btn btn-primary">Listen to offline mode login</button>
|
<button id="listen_offline" type="button" class="btn btn-primary">Listen to offline mode login</button>
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
|
<p>Connecting to backend server:</p>
|
||||||
|
<div id="connect">
|
||||||
|
<form class="input-group">
|
||||||
|
<input id="connect_address" type="text" class="form-control" placeholder="Address">
|
||||||
|
<input id="connect_port" type="number" min="1" max="65535" class="form-control"
|
||||||
|
placeholder="Port">
|
||||||
|
<input id="connect_version" type="text" class="form-control" placeholder="Version">
|
||||||
|
<select class="form-select" id="connect_online">
|
||||||
|
<option value="null" selected>Front Online Mode...</option>
|
||||||
|
<option>AUTO</option>
|
||||||
|
<option value="true">Force online</option>
|
||||||
|
<option value="false">Force offline (recommended for Geyser)</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<p>You can also use <a href="https://jo0001.github.io/ViaSetup/aspirin" id="viasetup_address">address
|
||||||
|
generator</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div aria-labelledby="settings-tab" class="tab-pane fade" id="settings">
|
<div aria-labelledby="settings-tab" class="tab-pane fade" id="settings">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Minecraft.id
|
// Minecraft.id
|
||||||
let urlParams = new URLSearchParams();
|
let urlParams = new URLSearchParams();
|
||||||
window.location.hash.substr(1).split("?")
|
window.location.hash.substring(1).split("?")
|
||||||
.map(it => new URLSearchParams(it)
|
.map(it => new URLSearchParams(it)
|
||||||
.forEach((a, b) => urlParams.append(b, a)));
|
.forEach((a, b) => urlParams.append(b, a)));
|
||||||
let mcIdUsername = urlParams.get("username");
|
let mcIdUsername = urlParams.get("username");
|
||||||
@ -138,7 +138,8 @@ $("#listen_offline").on("click", () => {
|
|||||||
workers.forEach(it => it.postMessage({action: "listen_pow", user: user, id: taskId, deltaTime: deltaTime}));
|
workers.forEach(it => it.postMessage({action: "listen_pow", user: user, id: taskId, deltaTime: deltaTime}));
|
||||||
addToast("Offline username", "Please wait a minute...");
|
addToast("Offline username", "Please wait a minute...");
|
||||||
});
|
});
|
||||||
$("#listen_continue").append(document.createTextNode(" " + mcIdUsername)).on("click", () => {
|
$("#mcIdUsername").text(mcIdUsername);
|
||||||
|
$("#listen_continue").on("click", () => {
|
||||||
sendSocket(JSON.stringify({
|
sendSocket(JSON.stringify({
|
||||||
"action": "minecraft_id_login",
|
"action": "minecraft_id_login",
|
||||||
"username": mcIdUsername,
|
"username": mcIdUsername,
|
||||||
@ -449,7 +450,7 @@ class MojangAccount extends McAccount {
|
|||||||
headers: {"content-type": "application/json"},
|
headers: {"content-type": "application/json"},
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if (r.status == 403) {
|
if (r.status === 403) {
|
||||||
this.logout();
|
this.logout();
|
||||||
throw "403, token expired?";
|
throw "403, token expired?";
|
||||||
}
|
}
|
||||||
@ -548,6 +549,13 @@ class MicrosoftAccount extends McAccount {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkActive() {
|
||||||
|
return fetch(getCorsProxy() + "https://api.minecraftservices.com/entitlements/mcstore", {
|
||||||
|
method: "get",
|
||||||
|
headers: {"authorization": "Bearer " + this.accessToken}
|
||||||
|
}).then(data => data.ok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function findAccountByMcName(name) {
|
function findAccountByMcName(name) {
|
||||||
@ -727,9 +735,22 @@ function onSocketMsg(event) {
|
|||||||
}
|
}
|
||||||
} else if (parsed.action === "session_hash_request") {
|
} else if (parsed.action === "session_hash_request") {
|
||||||
handleJoinRequest(parsed);
|
handleJoinRequest(parsed);
|
||||||
|
} else if (parsed.action === "parameters_request") {
|
||||||
|
handleParametersRequest(parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleParametersRequest(parsed) {
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
action: "parameters_response",
|
||||||
|
callback: parsed["callback"],
|
||||||
|
version: $("#connect_version").val(),
|
||||||
|
host: $("#connect_address").val(),
|
||||||
|
port: parseInt($("#connect_port").val()) || 25565,
|
||||||
|
frontOnline: $("#connect_online").val()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function listenStoredTokens() {
|
function listenStoredTokens() {
|
||||||
getTokens().forEach(listen);
|
getTokens().forEach(listen);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ self.addEventListener("fetch", evt => {
|
|||||||
if (!shouldCache(evt.request.url)
|
if (!shouldCache(evt.request.url)
|
||||||
|| evt.request.method !== "GET") return;
|
|| evt.request.method !== "GET") return;
|
||||||
evt.respondWith(
|
evt.respondWith(
|
||||||
fromCache(evt.request).catch(() => fromNetwork(evt.request))
|
fromNetwork(evt.request).catch(() => fromCache(evt.request))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user