Archived
1
0
Fork 0

Rename MinecraftDataTypes and add buggy NBT implementation

This commit is contained in:
Moritz Ruth 2020-10-10 23:13:20 +02:00
parent edc93f7a80
commit 4ec62d5d44
26 changed files with 284 additions and 31 deletions

View file

@ -0,0 +1,39 @@
plugins {
kotlin("jvm")
}
group = rootProject.group
version = rootProject.version
repositories {
mavenCentral()
jcenter()
}
val nettyVersion = properties["version.netty"].toString()
val junitVersion = properties["version.junit"].toString()
val striktVersion = properties["version.strikt"].toString()
dependencies {
// Netty
api("io.netty:netty-buffer:${nettyVersion}")
// Testing
testImplementation("io.strikt:strikt-core:${striktVersion}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
test {
useJUnitPlatform()
}
}

View file

@ -0,0 +1,32 @@
package space.blokk.nbt
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
class NBT internal constructor(private val map: MutableMap<String, Any>) {
constructor() : this(mutableMapOf())
val data get() = map.toMap()
inline fun <reified T> get(name: String) = data[name] as T
fun set(name: String, value: Any) {
NBTType.getFor(value) ?: throw IllegalArgumentException("The type of value cannot be represented as NBT")
map[name] = value
}
fun write(destination: DataOutputStream, name: String) {
NBTCompound.writeNamedTag(destination, name, this)
}
fun toSNBT() = NBTCompound.toSNBT(this)
override fun toString() = toSNBT()
companion object {
fun read(source: DataInputStream): Pair<String, NBT> {
if (source.readByte() != NBTCompound.id) throw IOException("The NBT data in source is not a compound tag")
return Pair(source.readUTF(), NBTCompound.readValue(source))
}
}
}

View file

@ -0,0 +1,39 @@
package space.blokk.nbt
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
object NBTList : NBTType<List<*>>(List::class, 9) {
override fun toSNBT(value: List<*>): String = "[${value.joinToString(",")}]"
override fun readValue(source: DataInputStream): List<Any> {
val typeID = source.readByte()
val length = source.readInt()
if (length == 0) return emptyList()
val type = getFor(typeID)
?: throw IOException("The NBT data contains an unknown type ID: $typeID")
return (0..length).map { type.readValue(source) }
}
override fun writeValue(destination: DataOutputStream, value: List<*>) {
@Suppress("UNCHECKED_CAST")
value as List<Any>
if (value.isEmpty()) {
destination.writeByte(END_TAG_ID)
destination.writeByte(0)
} else {
val type = getFor(value[0])
?: throw IllegalArgumentException("The type of value cannot be represented as NBT")
destination.writeByte(type.id.toInt())
destination.writeInt(value.size)
value.forEach { type.writeValue(destination, it) }
}
}
}

View file

@ -0,0 +1,140 @@
package space.blokk.nbt
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
import kotlin.reflect.KClass
abstract class NBTType<T : Any> internal constructor(val kClass: KClass<*>, val id: Byte) {
abstract fun toSNBT(value: T): String
abstract fun readValue(source: DataInputStream): T
abstract fun writeValue(destination: DataOutputStream, value: T)
fun writeIDAndValue(destination: DataOutputStream, value: T) {
destination.writeByte(id.toInt())
writeValue(destination, value)
}
fun writeNamedTag(destination: DataOutputStream, name: String, value: T) {
destination.writeByte(id.toInt())
destination.writeUTF(name)
writeValue(destination, value)
}
companion object {
const val END_TAG_ID = 0
val ALL by lazy {
setOf<NBTType<*>>(
NBTByte,
NBTShort,
NBTInt,
NBTLong,
NBTFloat,
NBTDouble,
NBTByteArray,
NBTString,
NBTList,
NBTCompound
)
}
private val BY_ID by lazy { ALL.map { it.id to it }.toMap() }
fun byID(id: Byte): NBTType<*>? = BY_ID[id]
@Suppress("UNCHECKED_CAST")
fun <T : Any> getFor(value: T): NBTType<T>? = ALL.find { it.kClass.isInstance(value) } as NBTType<T>?
}
}
object NBTByte : NBTType<Byte>(Byte::class, 1) {
override fun toSNBT(value: Byte): String = "${value}b"
override fun readValue(source: DataInputStream): Byte = source.readByte()
override fun writeValue(destination: DataOutputStream, value: Byte) = destination.writeByte(value.toInt())
}
object NBTShort : NBTType<Short>(Short::class, 2) {
override fun toSNBT(value: Short): String = "${value}s"
override fun readValue(source: DataInputStream): Short = source.readShort()
override fun writeValue(destination: DataOutputStream, value: Short) = destination.writeShort(value.toInt())
}
object NBTInt : NBTType<Int>(Integer::class, 3) {
override fun toSNBT(value: Int): String = "$value"
override fun readValue(source: DataInputStream): Int = source.readInt()
override fun writeValue(destination: DataOutputStream, value: Int) = destination.writeInt(value)
}
object NBTLong : NBTType<Long>(Long::class, 4) {
override fun toSNBT(value: Long): String = "${value}L"
override fun readValue(source: DataInputStream): Long = source.readLong()
override fun writeValue(destination: DataOutputStream, value: Long) = destination.writeLong(value)
}
object NBTFloat : NBTType<Float>(Float::class, 5) {
override fun toSNBT(value: Float): String = "${value}f"
override fun readValue(source: DataInputStream): Float = source.readFloat()
override fun writeValue(destination: DataOutputStream, value: Float) = destination.writeFloat(value)
}
object NBTDouble : NBTType<Double>(Double::class, 6) {
override fun toSNBT(value: Double): String = "$value"
override fun readValue(source: DataInputStream): Double = source.readDouble()
override fun writeValue(destination: DataOutputStream, value: Double) = destination.writeDouble(value)
}
object NBTByteArray : NBTType<ByteArray>(ByteArray::class, 7) {
override fun toSNBT(value: ByteArray): String = "[B;${value.joinToString(",")}]"
override fun readValue(source: DataInputStream): ByteArray {
val length = source.readInt()
val array = ByteArray(length)
source.read(array, 0, length)
return array
}
override fun writeValue(destination: DataOutputStream, value: ByteArray) {
destination.writeInt(value.size)
destination.write(value)
}
}
object NBTString : NBTType<String>(String::class, 8) {
override fun toSNBT(value: String): String = "\"${value.replace("\"", "\\\"")}\""
override fun readValue(source: DataInputStream): String = source.readUTF()
override fun writeValue(destination: DataOutputStream, value: String) = destination.writeUTF(value)
}
object NBTCompound : NBTType<NBT>(NBT::class, 10) {
override fun toSNBT(value: NBT): String =
"{${
value.data.entries.joinToString(",") { (name, v) ->
"$name: ${getFor(v)!!.toSNBT(v)}"
}
}}"
override fun readValue(source: DataInputStream): NBT {
val data = mutableMapOf<String, Any>()
while (true) {
val typeID = source.readByte()
if (typeID.toInt() == END_TAG_ID) break
println(typeID)
val type = byID(typeID) ?: throw IOException("The NBT data contains an unknown type ID: $typeID")
val name = source.readUTF()
data[name] = type.readValue(source)
}
return NBT(data)
}
override fun writeValue(destination: DataOutputStream, value: NBT) {
value.data.forEach { (name, v) ->
getFor(v)!!.writeNamedTag(destination, name, v)
}
destination.writeByte(END_TAG_ID)
}
}

View file

@ -9,11 +9,11 @@ import kotlin.experimental.and
*
* You can access them using the [with] function (`with(MinecraftDataTypes) { ... }`).
*/
object MinecraftDataTypes {
object MinecraftProtocolDataTypes {
/**
* Gets a variable length integer at the current readerIndex and increases the readerIndex by up to 5 in this buffer.
*
* @throws IOException if the VarInt is longer than the maximum of 5 bytes
* @throws IOException If the VarInt is longer than the maximum of 5 bytes
* @see <a href="https://wiki.vg/Protocol#VarInt_and_VarLong">https://wiki.vg/Protocol#VarInt_and_VarLong</a>
*/
fun ByteBuf.readVarInt(): Int {

View file

@ -1,8 +1,8 @@
package space.blokk.net.packet.handshaking
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readString
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readString
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.packet.IncomingPacketCodec
object HandshakePacketCodec : IncomingPacketCodec<HandshakePacket>(0x00, HandshakePacket::class) {

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.packet.OutgoingPacketCodec
object DisconnectPacketCodec : OutgoingPacketCodec<DisconnectPacket>(0x00, DisconnectPacket::class) {

View file

@ -1,8 +1,8 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
object EncryptionRequestPacketCodec :

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.packet.IncomingPacketCodec
object EncryptionResponsePacketCodec :

View file

@ -1,8 +1,8 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
object LoginPluginRequestPacketCodec :

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.packet.IncomingPacketCodec
object LoginPluginResponsePacketCodec :

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readString
import space.blokk.net.MinecraftProtocolDataTypes.readString
import space.blokk.net.packet.IncomingPacketCodec
object LoginStartPacketCodec : IncomingPacketCodec<LoginStartPacket>(0x00, LoginStartPacket::class) {

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.packet.OutgoingPacketCodec
object LoginSuccessPacketCodec : OutgoingPacketCodec<LoginSuccessPacket>(0x02, LoginSuccessPacket::class) {

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.login
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
object SetCompressionPacketCodec : OutgoingPacketCodec<SetCompressionPacket>(0x03, SetCompressionPacket::class) {

View file

@ -1,8 +1,8 @@
package space.blokk.net.packet.play
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readString
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readString
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.PacketDecodingException
import space.blokk.net.packet.IncomingPacketCodec
import space.blokk.player.ChatMode

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.play
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.readString
import space.blokk.net.MinecraftProtocolDataTypes.readString
import space.blokk.net.packet.IncomingPacketCodec
object IncomingPluginMessagePacketCodec :

View file

@ -1,8 +1,8 @@
package space.blokk.net.packet.play
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
import space.blokk.player.GameMode
import space.blokk.world.WorldDimension

View file

@ -1,7 +1,7 @@
package space.blokk.net.packet.play
import io.netty.buffer.ByteBuf
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.packet.OutgoingPacketCodec
object OutgoingPluginMessagePacketCodec :

View file

@ -2,7 +2,7 @@ package space.blokk.net.packet.status
import io.netty.buffer.ByteBuf
import space.blokk.Blokk
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.packet.OutgoingPacketCodec
import space.blokk.util.toJson

View file

@ -26,6 +26,7 @@ dependencies {
implementation(project(":blokk-api"))
implementation(project(":blokk-packets"))
implementation(project(":blokk-packet-codecs"))
implementation(project(":blokk-nbt"))
// Logging
implementation("org.slf4j:slf4j-api:${slf4jVersion}")
@ -59,6 +60,7 @@ tasks {
shadowJar {
archiveClassifier.set(null as String?)
@Suppress("UnstableApiUsage")
manifest {
this.attributes("Main-Class" to "space.blokk.BlokkServer")
this.attributes("Implementation-Version" to project.version)

View file

@ -44,7 +44,7 @@ class BlokkServer internal constructor() : Server {
init {
var dir = File(BlokkServer::class.java.protectionDomain.codeSource.location.toURI())
if (VERSION == "development") dir = dir.resolve("../../../../../data").normalize()
if (VERSION == "development") dir = dir.resolve("../../../../../data").normalize().also { it.mkdirs() }
serverDirectory = dir
}

View file

@ -3,9 +3,9 @@ package space.blokk.net
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageCodec
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftDataTypes.varIntReadable
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.varIntReadable
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
class FramingCodec : ByteToMessageCodec<ByteBuf>() {
private var currentLength: Int? = null

View file

@ -4,7 +4,7 @@ import io.netty.buffer.Unpooled
import space.blokk.Blokk
import space.blokk.BlokkServer
import space.blokk.chat.TextComponent
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.packet.login.EncryptionRequestPacket
import space.blokk.net.packet.login.EncryptionResponsePacket
import space.blokk.net.packet.login.LoginStartPacket

View file

@ -3,8 +3,8 @@ package space.blokk.net
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.MessageToMessageCodec
import space.blokk.net.MinecraftDataTypes.readVarInt
import space.blokk.net.MinecraftDataTypes.writeVarInt
import space.blokk.net.MinecraftProtocolDataTypes.readVarInt
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import java.io.IOException
class PacketMessageCodec(private val session: BlokkSession) : MessageToMessageCodec<ByteBuf, PacketMessage<*>>() {

View file

@ -4,7 +4,7 @@ import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import space.blokk.event.ifNotCancelled
import space.blokk.net.BlokkSession
import space.blokk.net.MinecraftDataTypes.readString
import space.blokk.net.MinecraftProtocolDataTypes.readString
import space.blokk.net.PacketReceivedEventHandler
import space.blokk.net.event.ClientBrandReceivedEvent

View file

@ -8,6 +8,7 @@ pluginManagement {
rootProject.name = "blokk"
include(":blokk-api")
include(":blokk-server")
include(":blokk-packets")
include(":blokk-nbt")
include(":blokk-packet-codecs")
include(":blokk-packets")
include(":blokk-server")