protocol autodetection, viaaas info in server brand and ping

This commit is contained in:
creeper123123321 2021-02-13 12:08:13 -03:00
parent d3325612a8
commit 33b40e6eea
21 changed files with 416 additions and 41 deletions

View File

@ -8,7 +8,7 @@ How to use: server.example.com._p25565._v1_12_2._ofalse._uBACKUSERNAME.viaaas.ex
Parts:
- ```server.example.com```: backend server address
- ```_p```: backend port
- ```_v```: backend version (protocol id https://wiki.vg/Protocol_version_numbers or name with underline instead of dots). Current default is 1.8, may change to auto detection in the future
- ```_v```: backend version (protocol id https://wiki.vg/Protocol_version_numbers or name with underline instead of dots). ```AUTO``` is default and 1.8 is fallback if it fails.
- ```_o```: ```t``` to force online mode in frontend, ```f``` to disable online mode in frontend. If not set, it will be based on backend online mode.
- ```_u```: username to use in backend connection
- ```viaaas.example.com```: hostname suffix (defined in config)
@ -36,7 +36,7 @@ How to start VIAaaS server:
- ```java -jar VIAaaS-all.jar```
Usage for offline mode:
- Connect to ```mc.example.com._v1_8.viaaas.localhost```
- Connect to ```mc.example.com.viaaas.localhost```
Usage for online mode:
- You can use two accounts (avoids Bad Login error), the same account for front-end and back-end connections or use ```_of```
@ -48,7 +48,7 @@ Usage for online mode:
- Go to VIAaaS auth webpage (https://localhost:25543/), configure the CORS Proxy URL (something like http://localhost:8080/,
note the ending slash) and listen to the username A that you're using to connect to the proxy.
- Add the account B you'll use in ```_u(account B)``` parameter to browser auth page.
- Connect to ```mc.example.com._v1_8._u(account B).viaaas.localhost``` (```_u``` parameter can be removed if you are using the same username)
- Connect to ```mc.example.com._u(account B).viaaas.localhost``` (```_u``` parameter can be removed if you are using the same username)
- Approve the login in auth webpage
- If you use the same online mode account, your client will give Bad Login after you approve it in your browser. You can use
https://www.curseforge.com/minecraft/mc-mods/auth-me for reauthenticating the client.

View File

@ -3,6 +3,7 @@ package com.github.creeper123123321.viaaas
import com.github.creeper123123321.viaaas.config.VIAaaSConfig
import com.google.common.base.Preconditions
import com.google.common.primitives.Ints
import io.netty.buffer.ByteBuf
import io.netty.channel.Channel
import io.netty.handler.codec.DecoderException
import org.slf4j.LoggerFactory
@ -132,4 +133,6 @@ fun writeFlushClose(ch: Channel, obj: Any) {
ch.writeAndFlush(obj).addListener { ch.close() }
}
val secureRandom = if (VIAaaSConfig.useStrongRandom) SecureRandom.getInstanceStrong() else SecureRandom()
val secureRandom = if (VIAaaSConfig.useStrongRandom) SecureRandom.getInstanceStrong() else SecureRandom()
fun readableToByteArray(byteBuf: ByteBuf) = ByteArray(byteBuf.readableBytes()).also { byteBuf.readBytes(it) }

View File

@ -7,6 +7,7 @@ import com.github.creeper123123321.viaaas.handler.FrontEndInit
import com.github.creeper123123321.viaaas.platform.*
import com.github.creeper123123321.viaaas.web.ViaWebApp
import com.github.creeper123123321.viaaas.web.WebDashboardServer
import com.google.gson.JsonParser
import de.gerrygames.viarewind.api.ViaRewindConfigImpl
import io.ktor.application.*
import io.ktor.client.*
@ -36,17 +37,15 @@ import io.netty.util.concurrent.Future
import us.myles.ViaVersion.ViaManager
import us.myles.ViaVersion.api.Via
import us.myles.ViaVersion.api.data.MappingDataLoader
import us.myles.ViaVersion.util.GsonUtil
import us.myles.viaversion.libs.gson.JsonObject
import us.myles.ViaVersion.api.protocol.ProtocolVersion
import java.io.File
import java.net.InetAddress
import java.security.KeyPairGenerator
import java.util.concurrent.CompletableFuture
val viaaasVer = GsonUtil.getGson().fromJson(
AspirinPlatform::class.java.classLoader.getResourceAsStream("viaaas_info.json")!!.reader(Charsets.UTF_8).readText(),
JsonObject::class.java
).get("version").asString
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
val httpClient = HttpClient {
@ -89,6 +88,9 @@ fun channelSocketFactory(): ChannelFactory<SocketChannel> {
return ChannelFactory { NioSocketChannel() }
}
val parentLoop = eventLoopGroup()
val childLoop = eventLoopGroup()
fun main(args: Array<String>) {
// 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) {
@ -109,14 +111,12 @@ fun main(args: Array<String>) {
)
MappingDataLoader.enableMappingsCache()
Via.getManager().init()
ProtocolVersion.register(-2, "AUTO")
AspirinRewind.init(ViaRewindConfigImpl(File("config/viarewind.yml")))
AspirinBackwards.init(File("config/viabackwards"))
val parent = eventLoopGroup()
val child = eventLoopGroup()
val future = ServerBootstrap()
.group(parent, child)
.group(parentLoop, childLoop)
.channelFactory(channelServerSocketFactory())
.childHandler(FrontEndInit)
.childOption(ChannelOption.IP_TOS, 0x18)
@ -138,7 +138,7 @@ fun main(args: Array<String>) {
ktorServer?.stop(1000, 1000)
httpClient.close()
listOf<Future<*>>(future.channel().close(), parent.shutdownGracefully(), child.shutdownGracefully())
listOf<Future<*>>(future.channel().close(), parentLoop.shutdownGracefully(), childLoop.shutdownGracefully())
.forEach { it.sync() }
Via.getManager().destroy()

View File

@ -32,7 +32,6 @@ class VIAaaSAddress {
}.asReversed().joinToString(".")
viaOptions = optionsList.asReversed().joinToString(".")
viaSuffix = viaHostName
return this
@ -58,6 +57,9 @@ class VIAaaSAddress {
protocol = it.version
}
}
if (protocol == -2) {
protocol = null
}
}
part.startsWith("_u", ignoreCase = true) -> {
if (arg.length > 16) throw IllegalArgumentException("Invalid username")

View File

@ -14,7 +14,7 @@ class MinecraftCodec : MessageToMessageCodec<ByteBuf, Packet>() {
val buf = ByteBufAllocator.DEFAULT.buffer()
try {
val handler = ctx.pipeline().get(MinecraftHandler::class.java)
PacketRegistry.encode(msg, buf, handler.data.frontVer!!)
PacketRegistry.encode(msg, buf, handler.data.frontVer!!, serverBound = !handler.frontEnd)
out.add(buf.retain())
} finally {
buf.release()

View File

@ -35,4 +35,6 @@ object VIAaaSConfig : Config(File("config/viaaas.yml")) {
emptyList<String>()
)!!.map { it as String }
val forceOnlineMode: Boolean get() = this.getBoolean("force-online-mode", false)
val showVersionPing: Boolean get() = this.getBoolean("show-version-ping", true)
val showBrandInfo: Boolean get() = this.getBoolean("show-brand-info", true)
}

View File

@ -2,6 +2,7 @@ package com.github.creeper123123321.viaaas.handler
import com.github.creeper123123321.viaaas.codec.FrameCodec
import com.github.creeper123123321.viaaas.codec.MinecraftCodec
import com.github.creeper123123321.viaaas.handler.autoprotocol.ProtocolDetectorHandler
import io.netty.channel.Channel
import io.netty.channel.ChannelInitializer
import io.netty.handler.timeout.ReadTimeoutHandler
@ -19,6 +20,11 @@ class BackEndInit(val connectionData: ConnectionData) : ChannelInitializer<Chann
// compress
.addLast("via-codec", ViaCodec(user))
.addLast("mc", MinecraftCodec())
.also {
if (connectionData.backVer == null) {
it.addLast("protocol-detector", ProtocolDetectorHandler(connectionData))
}
}
.addLast("handler", MinecraftHandler(connectionData, frontEnd = false))
}
}

View File

@ -17,10 +17,6 @@ object FrontEndInit : ChannelInitializer<Channel>() {
// "compress"
.addLast("flow-handler", FlowControlHandler())
.addLast("mc", MinecraftCodec())
.addLast(
"handler", MinecraftHandler(
ConnectionData(frontChannel = ch), frontEnd = true
)
)
.addLast("handler", MinecraftHandler(ConnectionData(frontChannel = ch), frontEnd = true))
}
}

View File

@ -2,8 +2,10 @@ package com.github.creeper123123321.viaaas.handler
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.send
import us.myles.ViaVersion.api.protocol.ProtocolVersion
fun forward(handler: MinecraftHandler, packet: Packet, flush: Boolean = false) {
send(handler.other!!, packet, flush)
}
}
fun is1_7(handler: MinecraftHandler) = handler.data.frontVer!! <= ProtocolVersion.v1_7_6.version

View File

@ -0,0 +1,37 @@
package com.github.creeper123123321.viaaas.handler.autoprotocol
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.handler.state.MinecraftConnectionState
import com.github.creeper123123321.viaaas.mcLogger
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.packet.status.StatusResponse
import com.google.gson.JsonParser
import io.netty.channel.ChannelHandlerContext
import us.myles.ViaVersion.api.protocol.ProtocolVersion
import us.myles.ViaVersion.packets.State
import java.nio.channels.ClosedChannelException
import java.util.concurrent.CompletableFuture
class ProtocolDetectionState(val future: CompletableFuture<ProtocolVersion>) : MinecraftConnectionState {
override val state: State
get() = State.STATUS
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
handler.data.frontChannel.close()
if (packet !is StatusResponse) throw IllegalArgumentException()
val ver = ProtocolVersion.getProtocol(
JsonParser.parseString(packet.json).asJsonObject.getAsJsonObject("version").get("protocol").asInt
)
future.complete(ver)
mcLogger.info("Auto-detected $ver for ${handler.remoteAddress}")
}
override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)
handler.data.frontChannel.close()
}
override fun onInactivated(handler: MinecraftHandler) {
future.completeExceptionally(ClosedChannelException())
}
}

View File

@ -0,0 +1,77 @@
package com.github.creeper123123321.viaaas.handler.autoprotocol
import com.github.creeper123123321.viaaas.childLoop
import com.github.creeper123123321.viaaas.codec.FrameCodec
import com.github.creeper123123321.viaaas.codec.MinecraftCodec
import com.github.creeper123123321.viaaas.handler.ConnectionData
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.packet.handshake.Handshake
import com.github.creeper123123321.viaaas.mcLogger
import com.github.creeper123123321.viaaas.packet.status.StatusRequest
import com.github.creeper123123321.viaaas.send
import java.util.concurrent.CompletableFuture
import java.net.InetSocketAddress
import us.myles.ViaVersion.api.protocol.ProtocolVersion
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.channel.socket.nio.NioSocketChannel
import com.google.common.cache.CacheLoader
import java.util.concurrent.TimeUnit
import com.google.common.cache.CacheBuilder
import io.netty.bootstrap.Bootstrap
import io.netty.channel.*
import io.netty.util.concurrent.Future
import us.myles.ViaVersion.packets.State
import java.util.concurrent.ExecutionException
object ProtocolDetector {
private val SERVER_VER = CacheBuilder.newBuilder()
.expireAfterAccess(100, TimeUnit.SECONDS)
.build(CacheLoader.from { address: InetSocketAddress? ->
val future = CompletableFuture<ProtocolVersion>()
try {
val ch: ChannelFuture = Bootstrap()
.group(childLoop)
.channel(NioSocketChannel::class.java)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.IP_TOS, 0x18)
.handler(object : ChannelInitializer<Channel>() {
override fun initChannel(channel: Channel) {
val data = ConnectionData(channel, state = ProtocolDetectionState(future), frontVer = -1)
channel.pipeline()
.addLast("timeout", ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast("frame", FrameCodec())
.addLast("mc", MinecraftCodec())
.addLast("handler", MinecraftHandler(data, frontEnd = false))
}
})
.connect(address!!)
ch.addListener { future1: Future<in Void> ->
if (!future1.isSuccess) {
future.completeExceptionally(future1.cause())
} else {
ch.channel().eventLoop().execute {
val handshake = Handshake()
handshake.address = address.hostString
handshake.port = address.port
handshake.protocolId = -1
handshake.nextState = State.STATUS
send(ch.channel(), handshake)
send(ch.channel(), StatusRequest(), flush = true)
}
}
}
} catch (throwable: Throwable) {
future.completeExceptionally(throwable)
}
future
})
fun detectVersion(address: InetSocketAddress): CompletableFuture<ProtocolVersion> {
return try {
SERVER_VER[address]
} catch (e: ExecutionException) {
mcLogger.warn("Protocol auto detector error: ", e)
CompletableFuture.completedFuture(null)
}
}
}

View File

@ -0,0 +1,90 @@
package com.github.creeper123123321.viaaas.handler.autoprotocol
import com.github.creeper123123321.viaaas.handler.ConnectionData
import com.github.creeper123123321.viaaas.mcLogger
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelPromise
import java.net.InetSocketAddress
import java.util.concurrent.TimeUnit
import io.netty.channel.ChannelDuplexHandler
import java.lang.Exception
import java.util.function.Consumer
// https://github.com/ViaVersion/ViaFabric/blob/mc-1.16/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java
class ProtocolDetectorHandler(val connectionData: ConnectionData) : ChannelDuplexHandler() {
private val queuedMessages = ArrayDeque<Pair<Any, ChannelPromise>>()
private var hold = true
private var pendentFlush = false
@Throws(Exception::class)
override fun channelActive(ctx: ChannelHandlerContext) {
super.channelActive(ctx)
if (ctx.channel().remoteAddress() is InetSocketAddress) {
val timeoutRun = ctx.executor().schedule({
mcLogger.warn(
"Timeout for protocol auto-detection in "
+ ctx.channel().remoteAddress() + " server"
)
hold = false
drainQueue(ctx)
ctx.pipeline().remove(this)
}, 10, TimeUnit.SECONDS)
ProtocolDetector.detectVersion(ctx.channel().remoteAddress() as InetSocketAddress)
.whenComplete { protocol, _ ->
if (protocol != null && protocol.version != -1) {
connectionData.backVer = protocol.version
} else {
connectionData.backVer = 47 // fallback
}
ctx.pipeline().remove(this)
timeoutRun.cancel(false)
}
// Let's cache it before we need it
}
}
@Throws(Exception::class)
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
if (!hold) {
drainQueue(ctx)
super.write(ctx, msg, promise)
} else {
queuedMessages.add(Pair(msg, promise))
}
}
@Throws(Exception::class)
override fun flush(ctx: ChannelHandlerContext) {
if (!hold) {
drainQueue(ctx)
super.flush(ctx)
} else {
pendentFlush = true
}
}
@Throws(Exception::class)
override fun channelInactive(ctx: ChannelHandlerContext) {
drainQueue(ctx)
super.channelInactive(ctx)
}
private fun drainQueue(ctx: ChannelHandlerContext) {
queuedMessages.forEach(Consumer {
ctx.write(
it.first,
it.second
)
})
queuedMessages.clear()
if (pendentFlush) ctx.flush()
pendentFlush = false
}
@Throws(Exception::class)
override fun handlerRemoved(ctx: ChannelHandlerContext) {
drainQueue(ctx)
super.handlerRemoved(ctx)
}
}

View File

@ -45,7 +45,7 @@ class HandshakeState : MinecraftConnectionState {
}
val parsed = VIAaaSAddress().parse(packet.address.substringBefore(0.toChar()), VIAaaSConfig.hostName)
val backProto = parsed.protocol ?: 47 // todo autodetection
val backProto = parsed.protocol
val hadHostname = parsed.viaSuffix != null
packet.address = parsed.serverAddress!!
@ -61,7 +61,7 @@ class HandshakeState : MinecraftConnectionState {
handler.data.backName = parsed.username
val playerAddr = handler.data.frontHandler.remoteAddress
mcLogger.info("Connecting $playerAddr (${handler.data.frontVer}) -> ${packet.address}:${packet.port} ($backProto)")
mcLogger.info("Connecting ${handler.data.state.state} $playerAddr (${handler.data.frontVer}) -> ${packet.address}:${packet.port} ($backProto)")
if (!hadHostname && VIAaaSConfig.requireHostName) {
throw UnsupportedOperationException("This VIAaaS instance requires you to use the hostname")

View File

@ -1,10 +1,18 @@
package com.github.creeper123123321.viaaas.handler.state
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.packet.UnknownPacket
import com.github.creeper123123321.viaaas.config.VIAaaSConfig
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.handler.forward
import com.github.creeper123123321.viaaas.handler.is1_7
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.packet.UnknownPacket
import com.github.creeper123123321.viaaas.packet.play.PluginMessage
import com.github.creeper123123321.viaaas.readableToByteArray
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import us.myles.ViaVersion.api.protocol.ProtocolVersion
import us.myles.ViaVersion.api.type.Type
import us.myles.ViaVersion.packets.State
object PlayState : MinecraftConnectionState {
@ -12,10 +20,42 @@ object PlayState : MinecraftConnectionState {
get() = State.PLAY
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
if ((packet as UnknownPacket).id !in 0..127) throw IllegalArgumentException("Invalid packet id!")
when {
packet is UnknownPacket && (packet.id !in 0..127) -> throw IllegalArgumentException("Invalid packet id!")
packet is PluginMessage -> modifyPluginMessage(handler, packet)
}
forward(handler, packet)
}
private fun modifyPluginMessage(handler: MinecraftHandler, pluginMessage: PluginMessage) {
when (pluginMessage.channel) {
"MC|Brand", "brand", "minecraft:brand" -> {
if (!VIAaaSConfig.showBrandInfo) return
val brand = if (is1_7(handler)) {
String(pluginMessage.data, Charsets.UTF_8)
} else {
Type.STRING.read(Unpooled.wrappedBuffer(pluginMessage.data))
} + " (VIAaaS C: ${ProtocolVersion.getProtocol(handler.data.frontVer!!)} S: ${
ProtocolVersion.getProtocol(
handler.data.backVer!!
)
})"
if (is1_7(handler)) {
pluginMessage.data = brand.toByteArray(Charsets.UTF_8)
} else {
val buf = ByteBufAllocator.DEFAULT.buffer()
try {
Type.STRING.write(buf, brand)
pluginMessage.data = readableToByteArray(buf)
} finally {
buf.release()
}
}
}
}
}
override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)
handler.data.frontChannel.close()

View File

@ -1,14 +1,20 @@
package com.github.creeper123123321.viaaas.handler.state
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.packet.status.StatusResponse
import com.github.creeper123123321.viaaas.packet.UnknownPacket
import com.github.creeper123123321.viaaas.config.VIAaaSConfig
import com.github.creeper123123321.viaaas.handler.MinecraftHandler
import com.github.creeper123123321.viaaas.handler.forward
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.packet.UnknownPacket
import com.github.creeper123123321.viaaas.packet.status.StatusResponse
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.protocol.ProtocolVersion
import us.myles.ViaVersion.packets.State
import java.util.*
object StatusState : MinecraftConnectionState {
override val state: State
@ -16,9 +22,31 @@ object StatusState : MinecraftConnectionState {
override fun handlePacket(handler: MinecraftHandler, ctx: ChannelHandlerContext, packet: Packet) {
if (packet is UnknownPacket) throw IllegalArgumentException("Invalid packet")
when (packet) {
is StatusResponse -> modifyResponse(handler, packet)
}
forward(handler, packet)
}
private fun modifyResponse(handler: MinecraftHandler, packet: StatusResponse) {
if (VIAaaSConfig.showVersionPing) {
val parsed = JsonParser.parseString(packet.json).asJsonObject
val players = parsed.getAsJsonObject("players") ?: JsonObject().also { parsed.add("players", it) }
val sample = players.getAsJsonArray("sample") ?: JsonArray().also { players.add("sample", it) }
sample.add(JsonObject().also {
it.addProperty("id", UUID.nameUUIDFromBytes("VIAaaS".toByteArray(Charsets.UTF_8)).toString())
it.addProperty(
"name",
"§9VIAaaS§r (C: §7${ProtocolVersion.getProtocol(handler.data.frontVer!!)}§r S: §7${
ProtocolVersion.getProtocol(handler.data.backVer!!)
}§r)"
)
})
packet.json = parsed.toString()
}
}
override fun disconnect(handler: MinecraftHandler, msg: String) {
super.disconnect(handler, msg)

View File

@ -2,6 +2,7 @@ package com.github.creeper123123321.viaaas.packet
import com.github.creeper123123321.viaaas.packet.handshake.Handshake
import com.github.creeper123123321.viaaas.packet.login.*
import com.github.creeper123123321.viaaas.packet.play.PluginMessage
import com.github.creeper123123321.viaaas.packet.status.StatusPing
import com.github.creeper123123321.viaaas.packet.status.StatusPong
import com.github.creeper123123321.viaaas.packet.status.StatusRequest
@ -17,6 +18,7 @@ object PacketRegistry {
val entries = mutableListOf<RegistryEntry>()
init {
// Obviosly stolen from https://github.com/VelocityPowered/Velocity/blob/dev/1.1.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java
register(Range.all(), State.HANDSHAKE, 0, true, ::Handshake)
register(Range.all(), State.LOGIN, 0, true, ::LoginStart)
register(Range.all(), State.LOGIN, 1, true, ::CryptoResponse)
@ -25,11 +27,33 @@ object PacketRegistry {
register(Range.all(), State.LOGIN, 1, false, ::CryptoRequest)
register(Range.all(), State.LOGIN, 2, false, ::LoginSuccess)
register(Range.all(), State.LOGIN, 3, false, ::SetCompression)
register(Range.all(), State.LOGIN, 4, false, ::PluginRequest)
register(Range.atLeast(ProtocolVersion.v1_13.version), State.LOGIN, 4, false, ::PluginRequest)
register(Range.all(), State.STATUS, 0, true, ::StatusRequest)
register(Range.all(), State.STATUS, 1, true, ::StatusPing)
register(Range.all(), State.STATUS, 0, false, ::StatusResponse)
register(Range.all(), State.STATUS, 1, false, ::StatusPong)
register(
::PluginMessage, State.PLAY, true, mapOf(
Range.closed(ProtocolVersion.v1_7_1.version, ProtocolVersion.v1_8.version) to 0x17,
Range.closed(ProtocolVersion.v1_9.version, ProtocolVersion.v1_11_1.version) to 0x09,
Range.singleton(ProtocolVersion.v1_12.version) to 0x0A,
Range.closed(ProtocolVersion.v1_12_1.version, ProtocolVersion.v1_12_2.version) to 0x09,
Range.closed(ProtocolVersion.v1_13.version, ProtocolVersion.v1_13_2.version) to 0x0A,
Range.closed(ProtocolVersion.v1_14.version, ProtocolVersion.v1_16_4.version) to 0x0B
)
)
register(
::PluginMessage, State.PLAY, false,
mapOf(
Range.closed(ProtocolVersion.v1_7_1.version, ProtocolVersion.v1_8.version) to 0x3F,
Range.closed(ProtocolVersion.v1_9.version, ProtocolVersion.v1_12_2.version) to 0x18,
Range.closed(ProtocolVersion.v1_13.version, ProtocolVersion.v1_13_2.version) to 0x19,
Range.closed(ProtocolVersion.v1_14.version, ProtocolVersion.v1_14_4.version) to 0x18,
Range.closed(ProtocolVersion.v1_15.version, ProtocolVersion.v1_15_2.version) to 0x19,
Range.closed(ProtocolVersion.v1_16.version, ProtocolVersion.v1_16_1.version) to 0x18,
Range.closed(ProtocolVersion.v1_16_2.version, ProtocolVersion.v1_16_4.version) to 0x17
)
)
}
inline fun <reified P : Packet> register(
@ -42,6 +66,15 @@ object PacketRegistry {
entries.add(RegistryEntry(protocol, state, id, serverBound, constructor, P::class.java))
}
inline fun <reified P : Packet> register(
constructor: Supplier<P>,
state: State,
serverBound: Boolean,
idByProtocol: Map<Range<Int>, Int>
) {
idByProtocol.forEach { (protocol, id) -> register(protocol, state, id, serverBound, constructor) }
}
data class RegistryEntry(
val versionRange: Range<Int>,
val state: State,
@ -63,9 +96,9 @@ object PacketRegistry {
}?.constructor
}
fun getPacketId(packetClass: Class<out Packet>, protocolVersion: Int): Int? {
fun getPacketId(packetClass: Class<out Packet>, protocolVersion: Int, serverBound: Boolean): Int? {
return entries.firstOrNull {
it.versionRange.contains(protocolVersion) && it.packetClass == packetClass
it.versionRange.contains(protocolVersion) && it.packetClass == packetClass && it.serverBound == serverBound
}?.id
}
@ -78,11 +111,11 @@ object PacketRegistry {
return packet
}
fun encode(packet: Packet, byteBuf: ByteBuf, protocolVersion: Int) {
fun encode(packet: Packet, byteBuf: ByteBuf, protocolVersion: Int, serverBound: Boolean) {
val id = if (packet is UnknownPacket) {
packet.id
} else {
getPacketId(packet.javaClass, protocolVersion)!!
getPacketId(packet.javaClass, protocolVersion, serverBound)!!
}
Type.VAR_INT.writePrimitive(byteBuf, id)
packet.encode(byteBuf, protocolVersion)

View File

@ -1,12 +1,13 @@
package com.github.creeper123123321.viaaas.packet
import com.github.creeper123123321.viaaas.readableToByteArray
import io.netty.buffer.ByteBuf
class UnknownPacket(val id: Int) : Packet {
lateinit var content: ByteArray
override fun decode(byteBuf: ByteBuf, protocolVersion: Int) {
content = ByteArray(byteBuf.readableBytes()).also { byteBuf.readBytes(it) }
content = readableToByteArray(byteBuf)
}
override fun encode(byteBuf: ByteBuf, protocolVersion: Int) {

View File

@ -1,6 +1,7 @@
package com.github.creeper123123321.viaaas.packet.login
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.readableToByteArray
import io.netty.buffer.ByteBuf
import us.myles.ViaVersion.api.type.Type
import kotlin.properties.Delegates
@ -12,7 +13,7 @@ class PluginRequest : Packet {
override fun decode(byteBuf: ByteBuf, protocolVersion: Int) {
id = Type.VAR_INT.readPrimitive(byteBuf)
channel = Type.STRING.read(byteBuf)
data = ByteArray(byteBuf.readableBytes()).also { byteBuf.readBytes(it) }
data = readableToByteArray(byteBuf)
}
override fun encode(byteBuf: ByteBuf, protocolVersion: Int) {

View File

@ -1,6 +1,7 @@
package com.github.creeper123123321.viaaas.packet.login
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.readableToByteArray
import io.netty.buffer.ByteBuf
import us.myles.ViaVersion.api.type.Type
import kotlin.properties.Delegates
@ -13,7 +14,7 @@ class PluginResponse : Packet {
id = Type.VAR_INT.readPrimitive(byteBuf)
success = byteBuf.readBoolean()
if (success) {
data = ByteArray(byteBuf.readableBytes()).also { byteBuf.readBytes(it) }
data = readableToByteArray(byteBuf)
}
}

View File

@ -0,0 +1,52 @@
package com.github.creeper123123321.viaaas.packet.play
import com.github.creeper123123321.viaaas.packet.Packet
import com.github.creeper123123321.viaaas.readableToByteArray
import io.netty.buffer.ByteBuf
import us.myles.ViaVersion.api.protocol.ProtocolVersion
import us.myles.ViaVersion.api.type.Type
class PluginMessage : Packet {
lateinit var channel: String
lateinit var data: ByteArray
override fun decode(byteBuf: ByteBuf, protocolVersion: Int) {
channel = Type.STRING.read(byteBuf)
data = if (protocolVersion <= ProtocolVersion.v1_7_6.version) {
ByteArray(readExtendedForgeShort(byteBuf)).also { byteBuf.readBytes(it) }
} else {
readableToByteArray(byteBuf)
}
}
override fun encode(byteBuf: ByteBuf, protocolVersion: Int) {
Type.STRING.write(byteBuf, channel)
if (protocolVersion <= ProtocolVersion.v1_7_6.version) {
writeExtendedForgeShort(byteBuf, data.size)
}
byteBuf.writeBytes(data)
}
// stolen from https://github.com/VelocityPowered/Velocity/blob/27ccb9d387fc9a0aecd5c4b570d7d957558efddc/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java#L418
fun readExtendedForgeShort(buf: ByteBuf): Int {
var low = buf.readUnsignedShort()
var high = 0
if (low and 0x8000 != 0) {
low = low and 0x7FFF
high = buf.readUnsignedByte().toInt()
}
return high and 0xFF shl 15 or low
}
fun writeExtendedForgeShort(buf: ByteBuf, toWrite: Int) {
var low = toWrite and 0x7FFF
val high = toWrite and 0x7F8000 shr 15
if (high != 0) {
low = low or 0x8000
}
buf.writeShort(low)
if (high != 0) {
buf.writeByte(high)
}
}
}

View File

@ -27,4 +27,8 @@ blocked-back-addresses: ["*.hypixel.net", "hypixel.net"]
# VIAaaS will only allow if it matches an address in this list
allowed-back-addresses: ["*"]
# Requires online mode for front-end connections. May be useful for stopping bots.
force-online-mode: false
force-online-mode: false
# Shows player and server version in player list
show-version-ping: true
# Shows info in server brand (F3)
show-brand-info: true