rendering: improvements, fixes, wip (2) light

* Wait for all neighbor chunks until preparation
* Convert more classes to kotlin
* Improve chunk preparation code a lot
* Show facing in debug screen
* Show more details on debug screen
This commit is contained in:
Bixilon 2021-02-27 19:13:24 +01:00
parent 419209e88c
commit 312486cfb4
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
26 changed files with 469 additions and 406 deletions

View File

@ -14,7 +14,7 @@ package de.bixilon.minosoft.data
import glm_.vec3.Vec3
enum class Directions (direction: Vec3) {
enum class Directions(direction: Vec3) {
DOWN(Vec3(0, -1, 0)),
UP(Vec3(0, 1, 0)),
NORTH(Vec3(0, 0, -1)),
@ -35,6 +35,8 @@ enum class Directions (direction: Vec3) {
companion object {
val DIRECTIONS = values()
val SIDES = arrayOf(NORTH, SOUTH, WEST, EAST)
const val SIDES_OFFSET = 2
@JvmStatic
fun byId(id: Int): Directions {

View File

@ -40,6 +40,11 @@ data class Dimension(
} else {
minY / ProtocolDefinition.SECTION_HEIGHT_Y
}
val highestSection = if (height < 0) {
(height + 1) / ProtocolDefinition.SECTION_HEIGHT_Y - 1
} else {
height / ProtocolDefinition.SECTION_HEIGHT_Y
}
override fun toString(): String {
return resourceLocation.toString()

View File

@ -12,13 +12,14 @@
*/
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
data class BlockPosition(val x: Int, val y: Int, val z: Int) {
constructor(chunkPosition: ChunkPosition, sectionHeight: Int, inChunkSectionPosition: InChunkSectionPosition) : this(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X + inChunkSectionPosition.x, sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + inChunkSectionPosition.y, chunkPosition.z * ProtocolDefinition.SECTION_WIDTH_Z + inChunkSectionPosition.z) // ToDo
fun getChunkLocation(): ChunkPosition {
fun getChunkPosition(): ChunkPosition {
val chunkX = if (this.x >= 0) {
this.x / ProtocolDefinition.SECTION_WIDTH_X
} else {
@ -32,7 +33,7 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) {
return ChunkPosition(chunkX, chunkY)
}
fun getInChunkLocation(): InChunkPosition {
fun getInChunkPosition(): InChunkPosition {
var x: Int = this.x % ProtocolDefinition.SECTION_WIDTH_X
if (x < 0) {
x += ProtocolDefinition.SECTION_WIDTH_X
@ -44,8 +45,16 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) {
return InChunkPosition(x, this.y, z)
}
fun getInChunkSectionLocation(): InChunkSectionPosition {
val location = getInChunkLocation()
infix operator fun plus(vec3: Vec3): BlockPosition {
return BlockPosition((x + vec3.x).toInt(), (y + vec3.y).toInt(), (z + vec3.z).toInt())
}
operator fun plus(directions: Directions): BlockPosition {
return this + directions.directionVector
}
fun getInChunkSectionPosition(): InChunkSectionPosition {
val location = getInChunkPosition()
return InChunkSectionPosition(location.x, getSectionHeight(), location.z)
}

View File

@ -13,19 +13,18 @@
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
import glm_.vec3.Vec3
data class ChunkPosition(val x: Int, val z: Int) {
override fun toString(): String {
return "($x $z)"
}
fun getLocationByDirection(direction: Directions): ChunkPosition {
return when (direction) {
Directions.NORTH -> ChunkPosition(x, z - 1)
Directions.SOUTH -> ChunkPosition(x, z + 1)
Directions.WEST -> ChunkPosition(x - 1, z)
Directions.EAST -> ChunkPosition(x + 1, z)
else -> throw IllegalArgumentException("Chunk location is just 2d")
}
operator fun plus(vec3: Vec3): ChunkPosition {
return ChunkPosition(x + vec3.x.toInt(), z + vec3.z.toInt())
}
operator fun plus(direction: Directions): ChunkPosition {
return this + direction.directionVector
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
data class InChunkPosition(val x: Int, val y: Int, val z: Int) {
@ -33,6 +34,10 @@ data class InChunkPosition(val x: Int, val y: Int, val z: Int) {
}
}
operator fun plus(vec3: Vec3): InChunkPosition {
return InChunkPosition((x + vec3.x).toInt(), (y + vec3.y).toInt(), (z + vec3.z).toInt())
}
override fun toString(): String {
return "($x $y $z)"
}

View File

@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
/**
* Chunk X, Y and Z location (max 16x16x16)
@ -28,14 +29,33 @@ data class InChunkSectionPosition(val x: Int, val y: Int, val z: Int) {
return InChunkPosition(x, y + ProtocolDefinition.SECTION_HEIGHT_Y * sectionHeight, z)
}
fun getLocationByDirection(direction: Directions): InChunkSectionPosition {
return when (direction) {
Directions.DOWN -> InChunkSectionPosition(x, y - 1, z)
Directions.UP -> InChunkSectionPosition(x, y + 1, z)
Directions.NORTH -> InChunkSectionPosition(x, y, z - 1)
Directions.SOUTH -> InChunkSectionPosition(x, y, z + 1)
Directions.WEST -> InChunkSectionPosition(x - 1, y, z)
Directions.EAST -> InChunkSectionPosition(x + 1, y, z)
operator fun plus(vec3: Vec3): InChunkSectionPosition {
var nextX = x + vec3.x.toInt()
var nextY = y + vec3.y.toInt()
var nextZ = z + vec3.z.toInt()
if (nextX < 0) {
nextX = ProtocolDefinition.SECTION_MAX_X
} else if (nextX > ProtocolDefinition.SECTION_MAX_X) {
nextX = 0
}
if (nextY < 0) {
nextY = ProtocolDefinition.SECTION_MAX_Y
} else if (nextY > ProtocolDefinition.SECTION_MAX_Y) {
nextY = 0
}
if (nextZ < 0) {
nextZ = ProtocolDefinition.SECTION_MAX_Z
} else if (nextZ > ProtocolDefinition.SECTION_MAX_Z) {
nextZ = 0
}
return InChunkSectionPosition(nextX, nextY, nextZ)
}
operator fun plus(directions: Directions): InChunkSectionPosition {
return this + directions.directionVector
}
}

View File

@ -33,12 +33,12 @@ class World {
fun getBlockInfo(blockPosition: BlockPosition): BlockInfo? {
val chunkLocation = blockPosition.getChunkLocation()
return chunks[chunkLocation]?.getBlockInfo(blockPosition.getInChunkLocation())
val chunkLocation = blockPosition.getChunkPosition()
return chunks[chunkLocation]?.getBlockInfo(blockPosition.getInChunkPosition())
}
fun getChunk(loc: ChunkPosition): Chunk? {
return chunks[loc]
fun getChunk(chunkPosition: ChunkPosition): Chunk? {
return chunks[chunkPosition]
}
fun getOrCreateChunk(chunkPosition: ChunkPosition): Chunk {
@ -50,7 +50,7 @@ class World {
}
fun setBlock(blockPosition: BlockPosition, block: BlockState?) {
chunks[blockPosition.getChunkLocation()]?.setRawBlock(blockPosition.getInChunkLocation(), block)
chunks[blockPosition.getChunkPosition()]?.setRawBlock(blockPosition.getInChunkPosition(), block)
}
fun unloadChunk(position: ChunkPosition) {
@ -94,7 +94,7 @@ class World {
}
fun setBlockEntityData(position: BlockPosition, data: BlockEntityMetaData?) {
chunks[position.getChunkLocation()]?.sections?.get(position.getSectionHeight())?.getBlockInfo(position.getInChunkSectionLocation())?.metaData = data
chunks[position.getChunkPosition()]?.sections?.get(position.getSectionHeight())?.getBlockInfo(position.getInChunkSectionPosition())?.metaData = data
}
fun setBlockEntityData(blockEntities: HashMap<BlockPosition, BlockEntityMetaData>) {

View File

@ -22,7 +22,7 @@ class NoiseBiomeAccessor(
) : BiomeAccessor {
override fun getBiome(position: BlockPosition): Biome? {
val inChunk = position.getInChunkSectionLocation()
val inChunk = position.getInChunkSectionPosition()
val index = inChunk.y * ProtocolDefinition.SECTION_HEIGHT_Y + ((inChunk.z / 4) * 4 + (inChunk.x / 4))
if (index < 0 || index > biomes.size) {
return null

View File

@ -16,13 +16,21 @@ package de.bixilon.minosoft.data.world.light
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.InChunkPosition
import de.bixilon.minosoft.data.world.World
class ChunkLightAccessor(
val blockLightLevel: MutableMap<InChunkPosition, Byte> = mutableMapOf(),
val skyLightLevel: MutableMap<InChunkPosition, Byte> = mutableMapOf(),
val world: World,
) : LightAccessor {
override fun getLightLevel(blockPosition: BlockPosition, direction: Directions): Int {
val inChunkLocation = blockPosition.getInChunkLocation()
return blockLightLevel[inChunkLocation]?.toInt() ?: 0
val inChunkPosition = blockPosition.getInChunkPosition()
val lightLevel = blockLightLevel[inChunkPosition] ?: skyLightLevel[inChunkPosition]
if (lightLevel == null) {
return 1
}
return lightLevel.toInt()
}
}

View File

@ -206,14 +206,14 @@ class Camera(
headLocation = Position(cameraPosition)
feetLocation = Position(headLocation.x, headLocation.y - PLAYER_HEIGHT, headLocation.z)
blockPosition = feetLocation.toBlockPosition()
currentBiome = connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition)
chunkPosition = blockPosition.getChunkLocation()
currentBiome = connection.player.world.getChunk(blockPosition.getChunkPosition())?.biomeAccessor?.getBiome(blockPosition)
chunkPosition = blockPosition.getChunkPosition()
sectionHeight = blockPosition.getSectionHeight()
inChunkSectionPosition = blockPosition.getInChunkSectionLocation()
inChunkSectionPosition = blockPosition.getInChunkSectionPosition()
// recalculate sky color for current biome
val blockPosition = Position(cameraPosition).toBlockPosition()
renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkLocation())?.biomeAccessor?.getBiome(blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR)
renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkPosition())?.biomeAccessor?.getBiome(blockPosition)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR)
connection.renderer.renderWindow.worldRenderer.recalculateFrustum(Frustum(this))
connection.player.world.dimension?.hasSkyLight?.let {
if (it) {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.chunk
import com.google.common.collect.ConcurrentHashMultiset
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.config.StaticConfiguration
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
@ -27,7 +28,6 @@ import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.logging.Log
import org.lwjgl.opengl.GL11.GL_CULL_FACE
import org.lwjgl.opengl.GL11.glEnable
import org.lwjgl.opengl.GL13.glDisable
@ -36,64 +36,30 @@ import java.util.concurrent.ConcurrentHashMap
class WorldRenderer(private val connection: Connection, private val world: World, val renderWindow: RenderWindow) : Renderer {
private lateinit var minecraftTextures: TextureArray
lateinit var chunkShader: Shader
private val chunkSectionsToDraw = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMesh>>()
private val visibleChunks: MutableSet<ChunkPosition> = mutableSetOf()
val chunkSectionsToDraw = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMesh>>()
val visibleChunks: MutableSet<ChunkPosition> = mutableSetOf()
private lateinit var frustum: Frustum
private var currentTick = 0 // for animation usage
private var lastTickIncrementTime = 0L
val queuedChunks: ConcurrentHashMultiset<ChunkPosition> = ConcurrentHashMultiset.create()
private fun prepareChunk(chunkPosition: ChunkPosition, sectionHeight: Int, section: ChunkSection, chunk: Chunk): ChunkMesh {
private fun prepareChunk(chunkPosition: ChunkPosition, sectionHeight: Int, section: ChunkSection): ChunkMesh {
queuedChunks.remove(chunkPosition)
if (frustum.containsChunk(chunkPosition, connection)) {
visibleChunks.add(chunkPosition)
}
val chunk = world.getChunk(chunkPosition)!!
val mesh = ChunkMesh()
val below = world.chunks[chunkPosition]?.sections?.get(sectionHeight - 1)
val above = world.chunks[chunkPosition]?.sections?.get(sectionHeight + 1)
//val north = (world.allChunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight)
//val south = (world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?: throw ChunkNotLoadedException("South not loaded")).sections?.get(sectionHeight)
//val west = (world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?: throw ChunkNotLoadedException("West not loaded")).sections?.get(sectionHeight)
//val east = (world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight)
val north = world.chunks[chunkPosition.getLocationByDirection(Directions.NORTH)]?.sections?.get(sectionHeight)
val south = world.chunks[chunkPosition.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight)
val west = world.chunks[chunkPosition.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight)
val east = world.chunks[chunkPosition.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight)
for ((position, blockInfo) in section.blocks) {
val blockBelow: BlockInfo? = if (position.y == 0 && below != null) {
below.getBlockInfo(position.x, ProtocolDefinition.SECTION_HEIGHT_Y - 1, position.z)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.DOWN))
}
val blockAbove: BlockInfo? = if (position.y == ProtocolDefinition.SECTION_HEIGHT_Y - 1 && above != null) {
above.getBlockInfo(position.x, 0, position.z)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.UP))
}
val blockNorth: BlockInfo? = if (position.z == 0 && north != null) {
north.getBlockInfo(position.x, position.y, ProtocolDefinition.SECTION_WIDTH_Z - 1)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.NORTH))
}
val blockSouth: BlockInfo? = if (position.z == ProtocolDefinition.SECTION_WIDTH_Z - 1 && south != null) {
south.getBlockInfo(position.x, position.y, 0)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.SOUTH))
}
val blockWest: BlockInfo? = if (position.x == 0 && west != null) {
west.getBlockInfo(ProtocolDefinition.SECTION_WIDTH_X - 1, position.y, position.x)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.WEST))
}
val blockEast: BlockInfo? = if (position.x == ProtocolDefinition.SECTION_WIDTH_X - 1 && east != null) {
east.getBlockInfo(0, position.y, position.z)
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.EAST))
}
val blockPosition = BlockPosition(chunkPosition, sectionHeight, position)
if (blockPosition == BlockPosition(-103, 3, 288)) {
Log.debug("")
val neighborBlocks: Array<BlockInfo?> = arrayOfNulls(Directions.DIRECTIONS.size)
for (direction in Directions.DIRECTIONS) {
neighborBlocks[direction.ordinal] = world.getBlockInfo(blockPosition + direction)
}
val biome = chunk.biomeAccessor!!.getBiome(blockPosition)
var tintColor: RGBColor? = null
@ -111,7 +77,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
blockInfo.block.tintColor?.let { tintColor = it }
}
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, chunk.lightAccessor!!, tintColor, blockPosition, mesh, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, chunk.lightAccessor!!, tintColor, blockPosition, mesh, neighborBlocks)
}
return mesh
}
@ -173,19 +139,54 @@ class WorldRenderer(private val connection: Connection, private val world: World
return textures
}
fun prepareChunk(chunkPosition: ChunkPosition, chunk: Chunk) {
chunkSectionsToDraw[chunkPosition] = ConcurrentHashMap()
if (!chunk.isFullyLoaded) {
fun prepareChunk(chunkPosition: ChunkPosition, chunk: Chunk? = world.getChunk(chunkPosition), checkQueued: Boolean = true) {
if (chunk == null || !chunk.isFullyLoaded) {
return
}
val neighborsChunkPositions: Array<ChunkPosition> = arrayOf(
chunkPosition + Directions.NORTH,
chunkPosition + Directions.SOUTH,
chunkPosition + Directions.WEST,
chunkPosition + Directions.EAST,
)
// ensure all neighbor chunks are loaded
for (direction in Directions.SIDES) {
val neighborChunk = world.chunks[chunkPosition + direction]
if (neighborChunk == null || !neighborChunk.isFullyLoaded) {
// neighbors not loaded, doing later
if (checkQueued) {
checkQueuedChunks(neighborsChunkPositions)
}
queuedChunks.add(chunkPosition)
return
}
}
queuedChunks.remove(chunkPosition)
chunkSectionsToDraw[chunkPosition] = ConcurrentHashMap()
for ((sectionHeight, section) in chunk.sections!!) {
prepareChunkSection(chunkPosition, sectionHeight, section, chunk)
prepareChunkSection(chunkPosition, sectionHeight, section)
}
if (checkQueued) {
checkQueuedChunks(neighborsChunkPositions)
}
}
private fun checkQueuedChunks(chunkPositions: Array<ChunkPosition>) {
for (position in chunkPositions) {
if (queuedChunks.contains(position)) {
prepareChunk(position, checkQueued = false)
}
}
}
fun prepareChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, section: ChunkSection, chunk: Chunk) {
fun prepareChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, section: ChunkSection) {
renderWindow.rendering.executor.execute {
val mesh = prepareChunk(chunkPosition, sectionHeight, section, chunk)
val mesh = prepareChunk(chunkPosition, sectionHeight, section)
var sectionMap = chunkSectionsToDraw[chunkPosition]
if (sectionMap == null) {
@ -201,6 +202,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
fun clearChunkCache() {
queuedChunks.clear()
renderWindow.renderQueue.add {
for ((location, map) in chunkSectionsToDraw) {
for ((sectionHeight, mesh) in map) {
@ -213,6 +215,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
fun unloadChunk(chunkPosition: ChunkPosition) {
queuedChunks.remove(chunkPosition)
renderWindow.renderQueue.add {
chunkSectionsToDraw[chunkPosition]?.let {
for ((_, mesh) in it) {
@ -243,8 +246,8 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
}
}
fun getChunkSize(): Int {
return chunkSectionsToDraw.size
}
}
private operator fun Int.plus(upOrDown: Directions): Int {
return this + upOrDown.directionVector.y.toInt()
}

View File

@ -98,9 +98,11 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
}
companion object {
val EMPTY_VECTOR = Vec3()
fun createElements(state: JsonObject, parent: BlockModel): MutableList<ElementRenderer> {
val rotation = glm.radians(vec3InJsonObject(state))
val uvlock = state["uvlock"]?.asBoolean ?: false
val uvLock = state["uvlock"]?.asBoolean ?: false
val rescale = state["rescale"]?.asBoolean ?: false
val parentElements = parent.elements
val result: MutableList<ElementRenderer> = mutableListOf()
@ -139,7 +141,7 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
}
fun getRotatedDirection(rotation: Vec3, direction: Directions): Directions {
if (rotation == Vec3(0, 0, 0)) {
if (rotation == EMPTY_VECTOR) {
return direction
}
var rotatedDirectionVector = BlockModelElement.rotateVector(direction.directionVector, rotation.z.toDouble(), Axes.Z)
@ -148,7 +150,7 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
}
fun rotatePositionsAxes(positions: Array<Vec3>, angles: Vec3, rescale: Boolean) {
if (angles == Vec3()) {
if (angles == EMPTY_VECTOR) {
return
}
BlockModelElement.rotatePositions(positions, Axes.X, angles.x.toDouble(), Vec3(), rescale)

View File

@ -13,4 +13,4 @@
package de.bixilon.minosoft.gui.rendering.exceptions
class ChunkNotLoadedException(message: String) : Exception(message)
class ChunkNotLoadedException(message: String = "Chunk is unloaded") : Exception(message)

View File

@ -17,6 +17,7 @@ import de.bixilon.minosoft.config.StaticConfiguration
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
import de.bixilon.minosoft.config.key.KeyAction
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.gui.rendering.font.FontBindings
import de.bixilon.minosoft.modding.loading.ModLoader
import de.bixilon.minosoft.util.GitInfo
@ -49,8 +50,8 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf(
"FPS: ${getFPS()}",
"Timings: avg ${getAvgFrameTime()}ms, min ${getMinFrameTime()}ms, max ${getMaxFrameTime()}ms",
"Chunks: ${hudTextElement.renderWindow.worldRenderer.getChunkSize()} / ${hudTextElement.connection.player.world.chunks.size}",
"Connected to ${hudTextElement.connection.address} with ${hudTextElement.connection.version}",
"Chunks: q=${hudTextElement.renderWindow.worldRenderer.queuedChunks.size} v=${hudTextElement.renderWindow.worldRenderer.visibleChunks.size} p=${hudTextElement.renderWindow.worldRenderer.chunkSectionsToDraw.size} t=${hudTextElement.connection.player.world.chunks.size}",
"Connected to ${hudTextElement.connection.address} on ${hudTextElement.connection.version} with ${hudTextElement.connection.player.account.username}",
"",
"XYZ ${getLocation()}",
"Block ${getBlockPosition()}",
@ -159,7 +160,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
private fun getFacing(): String {
val yaw = hudTextElement.renderWindow.camera.yaw
val pitch = hudTextElement.renderWindow.camera.pitch
return "todo (${formatRotation(yaw)} / ${formatRotation(pitch)})"
return "${Directions.byDirection(camera.cameraFront).name.toLowerCase()} (${formatRotation(yaw)} / ${formatRotation(pitch)})"
}

View File

@ -1,81 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.modding.event.events.BlockChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.logging.Log;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W03B;
public class PacketBlockChange extends ClientboundPacket {
BlockPosition position;
BlockState block;
@Override
public boolean read(InByteBuffer buffer) {
if (buffer.getVersionId() < V_14W03B) {
this.position = buffer.readBlockPositionByte();
this.block = buffer.getConnection().getMapping().getBlockState((buffer.readVarInt() << 4) | buffer.readByte()); // ToDo: When was the meta data "compacted"? (between 1.7.10 - 1.8)
return true;
}
this.position = buffer.readPosition();
this.block = buffer.getConnection().getMapping().getBlockState(buffer.readVarInt());
return true;
}
@Override
public void handle(Connection connection) {
Chunk chunk = connection.getPlayer().getWorld().getChunk(getPosition().getChunkLocation());
if (chunk == null) {
// thanks mojang
return;
}
connection.fireEvent(new BlockChangeEvent(connection, this));
int sectionHeight = getPosition().getSectionHeight();
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
// tweak
if (!connection.getVersion().isFlattened()) {
BlockState block = VersionTweaker.transformBlock(getBlock(), chunk.getSections(), this.position.getInChunkSectionLocation(), this.position.getSectionHeight());
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block);
} else {
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock());
}
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunkSection(getPosition().getChunkLocation(), sectionHeight, section, chunk);
}
@Override
public void log() {
Log.protocol(String.format("[IN] Block change received at %s (block=%s)", this.position, this.block));
}
public BlockPosition getPosition() {
return this.position;
}
public BlockState getBlock() {
return this.block;
}
}

View File

@ -0,0 +1,63 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.modding.event.events.BlockChangeEvent
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.logging.Log
class PacketBlockChange : ClientboundPacket() {
lateinit var position: BlockPosition
var block: BlockState? = null
override fun read(buffer: InByteBuffer): Boolean {
if (buffer.versionId < ProtocolVersions.V_14W03B) {
position = buffer.readBlockPositionByte()
block = buffer.connection.mapping.getBlockState(buffer.readVarInt() shl 4 or buffer.readByte().toInt()) // ToDo: When was the meta data "compacted"? (between 1.7.10 - 1.8)
return true
}
position = buffer.readPosition()
block = buffer.connection.mapping.getBlockState(buffer.readVarInt())
return true
}
override fun handle(connection: Connection) {
val chunk = connection.player.world.getChunk(position.getChunkPosition()) ?: return // thanks mojang
if (!chunk.isFullyLoaded) {
return
}
connection.fireEvent(BlockChangeEvent(connection, this))
val sectionHeight = position.getSectionHeight()
val inChunkSectionPosition = position.getInChunkSectionPosition()
val section = chunk.getSectionOrCreate(sectionHeight)
// tweak
if (!connection.version.isFlattened()) {
val block = VersionTweaker.transformBlock(block!!, chunk.sections!!, inChunkSectionPosition, sectionHeight)
section.setRawBlock(inChunkSectionPosition, block)
} else {
section.setRawBlock(inChunkSectionPosition, block)
}
connection.renderer.renderWindow.worldRenderer.prepareChunkSection(position.getChunkPosition(), sectionHeight, section)
}
override fun log() {
Log.protocol(String.format("[IN] Block change received at %s (block=%s)", position, block))
}
}

View File

@ -20,8 +20,8 @@ import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.ChunkUtil
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.chunk.ChunkUtil
import de.bixilon.minosoft.util.logging.Log
import java.util.*
@ -44,7 +44,7 @@ class PacketChunkBulk : ClientboundPacket() {
// chunk meta data
for (i in 0 until chunkCount) {
val chunkPosition = buffer.readChunkLocation()
val chunkPosition = buffer.readChunkPosition()
val sectionBitMask = longArrayOf(buffer.readUnsignedShort().toLong())
val addBitMask = buffer.readUnsignedShort()
data[chunkPosition] = ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMask, addBitMask, true, containsSkyLight)

View File

@ -24,8 +24,8 @@ import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.ChunkUtil
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.chunk.ChunkUtil
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.nbt.tag.CompoundTag
import java.util.*

View File

@ -1,129 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkPosition;
import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.data.world.InChunkPosition;
import de.bixilon.minosoft.modding.event.events.MultiBlockChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.logging.Log;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*;
public class PacketMultiBlockChange extends ClientboundPacket {
private final HashMap<InChunkPosition, BlockState> blocks = new HashMap<>();
private ChunkPosition chunkPosition;
@Override
public boolean read(InByteBuffer buffer) {
if (buffer.getVersionId() < V_14W26C) {
if (buffer.getVersionId() < V_1_7_5) {
this.chunkPosition = new ChunkPosition(buffer.readVarInt(), buffer.readVarInt());
} else {
this.chunkPosition = new ChunkPosition(buffer.readInt(), buffer.readInt());
}
short count = buffer.readShort();
int dataSize = buffer.readInt(); // should be count * 4
for (int i = 0; i < count; i++) {
int raw = buffer.readInt();
byte meta = (byte) (raw & 0xF);
short blockId = (short) ((raw & 0xFF_F0) >>> 4);
byte y = (byte) ((raw & 0xFF_00_00) >>> 16);
byte z = (byte) ((raw & 0x0F_00_00_00) >>> 24);
byte x = (byte) ((raw & 0xF0_00_00_00) >>> 28);
this.blocks.put(new InChunkPosition(x, y, z), buffer.getConnection().getMapping().getBlockState((blockId << 4) | meta));
}
return true;
}
if (buffer.getVersionId() < V_20W28A) {
this.chunkPosition = new ChunkPosition(buffer.readInt(), buffer.readInt());
int count = buffer.readVarInt();
for (int i = 0; i < count; i++) {
byte pos = buffer.readByte();
byte y = buffer.readByte();
int blockId = buffer.readVarInt();
this.blocks.put(new InChunkPosition((pos & 0xF0 >>> 4) & 0xF, y, pos & 0xF), buffer.getConnection().getMapping().getBlockState(blockId));
}
return true;
}
long rawPos = buffer.readLong();
this.chunkPosition = new ChunkPosition((int) (rawPos >> 42), (int) (rawPos << 22 >> 42));
int yOffset = ((int) rawPos & 0xFFFFF) * 16;
if (buffer.getVersionId() > V_1_16_2_PRE3) {
buffer.readBoolean(); // ToDo
}
int count = buffer.readVarInt();
for (int i = 0; i < count; i++) {
long data = buffer.readVarLong();
this.blocks.put(new InChunkPosition((int) ((data >> 8) & 0xF), yOffset + (int) ((data >> 4) & 0xF), (int) (data & 0xF)), buffer.getConnection().getMapping().getBlockState(((int) (data >>> 12))));
}
return true;
}
@Override
public void handle(Connection connection) {
Chunk chunk = connection.getPlayer().getWorld().getChunk(getChunkPosition());
if (chunk == null) {
// thanks mojang
return;
}
connection.fireEvent(new MultiBlockChangeEvent(connection, this));
chunk.setRawBlocks(getBlocks());
// tweak
if (!connection.getVersion().isFlattened()) {
for (Map.Entry<InChunkPosition, BlockState> entry : getBlocks().entrySet()) {
BlockState block = VersionTweaker.transformBlock(entry.getValue(), chunk.getSections(), entry.getKey().getInChunkSectionLocation(), entry.getKey().getSectionHeight());
if (block == entry.getValue()) {
continue;
}
chunk.setRawBlock(entry.getKey(), block);
}
}
HashSet<Integer> sectionHeights = new HashSet<>();
for (var entry : this.blocks.entrySet()) {
sectionHeights.add(entry.getKey().getSectionHeight());
}
for (var sectionHeight : sectionHeights) {
ChunkSection section = chunk.getSectionOrCreate(sectionHeight);
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunkSection(getChunkPosition(), sectionHeight, section, chunk);
}
}
@Override
public void log() {
Log.protocol(String.format("[IN] Multi block change received at %s (size=%d)", this.chunkPosition, this.blocks.size()));
}
public ChunkPosition getChunkPosition() {
return this.chunkPosition;
}
public HashMap<InChunkPosition, BlockState> getBlocks() {
return this.blocks;
}
}

View File

@ -0,0 +1,108 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker
import de.bixilon.minosoft.data.world.ChunkPosition
import de.bixilon.minosoft.data.world.InChunkPosition
import de.bixilon.minosoft.modding.event.events.MultiBlockChangeEvent
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.logging.Log
import java.util.*
class PacketMultiBlockChange : ClientboundPacket() {
val blocks = HashMap<InChunkPosition, BlockState?>()
var chunkPosition: ChunkPosition? = null
private set
override fun read(buffer: InByteBuffer): Boolean {
if (buffer.versionId < ProtocolVersions.V_14W26C) {
chunkPosition = if (buffer.versionId < ProtocolVersions.V_1_7_5) {
ChunkPosition(buffer.readVarInt(), buffer.readVarInt())
} else {
buffer.readChunkPosition()
}
val count = buffer.readShort()
val dataSize = buffer.readInt() // should be count * 4
for (i in 0 until count) {
val raw = buffer.readInt()
val meta = (raw and 0xF)
val blockId = (raw and 0xFFF0 ushr 4)
val y = (raw and 0xFF0000 ushr 16)
val z = (raw and 0x0F000000 ushr 24)
val x = (raw and -0x10000000 ushr 28)
blocks[InChunkPosition(x, y, z)] = buffer.connection.mapping.getBlockState((blockId shl 4) or meta)
}
return true
}
if (buffer.versionId < ProtocolVersions.V_20W28A) {
chunkPosition = ChunkPosition(buffer.readInt(), buffer.readInt())
val count = buffer.readVarInt()
for (i in 0 until count) {
val position = buffer.readByte().toInt()
val y = buffer.readByte()
val blockId = buffer.readVarInt()
blocks[InChunkPosition(position and 0xF0 ushr 4 and 0xF, y.toInt(), position and 0xF)] = buffer.connection.mapping.getBlockState(blockId)
}
return true
}
val rawPos = buffer.readLong()
chunkPosition = ChunkPosition((rawPos shr 42).toInt(), (rawPos shl 22 shr 42).toInt())
val yOffset = (rawPos.toInt() and 0xFFFFF) * 16
if (buffer.versionId > ProtocolVersions.V_1_16_2_PRE3) {
buffer.readBoolean() // ToDo
}
val count = buffer.readVarInt()
for (i in 0 until count) {
val data = buffer.readVarLong()
blocks[InChunkPosition((data shr 8 and 0xF).toInt(), yOffset + (data shr 4 and 0xF).toInt(), (data and 0xF).toInt())] = buffer.connection.mapping.getBlockState((data ushr 12).toInt())
}
return true
}
override fun handle(connection: Connection) {
val chunk = connection.player.world.getChunk(chunkPosition!!) ?: return // thanks mojang
if (!chunk.isFullyLoaded) {
return
}
connection.fireEvent(MultiBlockChangeEvent(connection, this))
chunk.setRawBlocks(blocks)
// tweak
if (!connection.version.isFlattened()) {
for ((key, value) in blocks) {
val block = VersionTweaker.transformBlock(value!!, chunk.sections!!, key.getInChunkSectionLocation(), key.getSectionHeight())
if (block === value) {
continue
}
chunk.setRawBlock(key, block)
}
}
val sectionHeights = HashSet<Int>()
for ((key) in blocks) {
sectionHeights.add(key.getSectionHeight())
}
for (sectionHeight in sectionHeights) {
val section = chunk.getSectionOrCreate(sectionHeight)
connection.renderer.renderWindow.worldRenderer.prepareChunkSection(chunkPosition!!, sectionHeight, section)
}
}
override fun log() {
Log.protocol(String.format("[IN] Multi block change received at %s (size=%d)", chunkPosition, blocks.size))
}
}

View File

@ -1,70 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkPosition;
import de.bixilon.minosoft.data.world.light.LightAccessor;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.ChunkUtil;
import de.bixilon.minosoft.util.logging.Log;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_16_PRE3;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_20W49A;
public class PacketUpdateLight extends ClientboundPacket {
private ChunkPosition position;
private LightAccessor lightAccessor;
@Override
public boolean read(InByteBuffer buffer) {
this.position = new ChunkPosition(buffer.readVarInt(), buffer.readVarInt());
if (buffer.getVersionId() >= V_1_16_PRE3) {
boolean trustEdges = buffer.readBoolean();
}
long[] skyLightMask;
long[] blockLightMask;
long[] emptySkyLightMask;
long[] emptyBlockLightMask;
if (buffer.getVersionId() < V_20W49A) {
// was a varInt before 20w45a, should we change this?
skyLightMask = new long[]{buffer.readVarLong()};
blockLightMask = new long[]{buffer.readVarLong()};
emptyBlockLightMask = new long[]{buffer.readVarLong()};
emptySkyLightMask = new long[]{buffer.readVarLong()};
} else {
skyLightMask = buffer.readLongArray();
blockLightMask = buffer.readLongArray();
emptySkyLightMask = buffer.readLongArray();
emptyBlockLightMask = buffer.readLongArray();
}
this.lightAccessor = ChunkUtil.readSkyLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask);
return true;
}
@Override
public void log() {
Log.protocol("[IN] Received light update (position=%s)", this.position);
}
@Override
public void handle(Connection connection) {
Chunk chunk = connection.getPlayer().getWorld().getOrCreateChunk(this.position);
chunk.setLightAccessor(this.lightAccessor);
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(this.position, chunk);
}
}

View File

@ -0,0 +1,67 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.world.ChunkPosition
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.chunk.LightUtil.readLightPacket
import de.bixilon.minosoft.util.logging.Log
class PacketUpdateLight : ClientboundPacket() {
private var position: ChunkPosition? = null
private var lightAccessor: LightAccessor? = null
override fun read(buffer: InByteBuffer): Boolean {
position = ChunkPosition(buffer.readVarInt(), buffer.readVarInt())
if (position == ChunkPosition(-1, 21)) {
Log.debug("")
}
if (buffer.versionId >= ProtocolVersions.V_1_16_PRE3) {
val trustEdges = buffer.readBoolean()
}
val skyLightMask: LongArray
val blockLightMask: LongArray
val emptySkyLightMask: LongArray
val emptyBlockLightMask: LongArray
if (buffer.versionId < ProtocolVersions.V_20W49A) {
// was a varInt before 20w45a, should we change this?
skyLightMask = longArrayOf(buffer.readVarLong())
blockLightMask = longArrayOf(buffer.readVarLong())
emptyBlockLightMask = longArrayOf(buffer.readVarLong())
emptySkyLightMask = longArrayOf(buffer.readVarLong())
} else {
skyLightMask = buffer.readLongArray()
blockLightMask = buffer.readLongArray()
emptySkyLightMask = buffer.readLongArray()
emptyBlockLightMask = buffer.readLongArray()
}
lightAccessor = readLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask, buffer.connection.player.world.dimension!!)
return true
}
override fun log() {
Log.protocol("[IN] Received light update (position=%s)", position)
}
override fun handle(connection: Connection) {
val chunk = connection.player.world.getOrCreateChunk(position!!)
chunk.lightAccessor = lightAccessor
connection.renderer.renderWindow.worldRenderer.prepareChunk(position!!, chunk)
}
}

View File

@ -592,7 +592,7 @@ public class InByteBuffer {
return new ResourceLocation(resourceLocation);
}
public ChunkPosition readChunkLocation() {
public ChunkPosition readChunkPosition() {
return new ChunkPosition(readInt(), readInt());
}
}

View File

@ -56,8 +56,11 @@ public final class ProtocolDefinition {
public static final Pattern SCOREBOARD_OBJECTIVE_PATTERN = Pattern.compile("[a-zA-z-.+]{1,16}");
public static final int SECTION_WIDTH_X = 16;
public static final int SECTION_MAX_X = SECTION_WIDTH_X - 1;
public static final int SECTION_WIDTH_Z = 16;
public static final int SECTION_MAX_Z = SECTION_WIDTH_Z - 1;
public static final int SECTION_HEIGHT_Y = 16;
public static final int SECTION_MAX_Y = SECTION_HEIGHT_Y - 1;
public static final int SECTIONS_PER_CHUNK = 16;
public static final int BLOCKS_PER_SECTION = SECTION_WIDTH_X * SECTION_HEIGHT_Y * SECTION_WIDTH_X;

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util;
package de.bixilon.minosoft.util.chunk;
import de.bixilon.minosoft.data.mappings.Dimension;
import de.bixilon.minosoft.data.mappings.biomes.Biome;
@ -23,10 +23,10 @@ import de.bixilon.minosoft.data.world.InChunkSectionPosition;
import de.bixilon.minosoft.data.world.biome.DummyBiomeAccessor;
import de.bixilon.minosoft.data.world.biome.XZBiomeAccessor;
import de.bixilon.minosoft.data.world.light.DummyLightAccessor;
import de.bixilon.minosoft.data.world.light.LightAccessor;
import de.bixilon.minosoft.data.world.palette.Palette;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.BitByte;
import java.util.BitSet;
import java.util.HashMap;
@ -203,6 +203,7 @@ public final class ChunkUtil {
if (containsSkyLight) {
byte[] skyLight = buffer.readBytes(ProtocolDefinition.BLOCKS_PER_SECTION >> 1);
}
// ToDo
}
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap));
@ -219,24 +220,4 @@ public final class ChunkUtil {
return chunkData;
}
public static LightAccessor readSkyLightPacket(InByteBuffer buffer, long[] skyLightMask, long[] blockLightMask, long[] emptyBlockLightMask, long[] emptySkyLightMask) {
readLightArray(buffer, BitSet.valueOf(skyLightMask));
readLightArray(buffer, BitSet.valueOf(blockLightMask));
// ToDo
return DummyLightAccessor.INSTANCE;
}
private static void readLightArray(InByteBuffer buffer, BitSet lightMask) {
int highestSectionIndex = ProtocolDefinition.SECTIONS_PER_CHUNK + 2;
if (buffer.getVersionId() >= V_20W49A) {
buffer.readVarInt(); // section count
highestSectionIndex = lightMask.length();
}
for (int c = 0; c < highestSectionIndex; c++) { // light sections
if (!lightMask.get(c)) {
continue;
}
byte[] light = buffer.readBytes(buffer.readVarInt());
}
}
}

View File

@ -0,0 +1,67 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.chunk
import de.bixilon.minosoft.data.mappings.Dimension
import de.bixilon.minosoft.data.world.InChunkPosition
import de.bixilon.minosoft.data.world.light.ChunkLightAccessor
import de.bixilon.minosoft.data.world.light.DummyLightAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import java.util.*
object LightUtil {
fun readLightPacket(buffer: InByteBuffer, skyLightMask: LongArray, blockLightMask: LongArray, emptyBlockLightMask: LongArray, emptySkyLightMask: LongArray, dimension: Dimension): LightAccessor {
// ToDo
val blockLight = readLightArray(buffer, BitSet.valueOf(blockLightMask), dimension)
if (!dimension.hasSkyLight) {
return ChunkLightAccessor(blockLight, world = buffer.connection.player.world)
}
val skyLight = readLightArray(buffer, BitSet.valueOf(skyLightMask), dimension)
return DummyLightAccessor
return ChunkLightAccessor(blockLight, skyLight, buffer.connection.player.world)
}
private fun readLightArray(buffer: InByteBuffer, lightMask: BitSet, dimension: Dimension): MutableMap<InChunkPosition, Byte> {
var highestSectionIndex = dimension.highestSection + 1
val lowesSectionIndex = dimension.lowestSection - 1
if (buffer.versionId >= ProtocolVersions.V_20W49A) {
buffer.readVarInt() // section count
highestSectionIndex = lightMask.length()
}
val lightLevels: MutableMap<InChunkPosition, Byte> = mutableMapOf()
for ((arrayIndex, c) in (lowesSectionIndex until highestSectionIndex).withIndex()) { // light sections
if (!lightMask[arrayIndex]) {
continue
}
val lightArray = buffer.readBytes(buffer.readVarInt())
var index = 0
for (y in 0 until 16) {
for (z in 0 until 16) {
for (x in 0 until 16 step 2) {
lightLevels[InChunkPosition(x, y + c * 16, z)] = (lightArray[index].toInt() and 0x0F).toByte()
lightLevels[InChunkPosition(x + 1, y + c * 16, z)] = ((lightArray[index].toInt() ushr 4) and 0x0F).toByte()
index++
}
}
}
}
return lightLevels
}
}