Finish chunk data packet codec (for now)
This commit is contained in:
parent
b96afbfcea
commit
e0a0003b07
15 changed files with 138 additions and 16 deletions
21
.runConfigurations/Build TestPlugin.jar.run.xml
Normal file
21
.runConfigurations/Build TestPlugin.jar.run.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Build TestPlugin.jar" type="GradleRunConfiguration" factoryName="Gradle">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/test-plugin" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" value="" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="shadowJar" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" value="" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -10,6 +10,7 @@
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Build TestPlugin.jar" run_configuration_type="GradleRunConfiguration" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
|
@ -32,6 +32,8 @@ class BlockCodec<T : Block> internal constructor(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val lastStateID get() = firstStateID + states.size
|
||||||
|
|
||||||
class IllegalAttributeTargetType : Exception("The type of the target property is not allowed for attributes")
|
class IllegalAttributeTargetType : Exception("The type of the target property is not allowed for attributes")
|
||||||
|
|
||||||
fun getStateID(block: T): Int {
|
fun getStateID(block: T): Int {
|
||||||
|
|
|
@ -14,6 +14,9 @@ interface Material<T : Block> {
|
||||||
val collisionShape: CollisionShape
|
val collisionShape: CollisionShape
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* All materials, sorted by their numeric ID in ascending order.
|
||||||
|
*/
|
||||||
val all = GENERATED_BLOCKS
|
val all = GENERATED_BLOCKS
|
||||||
val byID = GENERATED_BLOCKS.map { it.id to it }.toMap()
|
val byID = GENERATED_BLOCKS.map { it.id to it }.toMap()
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@ class NBT internal constructor(private val map: MutableMap<String, Any>) {
|
||||||
map[name] = value
|
map[name] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun write(destination: DataOutputStream, name: String) {
|
fun write(destination: DataOutputStream, name: String? = null) {
|
||||||
NBTCompound.writeNamedTag(destination, name, this)
|
if (name == null) NBTCompound.writeValue(destination, this)
|
||||||
|
else NBTCompound.writeNamedTag(destination, name, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toSNBT(pretty: Boolean) = NBTCompound.toSNBT(this, pretty)
|
fun toSNBT(pretty: Boolean) = NBTCompound.toSNBT(this, pretty)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package space.blokk.net.packet.play
|
package space.blokk.net.packet.play
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
|
import io.netty.buffer.ByteBufOutputStream
|
||||||
|
import io.netty.buffer.Unpooled
|
||||||
import space.blokk.nbt.NBT
|
import space.blokk.nbt.NBT
|
||||||
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
|
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
|
||||||
import space.blokk.net.packet.OutgoingPacketCodec
|
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.Air
|
||||||
import space.blokk.world.block.CaveAir
|
import space.blokk.world.block.CaveAir
|
||||||
import space.blokk.world.block.Material
|
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<ChunkDataPacket>(0x22, ChunkDataPacket::class) {
|
object ChunkDataPacketCodec : OutgoingPacketCodec<ChunkDataPacket>(0x22, ChunkDataPacket::class) {
|
||||||
private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class)
|
private val airBlocks: Set<Material<*>> = setOf(Air, CaveAir)
|
||||||
private val solidHeightmapIgnoredBlocks = Material.all.filter {
|
private val nonSolidBlocks = Material.all.filter { it.collisionShape.isEmpty() }
|
||||||
// TODO
|
|
||||||
true
|
private val numberOfBitsPerBlock = ceil(log(Material.all.last().codec.lastStateID.toDouble(), 2.0)).toInt()
|
||||||
}
|
|
||||||
|
|
||||||
override fun ChunkDataPacket.encode(dst: ByteBuf) {
|
override fun ChunkDataPacket.encode(dst: ByteBuf) {
|
||||||
dst.writeInt(key.x)
|
dst.writeInt(key.x)
|
||||||
dst.writeInt(key.z)
|
dst.writeInt(key.z)
|
||||||
dst.writeBoolean(true) // Full Chunk
|
dst.writeBoolean(true) // Full Chunk
|
||||||
|
|
||||||
|
// Bitmask
|
||||||
var includedSections = 0
|
var includedSections = 0
|
||||||
data.sections.forEachIndexed { index, value ->
|
data.sections.forEachIndexed { index, value ->
|
||||||
if (value != null) includedSections = includedSections and (1 shl index)
|
if (value != null) includedSections = includedSections and (1 shl index)
|
||||||
|
@ -29,10 +35,46 @@ object ChunkDataPacketCodec : OutgoingPacketCodec<ChunkDataPacket>(0x22, ChunkDa
|
||||||
|
|
||||||
dst.writeVarInt(includedSections)
|
dst.writeVarInt(includedSections)
|
||||||
|
|
||||||
val heightmap = data.solidBlocksHeightmap ?: generateHeightmap(data.sections, listOf())
|
// Heightmaps
|
||||||
|
|
||||||
val heightmaps = NBT()
|
val heightmaps = NBT()
|
||||||
heightmaps.set("MOTION_BLOCKING", heightmap.toCompactLongArray(9))
|
heightmaps.set(
|
||||||
heightmaps.set("MOTION_BLOCKING", heightmap.toCompactLongArray(9))
|
"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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import space.blokk.net.packet.Protocol
|
||||||
object PlayProtocol : Protocol(
|
object PlayProtocol : Protocol(
|
||||||
"PLAY",
|
"PLAY",
|
||||||
ClientSettingsPacketCodec,
|
ClientSettingsPacketCodec,
|
||||||
|
ChunkDataPacketCodec,
|
||||||
DeclareRecipesPacketCodec,
|
DeclareRecipesPacketCodec,
|
||||||
DisconnectPacketCodec,
|
DisconnectPacketCodec,
|
||||||
IncomingPluginMessagePacketCodec,
|
IncomingPluginMessagePacketCodec,
|
||||||
|
|
|
@ -61,8 +61,10 @@ tasks {
|
||||||
|
|
||||||
@Suppress("UnstableApiUsage")
|
@Suppress("UnstableApiUsage")
|
||||||
manifest {
|
manifest {
|
||||||
this.attributes("Main-Class" to "space.blokk.BlokkServer")
|
this.attributes(
|
||||||
this.attributes("Implementation-Version" to project.version)
|
"Main-Class" to "space.blokk.BlokkServer",
|
||||||
|
"Implementation-Version" to project.version
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,3 +12,4 @@ include(":blokk-nbt")
|
||||||
include(":blokk-packet-codecs")
|
include(":blokk-packet-codecs")
|
||||||
include(":blokk-packets")
|
include(":blokk-packets")
|
||||||
include(":blokk-server")
|
include(":blokk-server")
|
||||||
|
include("test-plugin")
|
||||||
|
|
22
test-plugin/build.gradle.kts
Normal file
22
test-plugin/build.gradle.kts
Normal file
|
@ -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"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package space.blokk.world.anvil
|
package space.blokk.testplugin.anvil
|
||||||
|
|
||||||
import com.google.common.cache.LoadingCache
|
import com.google.common.cache.LoadingCache
|
||||||
import space.blokk.player.Player
|
import space.blokk.player.Player
|
|
@ -1,4 +1,4 @@
|
||||||
package space.blokk.world.anvil
|
package space.blokk.testplugin.anvil
|
||||||
|
|
||||||
import space.blokk.world.Chunk
|
import space.blokk.world.Chunk
|
||||||
import space.blokk.world.Voxel
|
import space.blokk.world.Voxel
|
|
@ -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.CacheBuilder
|
||||||
import com.google.common.cache.CacheLoader
|
import com.google.common.cache.CacheLoader
|
1
test-plugin/src/main/resources/plugin_class.txt
Normal file
1
test-plugin/src/main/resources/plugin_class.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
space.blokk.testplugin.TestPlugin
|
Reference in a new issue