diff --git a/.runConfigurations/Build TestPlugin.jar.run.xml b/.runConfigurations/Build TestPlugin.jar.run.xml
new file mode 100644
index 0000000..b2197ec
--- /dev/null
+++ b/.runConfigurations/Build TestPlugin.jar.run.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/.runConfigurations/Run.run.xml b/.runConfigurations/Run.run.xml
index 46d06a7..bb0a744 100644
--- a/.runConfigurations/Run.run.xml
+++ b/.runConfigurations/Run.run.xml
@@ -10,6 +10,7 @@
+
\ No newline at end of file
diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt
index 4ebef1d..56c6ba2 100644
--- a/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt
+++ b/blokk-api/src/main/kotlin/space/blokk/world/block/BlockCodec.kt
@@ -32,6 +32,8 @@ class BlockCodec internal constructor(
}
})
+ val lastStateID get() = firstStateID + states.size
+
class IllegalAttributeTargetType : Exception("The type of the target property is not allowed for attributes")
fun getStateID(block: T): Int {
diff --git a/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt b/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt
index 9a4ff1a..231d565 100644
--- a/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt
+++ b/blokk-api/src/main/kotlin/space/blokk/world/block/Material.kt
@@ -14,6 +14,9 @@ interface Material {
val collisionShape: CollisionShape
companion object {
+ /**
+ * All materials, sorted by their numeric ID in ascending order.
+ */
val all = GENERATED_BLOCKS
val byID = GENERATED_BLOCKS.map { it.id to it }.toMap()
diff --git a/blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt b/blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt
index 6f890dd..8fe6f41 100644
--- a/blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt
+++ b/blokk-nbt/src/main/kotlin/space/blokk/nbt/NBT.kt
@@ -32,8 +32,9 @@ class NBT internal constructor(private val map: MutableMap) {
map[name] = value
}
- fun write(destination: DataOutputStream, name: String) {
- NBTCompound.writeNamedTag(destination, name, this)
+ fun write(destination: DataOutputStream, name: String? = null) {
+ if (name == null) NBTCompound.writeValue(destination, this)
+ else NBTCompound.writeNamedTag(destination, name, this)
}
fun toSNBT(pretty: Boolean) = NBTCompound.toSNBT(this, pretty)
diff --git a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt
index 53469ca..3b53066 100644
--- a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt
+++ b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/ChunkDataPacketCodec.kt
@@ -1,6 +1,8 @@
package space.blokk.net.packet.play
import io.netty.buffer.ByteBuf
+import io.netty.buffer.ByteBufOutputStream
+import io.netty.buffer.Unpooled
import space.blokk.nbt.NBT
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
@@ -9,19 +11,23 @@ import space.blokk.util.toCompactLongArray
import space.blokk.world.block.Air
import space.blokk.world.block.CaveAir
import space.blokk.world.block.Material
+import space.blokk.world.block.material
+import java.io.DataOutputStream
+import kotlin.math.ceil
+import kotlin.math.log
object ChunkDataPacketCodec : OutgoingPacketCodec(0x22, ChunkDataPacket::class) {
- private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class)
- private val solidHeightmapIgnoredBlocks = Material.all.filter {
- // TODO
- true
- }
+ private val airBlocks: Set> = setOf(Air, CaveAir)
+ private val nonSolidBlocks = Material.all.filter { it.collisionShape.isEmpty() }
+
+ private val numberOfBitsPerBlock = ceil(log(Material.all.last().codec.lastStateID.toDouble(), 2.0)).toInt()
override fun ChunkDataPacket.encode(dst: ByteBuf) {
dst.writeInt(key.x)
dst.writeInt(key.z)
dst.writeBoolean(true) // Full Chunk
+ // Bitmask
var includedSections = 0
data.sections.forEachIndexed { index, value ->
if (value != null) includedSections = includedSections and (1 shl index)
@@ -29,10 +35,46 @@ object ChunkDataPacketCodec : OutgoingPacketCodec(0x22, ChunkDa
dst.writeVarInt(includedSections)
- val heightmap = data.solidBlocksHeightmap ?: generateHeightmap(data.sections, listOf())
-
+ // Heightmaps
val heightmaps = NBT()
- heightmaps.set("MOTION_BLOCKING", heightmap.toCompactLongArray(9))
- heightmaps.set("MOTION_BLOCKING", heightmap.toCompactLongArray(9))
+ heightmaps.set(
+ "MOTION_BLOCKING",
+ (data.solidBlocksHeightmap ?: generateHeightmap(
+ data.sections,
+ nonSolidBlocks
+ )).toCompactLongArray(9)
+ )
+
+ heightmaps.set(
+ "WORLD_SURFACE",
+ (data.nonAirBlocksHeightmap ?: generateHeightmap(
+ data.sections,
+ airBlocks
+ )).toCompactLongArray(9)
+ )
+
+ DataOutputStream(ByteBufOutputStream(dst)).use { heightmaps.write(it) }
+
+ // Biomes
+ data.biomes.forEach { dst.writeInt(it.numericID) }
+
+ // Blocks
+ val dataBuf = Unpooled.buffer() // TODO: Set an initial capacity
+ for (section in data.sections.filterNotNull()) {
+ val ids = section.map { it.material().codec.getStateID(it) }.toIntArray()
+ dataBuf.writeShort(ids.count { !(it == Air.codec.id || it == CaveAir.codec.id) })
+ dataBuf.writeByte(numberOfBitsPerBlock)
+
+ val array = ids.toCompactLongArray(numberOfBitsPerBlock)
+ dataBuf.writeVarInt(array.size)
+ array.forEach { dataBuf.writeLong(it) }
+ }
+
+ dst.writeVarInt(dataBuf.readableBytes())
+ dst.writeBytes(dataBuf)
+ dataBuf.release()
+
+ // Block entities
+ dst.writeVarInt(0)
}
}
diff --git a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/PlayProtocol.kt b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/PlayProtocol.kt
index a785975..331b30f 100644
--- a/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/PlayProtocol.kt
+++ b/blokk-packet-codecs/src/main/kotlin/space/blokk/net/packet/play/PlayProtocol.kt
@@ -5,6 +5,7 @@ import space.blokk.net.packet.Protocol
object PlayProtocol : Protocol(
"PLAY",
ClientSettingsPacketCodec,
+ ChunkDataPacketCodec,
DeclareRecipesPacketCodec,
DisconnectPacketCodec,
IncomingPluginMessagePacketCodec,
diff --git a/blokk-server/build.gradle.kts b/blokk-server/build.gradle.kts
index 3953e91..34a5df3 100644
--- a/blokk-server/build.gradle.kts
+++ b/blokk-server/build.gradle.kts
@@ -61,8 +61,10 @@ tasks {
@Suppress("UnstableApiUsage")
manifest {
- this.attributes("Main-Class" to "space.blokk.BlokkServer")
- this.attributes("Implementation-Version" to project.version)
+ this.attributes(
+ "Main-Class" to "space.blokk.BlokkServer",
+ "Implementation-Version" to project.version
+ )
}
}
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 1c62c9f..2022560 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -12,3 +12,4 @@ include(":blokk-nbt")
include(":blokk-packet-codecs")
include(":blokk-packets")
include(":blokk-server")
+include("test-plugin")
diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts
new file mode 100644
index 0000000..2754e16
--- /dev/null
+++ b/test-plugin/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ kotlin("jvm")
+ id("com.github.johnrengelman.shadow") version "6.1.0"
+}
+
+group = "space.blokk.testplugin"
+version = "0.0.1-SNAPSHOT"
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation(project(":blokk-api"))
+}
+
+tasks {
+ shadowJar {
+ archiveFileName.set("TestPlugin.jar")
+ destinationDirectory.set(file("../serverData/plugins"))
+ }
+}
diff --git a/test-plugin/src/main/kotlin/space/blokk/testplugin/TestPlugin.kt b/test-plugin/src/main/kotlin/space/blokk/testplugin/TestPlugin.kt
new file mode 100644
index 0000000..679b907
--- /dev/null
+++ b/test-plugin/src/main/kotlin/space/blokk/testplugin/TestPlugin.kt
@@ -0,0 +1,25 @@
+package space.blokk.testplugin
+
+import space.blokk.Blokk
+import space.blokk.event.EventHandler
+import space.blokk.event.Listener
+import space.blokk.net.event.SessionAfterLoginEvent
+import space.blokk.plugin.Plugin
+import space.blokk.world.*
+import space.blokk.testplugin.anvil.AnvilWorld
+import space.blokk.world.block.GrassBlock
+
+class TestPlugin: Plugin("Test", "1.0.0") {
+ override fun onEnable() {
+ val world = AnvilWorld(WorldDimension.OVERWORLD, WorldType.FLAT)
+ world.getVoxel(VoxelLocation(0, 2, 0)).block = GrassBlock(Unit)
+
+ Blokk.sessions.registerListener(object : Listener {
+ @EventHandler
+ fun onSessionAfterLogin(event: SessionAfterLoginEvent) {
+ event.initialWorldAndLocation =
+ WorldAndLocationWithRotation(world, LocationWithRotation(0.0, 4.0, 0.0, 0f, 0f))
+ }
+ })
+ }
+}
diff --git a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilChunk.kt b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilChunk.kt
similarity index 97%
rename from blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilChunk.kt
rename to test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilChunk.kt
index fac4680..f38a801 100644
--- a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilChunk.kt
+++ b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilChunk.kt
@@ -1,4 +1,4 @@
-package space.blokk.world.anvil
+package space.blokk.testplugin.anvil
import com.google.common.cache.LoadingCache
import space.blokk.player.Player
diff --git a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilVoxel.kt b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilVoxel.kt
similarity index 97%
rename from blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilVoxel.kt
rename to test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilVoxel.kt
index a7de44a..b2ef75b 100644
--- a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilVoxel.kt
+++ b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilVoxel.kt
@@ -1,4 +1,4 @@
-package space.blokk.world.anvil
+package space.blokk.testplugin.anvil
import space.blokk.world.Chunk
import space.blokk.world.Voxel
diff --git a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilWorld.kt b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilWorld.kt
similarity index 96%
rename from blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilWorld.kt
rename to test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilWorld.kt
index 886a7ed..65ea274 100644
--- a/blokk-server/src/main/kotlin/space/blokk/world/anvil/AnvilWorld.kt
+++ b/test-plugin/src/main/kotlin/space/blokk/testplugin/anvil/AnvilWorld.kt
@@ -1,4 +1,4 @@
-package space.blokk.world.anvil
+package space.blokk.testplugin.anvil
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
diff --git a/test-plugin/src/main/resources/plugin_class.txt b/test-plugin/src/main/resources/plugin_class.txt
new file mode 100644
index 0000000..f7dff21
--- /dev/null
+++ b/test-plugin/src/main/resources/plugin_class.txt
@@ -0,0 +1 @@
+space.blokk.testplugin.TestPlugin