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="" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Build TestPlugin.jar" run_configuration_type="GradleRunConfiguration" />
|
||||
</method>
|
||||
</configuration>
|
||||
</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")
|
||||
|
||||
fun getStateID(block: T): Int {
|
||||
|
|
|
@ -14,6 +14,9 @@ interface Material<T : Block> {
|
|||
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()
|
||||
|
||||
|
|
|
@ -32,8 +32,9 @@ class NBT internal constructor(private val map: MutableMap<String, Any>) {
|
|||
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)
|
||||
|
|
|
@ -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<ChunkDataPacket>(0x22, ChunkDataPacket::class) {
|
||||
private val nonAirHeightmapIgnoredBlocks = setOf(Air::class, CaveAir::class)
|
||||
private val solidHeightmapIgnoredBlocks = Material.all.filter {
|
||||
// TODO
|
||||
true
|
||||
}
|
||||
private val airBlocks: Set<Material<*>> = 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<ChunkDataPacket>(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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import space.blokk.net.packet.Protocol
|
|||
object PlayProtocol : Protocol(
|
||||
"PLAY",
|
||||
ClientSettingsPacketCodec,
|
||||
ChunkDataPacketCodec,
|
||||
DeclareRecipesPacketCodec,
|
||||
DisconnectPacketCodec,
|
||||
IncomingPluginMessagePacketCodec,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,3 +12,4 @@ include(":blokk-nbt")
|
|||
include(":blokk-packet-codecs")
|
||||
include(":blokk-packets")
|
||||
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 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.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.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