mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 11:24:56 -04:00
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:
parent
419209e88c
commit
312486cfb4
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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>) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)})"
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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.*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -592,7 +592,7 @@ public class InByteBuffer {
|
||||
return new ResourceLocation(resourceLocation);
|
||||
}
|
||||
|
||||
public ChunkPosition readChunkLocation() {
|
||||
public ChunkPosition readChunkPosition() {
|
||||
return new ChunkPosition(readInt(), readInt());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
67
src/main/java/de/bixilon/minosoft/util/chunk/LightUtil.kt
Normal file
67
src/main/java/de/bixilon/minosoft/util/chunk/LightUtil.kt
Normal 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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user