Rename MinecraftDataTypes and add buggy NBT implementation
This commit is contained in:
parent
edc93f7a80
commit
4ec62d5d44
26 changed files with 284 additions and 31 deletions
39
blokk-nbt/build.gradle.kts
Normal file
39
blokk-nbt/build.gradle.kts
Normal 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()
|
||||
}
|
||||
}
|
32
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt
Normal file
32
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
39
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBTList.kt
Normal file
39
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBTList.kt
Normal 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) }
|
||||
}
|
||||
}
|
||||
}
|
140
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBTType.kt
Normal file
140
blokk-nbt/src/main/kotlin/space/blokk/nbt/NBTType.kt
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<*>>() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
Reference in a new issue