diff --git a/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt b/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt index 56dabd96e..4d6325df3 100644 --- a/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt +++ b/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt @@ -35,7 +35,6 @@ import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.ChunkLight import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours import de.bixilon.minosoft.data.world.positions.ChunkPosition -import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.modding.event.master.EventMaster import de.bixilon.minosoft.protocol.network.session.Session import de.bixilon.minosoft.protocol.network.session.play.PlaySession @@ -80,7 +79,7 @@ object LightTestingUtil { } fun createChunkWithNeighbours(): Chunk { - val chunk = createEmptyChunk(Vec2i.EMPTY) + val chunk = createEmptyChunk(ChunkPosition.EMPTY) var index = 0 for (x in -1..1) { for (z in -1..1) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/noise/VoronoiBiomeAccessor.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/noise/VoronoiBiomeAccessor.kt index 138a2e62e..0a7d7a709 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/noise/VoronoiBiomeAccessor.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/noise/VoronoiBiomeAccessor.kt @@ -18,6 +18,7 @@ import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray import de.bixilon.minosoft.data.world.chunk.chunk.Chunk +import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition class VoronoiBiomeAccessor( @@ -37,9 +38,9 @@ class VoronoiBiomeAccessor( val biomeY = unpackY(offset) val biomeZ = unpackZ(offset) - val biomeChunk = chunk.neighbours.trace(biomeX shr 4, biomeZ shr 4) + val biomeChunk = chunk.neighbours.traceChunk(ChunkPosition(biomeX shr 4, biomeZ shr 4)) - return biomeChunk?.biomeSource?.get(biomeX and 0x0F, biomeY, biomeZ and 0x0F) + return biomeChunk?.biomeSource?.get(InChunkPosition(biomeX and 0x0F, biomeY, biomeZ and 0x0F)) } fun getBiomeOffset(seed: Long, x: Int, y: Int, z: Int): Int { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt index 22b2dadbd..36235a65d 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt @@ -21,9 +21,9 @@ import de.bixilon.minosoft.data.world.container.SectionDataProvider import de.bixilon.minosoft.data.world.container.biome.BiomeSectionDataProvider import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider import de.bixilon.minosoft.data.world.positions.BlockPosition +import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.protocol.network.session.play.PlaySession -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import java.util.* /** @@ -70,59 +70,16 @@ class ChunkSection( blockEntities.clear() } - fun traceBlock(offset: BlockPosition): BlockState? - fun traceBlock(origin: InSectionPosition, offset: BlockPosition) = traceBlock(offset - origin) - fun traceBlock(origin: InSectionPosition, direction: Directions) = traceBlock((BlockPosition(origin) + direction)) - - - @Deprecated("") - fun traceBlock(x: Int, y: Int, z: Int, direction: Directions) = when (direction) { - Directions.DOWN -> { - if (y == 0) { - neighbours?.get(Directions.O_DOWN)?.blocks?.let { it[x, ProtocolDefinition.SECTION_MAX_Y, z] } - } else { - blocks[x, y - 1, z] - } - } - - Directions.UP -> { - if (y == ProtocolDefinition.SECTION_MAX_Y) { - neighbours?.get(Directions.O_UP)?.blocks?.let { it[x, 0, z] } - } else { - blocks[x, y + 1, z] - } - } - - Directions.NORTH -> { - if (z == 0) { - neighbours?.get(Directions.O_NORTH)?.blocks?.let { it[x, y, ProtocolDefinition.SECTION_MAX_Z] } - } else { - blocks[x, y, z - 1] - } - } - - Directions.SOUTH -> { - if (z == ProtocolDefinition.SECTION_MAX_Z) { - neighbours?.get(Directions.O_SOUTH)?.blocks?.let { it[x, y, 0] } - } else { - blocks[x, y, z + 1] - } - } - - Directions.WEST -> { - if (x == 0) { - neighbours?.get(Directions.O_WEST)?.blocks?.let { it[ProtocolDefinition.SECTION_MAX_X, y, z] } - } else { - blocks[x - 1, y, z] - } - } - - Directions.EAST -> { - if (x == ProtocolDefinition.SECTION_MAX_X) { - neighbours?.get(Directions.O_EAST)?.blocks?.let { it[0, y, z] } - } else { - blocks[x + 1, y, z] - } + fun traceBlock(offset: BlockPosition): BlockState? { + val chunkOffset = offset.chunkPosition + val height = offset.sectionHeight + if (chunkOffset == ChunkPosition.EMPTY && height == 0) { + return blocks[offset.inSectionPosition] } + val chunk = this.chunk.neighbours.traceChunk(chunkOffset) ?: return null + return chunk[offset.inChunkPosition] } + + fun traceBlock(origin: InSectionPosition, offset: BlockPosition) = traceBlock(offset - origin) + fun traceBlock(origin: InSectionPosition, direction: Directions) = traceBlock((BlockPosition(origin.x, origin.y, origin.z) + direction)) } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt index 5dd12773d..fa9457a1e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt @@ -167,9 +167,9 @@ class Chunk( var section = sections[index] // get another time, it might have changed already if (section == null) { section = ChunkSection(sectionHeight, chunk = this) - val neighbours = this.neighbours.get() - if (neighbours != null) { - this.neighbours.completeSection(neighbours, section, sectionHeight, world.biomes.noise) + val neighbours = this.neighbours + if (neighbours.complete) { + this.neighbours.completeSection(section, sectionHeight, world.biomes.noise) } sections[index] = section @@ -181,10 +181,10 @@ class Chunk( sections[index + 1]?.neighbours?.set(Directions.O_DOWN, section) } - if (neighbours != null) { - for (neighbour in neighbours) { - val neighbourNeighbours = neighbour.neighbours.get() ?: continue - neighbour.neighbours.update(neighbourNeighbours, sectionHeight) + if (neighbours.complete) { + for (neighbour in neighbours.neighbours.array) { + if (neighbour == null || !neighbour.neighbours.complete) continue + neighbour.neighbours.update(sectionHeight) } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/ChunkHeightmap.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/ChunkHeightmap.kt index d5d68c66f..2b91a7e10 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/ChunkHeightmap.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/ChunkHeightmap.kt @@ -25,6 +25,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap { override fun get(index: Int) = heightmap[index] override fun get(x: Int, z: Int) = this[(z shl 4) or x] + override fun get(position: InChunkPosition) = get(position.x, position.z) protected abstract fun passes(state: BlockState): HeightmapPass @@ -47,7 +48,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap { val sections = chunk.sections var y = Int.MIN_VALUE - val index = (z shl 4) or x + val index = position.xz sectionLoop@ for (sectionIndex in (position.y.sectionHeight - chunk.minSection) downTo 0) { if (sectionIndex >= sections.size) { @@ -88,7 +89,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap { override fun onBlockChange(position: InChunkPosition, state: BlockState?) { chunk.lock.lock() - val index = (z shl 4) or x + val index = position.xz val previous = heightmap[index] diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt index f8ffb4d8a..3538dbc0f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt @@ -17,7 +17,6 @@ import de.bixilon.kutil.array.ArrayUtil.getFirst import de.bixilon.kutil.array.ArrayUtil.getLast import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.world.chunk.chunk.Chunk -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -71,23 +70,23 @@ class BorderSectionLight( if (z > 0) { traceBlockIncrease(x, z - 1, neighbourLuminance) } else { - chunk.neighbours[ChunkNeighbours.NORTH]?.getBorderLight()?.traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance) + chunk.neighbours[Directions.NORTH]?.getBorderLight()?.traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance) } if (z < ProtocolDefinition.SECTION_MAX_Z) { traceBlockIncrease(x, z + 1, neighbourLuminance) } else { - chunk.neighbours[ChunkNeighbours.SOUTH]?.getBorderLight()?.traceBlockIncrease(x, 0, neighbourLuminance) + chunk.neighbours[Directions.SOUTH]?.getBorderLight()?.traceBlockIncrease(x, 0, neighbourLuminance) } if (x > 0) { traceBlockIncrease(x - 1, z, neighbourLuminance) } else { - chunk.neighbours[ChunkNeighbours.WEST]?.getBorderLight()?.traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLuminance) + chunk.neighbours[Directions.WEST]?.getBorderLight()?.traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLuminance) } if (x < ProtocolDefinition.SECTION_MAX_X) { traceBlockIncrease(x + 1, z, neighbourLuminance) } else { - chunk.neighbours[ChunkNeighbours.EAST]?.getBorderLight()?.traceBlockIncrease(0, z, neighbourLuminance) + chunk.neighbours[Directions.EAST]?.getBorderLight()?.traceBlockIncrease(0, z, neighbourLuminance) } } @@ -124,49 +123,49 @@ class BorderSectionLight( if (z > 0) { traceSkyIncrease(x, z - 1, neighbourLevel) } else { - chunk.neighbours.get(ChunkNeighbours.NORTH)?.getBorderLight()?.traceSkyIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLevel) + chunk.neighbours[Directions.NORTH]?.getBorderLight()?.traceSkyIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLevel) } if (z < ProtocolDefinition.SECTION_MAX_Y) { traceSkyIncrease(x, z + 1, neighbourLevel) } else { - chunk.neighbours.get(ChunkNeighbours.SOUTH)?.getBorderLight()?.traceSkyIncrease(x, 0, neighbourLevel) + chunk.neighbours[Directions.SOUTH]?.getBorderLight()?.traceSkyIncrease(x, 0, neighbourLevel) } if (x > 0) { traceSkyIncrease(x - 1, z, neighbourLevel) } else { - chunk.neighbours.get(ChunkNeighbours.WEST)?.getBorderLight()?.traceSkyIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLevel) + chunk.neighbours[Directions.WEST]?.getBorderLight()?.traceSkyIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLevel) } if (x < ProtocolDefinition.SECTION_MAX_X) { traceSkyIncrease(x + 1, z, neighbourLevel) } else { - chunk.neighbours.get(ChunkNeighbours.EAST)?.getBorderLight()?.traceSkyIncrease(0, z, neighbourLevel) + chunk.neighbours[Directions.EAST]?.getBorderLight()?.traceSkyIncrease(0, z, neighbourLevel) } } internal fun decreaseCheckLevel(x: Int, z: Int, light: Int, reset: Boolean) { decreaseCheckX(z, light, reset) - val neighbours = chunk.neighbours.get() ?: return + val neighbours = chunk.neighbours if (x - light < 0) { - neighbours[ChunkNeighbours.WEST].getBorderLight().decreaseCheckX(z, light - x, reset) + neighbours[Directions.WEST]?.getBorderLight()?.decreaseCheckX(z, light - x, reset) } if (x + light > ProtocolDefinition.SECTION_MAX_X) { - neighbours[ChunkNeighbours.EAST].getBorderLight().decreaseCheckX(z, light - (ProtocolDefinition.SECTION_MAX_X - x), reset) + neighbours[Directions.EAST]?.getBorderLight()?.decreaseCheckX(z, light - (ProtocolDefinition.SECTION_MAX_X - x), reset) } } private fun decreaseCheckX(z: Int, light: Int, reset: Boolean) { - val neighbours = chunk.neighbours.get() ?: return + val neighbours = chunk.neighbours if (reset) reset() if (z - light < 0) { - val neighbour = neighbours[ChunkNeighbours.NORTH].getBorderLight() - if (reset) neighbour.reset() + val neighbour = neighbours[Directions.NORTH]?.getBorderLight() + if (reset) neighbour?.reset() } if (z + light > ProtocolDefinition.SECTION_MAX_Z) { - val neighbour = neighbours[ChunkNeighbours.SOUTH].getBorderLight() - if (reset) neighbour.reset() + val neighbour = neighbours[Directions.SOUTH]?.getBorderLight() + if (reset) neighbour?.reset() } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt index b6e9f3465..1ac1be12e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt @@ -75,7 +75,7 @@ class ChunkLight(val chunk: Chunk) { for (chunkX in -1..1) { for (chunkZ in -1..1) { val offset = ChunkPosition(chunkX, chunkZ) - if (offset.xz == 0) continue + if (offset == ChunkPosition.EMPTY) continue val nextPosition = chunkPosition + offset val chunk = neighbours[neighbourIndex++] diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt index 8ebac25f6..2853a9c29 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2025 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. * @@ -14,9 +14,9 @@ package de.bixilon.minosoft.data.world.chunk.light import de.bixilon.minosoft.data.direction.Directions -import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours +import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray +import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight @@ -39,7 +39,7 @@ class ChunkSkyLight(val light: ChunkLight) { val baseY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y for (y in topY downTo bottomY) { - section.light.traceSkyLightIncrease(x, y, z, NEIGHBOUR_TRACE_LEVEL, target, baseY + y) + section.light.traceSkyLightIncrease(InSectionPosition(x, y, z), NEIGHBOUR_TRACE_LEVEL, target, baseY + y) } section.light.update = true } @@ -81,10 +81,10 @@ class ChunkSkyLight(val light: ChunkLight) { } val section = chunk[y.sectionHeight] ?: return - section.light.traceSkyLightDown(x, y.inSectionHeight, z, Directions.DOWN, y) + section.light.traceSkyLightDown(InSectionPosition(x, y.inSectionHeight, z), Directions.DOWN, y) } - private fun floodFill(neighbours: Array, x: Int, z: Int) { + private fun floodFill(neighbours: ChunkNeighbourArray, x: Int, z: Int) { val heightmapIndex = (z shl 4) or x val maxHeight = light.heightmap[heightmapIndex] @@ -94,39 +94,40 @@ class ChunkSkyLight(val light: ChunkLight) { if (x > 0) { trace(x - 1, light.heightmap[heightmapIndex - 1], maxHeight, z, Directions.WEST) } else { - val neighbour = neighbours[ChunkNeighbours.WEST].light - neighbour.sky.trace(ProtocolDefinition.SECTION_MAX_X, neighbour.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X], maxHeight, z, Directions.WEST) + val neighbour = neighbours[Directions.WEST]?.light + neighbour?.sky?.trace(ProtocolDefinition.SECTION_MAX_X, neighbour.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X], maxHeight, z, Directions.WEST) } if (x < ProtocolDefinition.SECTION_MAX_X) { trace(x + 1, light.heightmap[heightmapIndex + 1], maxHeight, z, Directions.EAST) } else { - val neighbour = neighbours[ChunkNeighbours.EAST].light - neighbour.sky.trace(0, neighbour.heightmap[(z shl 4) or 0], maxHeight, z, Directions.EAST) + val neighbour = neighbours[Directions.EAST]?.light + neighbour?.sky?.trace(0, neighbour.heightmap[(z shl 4) or 0], maxHeight, z, Directions.EAST) } if (z > 0) { trace(x, light.heightmap[((z - 1) shl 4) or x], maxHeight, z - 1, Directions.NORTH) } else { - val neighbour = neighbours[ChunkNeighbours.NORTH].light - neighbour.sky.trace(x, neighbour.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x], maxHeight, ProtocolDefinition.SECTION_MAX_Z, Directions.NORTH) + val neighbour = neighbours[Directions.NORTH]?.light + neighbour?.sky?.trace(x, neighbour.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x], maxHeight, ProtocolDefinition.SECTION_MAX_Z, Directions.NORTH) } if (z < ProtocolDefinition.SECTION_MAX_Z) { trace(x, light.heightmap[((z + 1) shl 4) or x], maxHeight, z + 1, Directions.SOUTH) } else { - val neighbour = neighbours[ChunkNeighbours.SOUTH].light - neighbour.sky.trace(x, neighbour.heightmap[(0 shl 4) or x], maxHeight, 0, Directions.SOUTH) + val neighbour = neighbours[Directions.SOUTH]?.light + neighbour?.sky?.trace(x, neighbour.heightmap[(0 shl 4) or x], maxHeight, 0, Directions.SOUTH) } } fun floodFill(x: Int, z: Int) { - val neighbours = chunk.neighbours.get() ?: return - floodFill(neighbours, x, z) + if (!this.chunk.neighbours.complete) return + floodFill(chunk.neighbours.neighbours, x, z) } private fun floodFill() { - val neighbours = this.chunk.neighbours.get() ?: return + val neighbours = this.chunk.neighbours.neighbours + if (!this.chunk.neighbours.complete) return for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { floodFill(neighbours, x, z) @@ -142,30 +143,30 @@ class ChunkSkyLight(val light: ChunkLight) { calculate() } - fun getNeighbourMinHeight(neighbours: Array, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int { + fun getNeighbourMinHeight(neighbours: ChunkNeighbourArray, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int { return minOf( if (x > 0) { light.heightmap[heightmapIndex - 1] } else { - neighbours[ChunkNeighbours.WEST].light.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X] + neighbours[Directions.WEST]?.light?.heightmap?.get((z shl 4) or ProtocolDefinition.SECTION_MAX_X) ?: 0 }, if (x < ProtocolDefinition.SECTION_MAX_X) { light.heightmap[heightmapIndex + 1] } else { - neighbours[ChunkNeighbours.EAST].light.heightmap[(z shl 4) or 0] + neighbours[Directions.EAST]?.light?.heightmap?.get((z shl 4) or 0) ?: 0 }, if (z > 0) { light.heightmap[((z - 1) shl 4) or x] } else { - neighbours[ChunkNeighbours.NORTH].light.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x] + neighbours[Directions.NORTH]?.light?.heightmap?.get((ProtocolDefinition.SECTION_MAX_Z shl 4) or x) ?: 0 }, if (z < ProtocolDefinition.SECTION_MAX_Z) { light.heightmap[((z + 1) shl 4) or x] } else { - neighbours[ChunkNeighbours.SOUTH].light.heightmap[(0 shl 4) or x] + neighbours[Directions.SOUTH]?.light?.heightmap?.get((0 shl 4) or x) ?: 0 } ) } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt index 3bab54864..acff30d34 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt @@ -17,9 +17,8 @@ import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.blocks.light.TransparentProperty import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.world.chunk.ChunkSection -import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.ChunkSkyLight.Companion.NEIGHBOUR_TRACE_LEVEL -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours +import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -144,7 +143,7 @@ class SectionLight( update = true } val chunk = section.chunk - val chunkNeighbours = chunk.neighbours.get() ?: return + val chunkNeighbours = chunk.neighbours.neighbours val neighbours = section.neighbours ?: return if (nextLuminance == 1) { @@ -183,28 +182,28 @@ class SectionLight( if (position.z > 0) { traceBlockIncrease(position.minusZ(), neighbourLuminance, Directions.NORTH) } else { - neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), neighbourLuminance, Directions.NORTH) + neighbours[Directions.O_NORTH, Directions.NORTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), neighbourLuminance, Directions.NORTH) } } if (target == null || (target != Directions.NORTH && lightProperties.propagatesLight(Directions.SOUTH))) { if (position.z < ProtocolDefinition.SECTION_MAX_Y) { traceBlockIncrease(position.plusZ(), neighbourLuminance, Directions.SOUTH) } else { - neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = 0), neighbourLuminance, Directions.SOUTH) + neighbours[Directions.O_SOUTH, Directions.SOUTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = 0), neighbourLuminance, Directions.SOUTH) } } if (target == null || (target != Directions.EAST && lightProperties.propagatesLight(Directions.WEST))) { if (position.x > 0) { traceBlockIncrease(position.minusX(), neighbourLuminance, Directions.WEST) } else { - neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), neighbourLuminance, Directions.WEST) + neighbours[Directions.O_WEST, Directions.WEST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), neighbourLuminance, Directions.WEST) } } if (target == null || (target != Directions.WEST && lightProperties.propagatesLight(Directions.EAST))) { if (position.x < ProtocolDefinition.SECTION_MAX_X) { traceBlockIncrease(position.plusX(), neighbourLuminance, Directions.EAST) } else { - neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = 0), neighbourLuminance, Directions.EAST) + neighbours[Directions.O_EAST, Directions.EAST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = 0), neighbourLuminance, Directions.EAST) } } } @@ -317,7 +316,7 @@ class SectionLight( // this light level will be 15, don't care return } - val chunkNeighbours = chunk.neighbours.get() ?: return + val chunkNeighbours = chunk.neighbours.neighbours val currentLight = this[position].toInt() if (((currentLight and SKY_LIGHT_MASK) shr 4) >= nextLevel) { return @@ -372,28 +371,28 @@ class SectionLight( if (position.z > 0) { traceSkyLightIncrease(position.minusZ(), nextNeighbourLevel, Directions.NORTH, totalY) } else { - neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), nextNeighbourLevel, Directions.NORTH, totalY) + neighbours[Directions.O_NORTH, Directions.NORTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), nextNeighbourLevel, Directions.NORTH, totalY) } } if (target != Directions.NORTH && (target == null || lightProperties.propagatesLight(Directions.SOUTH))) { if (position.z < ProtocolDefinition.SECTION_MAX_Z) { traceSkyLightIncrease(position.plusZ(), nextNeighbourLevel, Directions.SOUTH, totalY) } else { - neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = 0), nextNeighbourLevel, Directions.SOUTH, totalY) + neighbours[Directions.O_SOUTH, Directions.SOUTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = 0), nextNeighbourLevel, Directions.SOUTH, totalY) } } if (target != Directions.EAST && (target == null || lightProperties.propagatesLight(Directions.WEST))) { if (position.x > 0) { traceSkyLightIncrease(position.minusX(), nextNeighbourLevel, Directions.WEST, totalY) } else { - neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), nextNeighbourLevel, Directions.WEST, totalY) + neighbours[Directions.O_WEST, Directions.WEST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), nextNeighbourLevel, Directions.WEST, totalY) } } if (target != Directions.WEST && (target == null || lightProperties.propagatesLight(Directions.EAST))) { if (position.x < ProtocolDefinition.SECTION_MAX_X) { traceSkyLightIncrease(position.plusX(), nextNeighbourLevel, Directions.EAST, totalY) } else { - neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = 0), nextNeighbourLevel, Directions.EAST, totalY) + neighbours[Directions.O_EAST, Directions.EAST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = 0), nextNeighbourLevel, Directions.EAST, totalY) } } } @@ -428,8 +427,8 @@ class SectionLight( } } - private inline operator fun Array.get(direction: Int, neighbour: Int, neighbours: Array): ChunkSection? { - return this[direction] ?: neighbours[neighbour].getOrPut(section.sectionHeight, false) + private inline operator fun Array.get(direction: Int, neighbour: Directions, neighbours: ChunkNeighbourArray): ChunkSection? { + return this[direction] ?: neighbours[neighbour]?.getOrPut(section.sectionHeight, false) } fun propagateFromNeighbours(position: InSectionPosition) { @@ -492,7 +491,8 @@ class SectionLight( val totalY = section.sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + position.y section.chunk.let { // check if neighbours are above heightmap, if so set light level to max - val chunkNeighbours = it.neighbours.get() ?: return@let + val chunkNeighbours = it.neighbours.neighbours + if (!it.neighbours.complete) return@let val minHeight = it.light.sky.getNeighbourMinHeight(chunkNeighbours, position.x, position.z) if (totalY > minHeight) { skyLight = ProtocolDefinition.MAX_LIGHT_LEVEL_I diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt index 7d2a15bae..b0f475c9c 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt @@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.ChunkPrototype import de.bixilon.minosoft.data.world.chunk.manager.size.WorldSizeManager -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours +import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate import de.bixilon.minosoft.data.world.chunk.update.WorldUpdateEvent import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate @@ -57,9 +57,9 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity: } val updates = hashSetOf(ChunkUnloadUpdate(position, chunk)) - for ((index, neighbour) in chunk.neighbours.neighbours.withIndex()) { + for ((index, neighbour) in chunk.neighbours.neighbours.array.withIndex()) { if (neighbour == null) continue - val offset = ChunkNeighbours.OFFSETS[index] + val offset = ChunkPosition(ChunkNeighbourArray.OFFSETS[index]) val neighbourPosition = position + offset neighbour.neighbours.remove(-offset) updates += NeighbourChangeUpdate(neighbourPosition, neighbour) @@ -137,10 +137,10 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity: val updates = HashSet(9, 1.0f) updates += ChunkCreateUpdate(chunk.position, chunk) - for (index in 0 until ChunkNeighbours.COUNT) { - val offset = ChunkNeighbours.OFFSETS[index] + for (index in 0 until ChunkNeighbourArray.COUNT) { + val offset = ChunkPosition(ChunkNeighbourArray.OFFSETS[index]) val neighbour = this.chunks.unsafe[chunk.position + offset] ?: continue - chunk.neighbours[index] = neighbour + chunk.neighbours[offset] = neighbour neighbour.neighbours[-offset] = chunk } @@ -148,7 +148,7 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity: // TODO: fire event - for (neighbour in chunk.neighbours) { + for (neighbour in chunk.neighbours.neighbours.array) { if (neighbour == null) continue updates += NeighbourChangeUpdate(neighbour.position, neighbour) } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbourArray.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbourArray.kt new file mode 100644 index 000000000..94be0f5c0 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbourArray.kt @@ -0,0 +1,78 @@ +/* + * Minosoft + * Copyright (C) 2020-2025 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.neighbours + +import de.bixilon.minosoft.data.Axes +import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.world.chunk.chunk.Chunk +import de.bixilon.minosoft.data.world.positions.ChunkPosition + +@JvmInline +value class ChunkNeighbourArray(val array: Array) { + + constructor() : this(arrayOfNulls(OFFSETS.size)) + + + private operator fun get(index: Int) = array[index] + private operator fun set(index: Int, chunk: Chunk?) { + array[index] = chunk + } + + operator fun get(direction: Directions): Chunk? { + if (direction.axis == Axes.Y) throw IllegalArgumentException("Chunk neighbours are 2D!") + return this[BY_DIRECTION[direction.ordinal - Directions.SIDE_OFFSET]] + } + + operator fun get(offset: ChunkPosition) = this[offset.neighbourIndex] + + operator fun set(offset: ChunkPosition, chunk: Chunk?) { + this[offset.neighbourIndex] = chunk + } + + + companion object { + const val COUNT = 8 + private val BY_DIRECTION = intArrayOf(3, 4, 1, 6) + + /** + * 0 | 3 | 5 + * 1 | - | 6 + * 2 | 4 | 7 + */ + private val _0 = ChunkPosition(-1, -1) + private val _1 = ChunkPosition(-1, +0) + private val _2 = ChunkPosition(-1, +1) + private val _3 = ChunkPosition(+0, -1) + private val _4 = ChunkPosition(+0, +1) + private val _5 = ChunkPosition(+1, -1) + private val _6 = ChunkPosition(+1, +0) + private val _7 = ChunkPosition(+1, +1) + + + val OFFSETS = longArrayOf(_0.raw, _1.raw, _2.raw, _3.raw, _4.raw, _5.raw, _6.raw, _7.raw) + + private val ChunkPosition.neighbourIndex: Int + get() = when (this) { // TODO: use some hash table + _0 -> 0 + _1 -> 1 + _2 -> 2 + _3 -> 3 + _4 -> 4 + _5 -> 5 + _6 -> 6 + _7 -> 7 + else -> throw IllegalArgumentException("Invalid chunk offset: $this") + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt index 942639dee..fcf0c286f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt @@ -25,84 +25,69 @@ import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.ChunkUtil +import kotlin.math.abs -class ChunkNeighbours(val chunk: Chunk) : Iterable { - val neighbours: Array = arrayOfNulls(COUNT) +class ChunkNeighbours(val chunk: Chunk) { + val neighbours = ChunkNeighbourArray() private var count = 0 - val complete: Boolean get() = count == COUNT + val complete: Boolean get() = count == ChunkNeighbourArray.COUNT fun get(): Array? { - if (count == COUNT) { // TODO: Race condition! + if (complete) { // TODO: Race condition! return neighbours.unsafeCast() } return null } - @Deprecated("index") - operator fun set(index: Int, chunk: Chunk) { + operator fun set(offset: ChunkPosition, chunk: Chunk) { this.chunk.lock.lock() - val current = neighbours[index] - neighbours[index] = chunk + val current = neighbours[offset] + neighbours[offset] = chunk if (current == null) { count++ - if (count == COUNT) { - complete(get()!!) + if (complete) { + complete() } } this.chunk.lock.unlock() } - operator fun set(offset: ChunkPosition, chunk: Chunk) { - set(getIndex(offset), chunk) - } - - @Deprecated("index") - fun remove(index: Int) { + fun remove(offset: ChunkPosition) { chunk.lock.lock() - val current = neighbours[index] - neighbours[index] = null + val current = neighbours[offset] if (current != null) { + neighbours[offset] = null count-- } chunk.lock.unlock() } - fun remove(offset: ChunkPosition) { - remove(getIndex(offset)) - } - - fun completeSection(neighbours: Array, section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) { + fun completeSection(section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) { section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight) } - private fun complete(neighbours: Array) { + private fun complete() { val noise = chunk.world.biomes.noise for ((index, section) in chunk.sections.withIndex()) { if (section == null) continue val sectionHeight = index + chunk.minSection - completeSection(neighbours, section, sectionHeight, noise) + completeSection(section, sectionHeight, noise) } chunk.light.recalculate(false) chunk.light.propagateFromNeighbours(fireEvent = false, fireSameChunkEvent = false) } - @Deprecated("index") - operator fun get(index: Int): Chunk? { - return neighbours[index] + operator fun get(direction: Directions): Chunk? { + return neighbours[direction] } - operator fun get(offset: ChunkPosition): Chunk { - if (offset.xz == 0) return chunk - return this[getIndex(offset)] // TODO: trace + operator fun get(offset: ChunkPosition): Chunk? { + if (offset == ChunkPosition.EMPTY) return chunk + return traceChunk(offset) } - override fun iterator(): Iterator { - return neighbours.iterator() - } - - - fun update(neighbours: Array, sectionHeight: Int) { + fun update(sectionHeight: Int) { for (nextSectionHeight in sectionHeight - 1..sectionHeight + 1) { if (nextSectionHeight < chunk.minSection || nextSectionHeight > chunk.maxSection) { continue @@ -114,29 +99,18 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable { } } - fun traceChunk(offset: ChunkPosition): Chunk = when { - offsetX == 0 -> when { - offsetZ == 0 -> chunk - offsetZ < 0 -> neighbours[3]?.neighbours?.trace(offsetX, offsetZ + 1) - offsetZ > 0 -> neighbours[4]?.neighbours?.trace(offsetX, offsetZ - 1) + fun traceChunk(offset: ChunkPosition): Chunk? { + if (offset == ChunkPosition.EMPTY) return chunk + if (abs(offset.x) <= 1 && abs(offset.z) <= 1) return this.neighbours[offset] + + // TODO: optimize diagonal trace + return when { + offset.z < 0 -> neighbours[Directions.NORTH]?.neighbours?.traceChunk(offset.plusZ()) + offset.z > 0 -> neighbours[Directions.SOUTH]?.neighbours?.traceChunk(offset.minusZ()) + offset.x < 0 -> neighbours[Directions.WEST]?.neighbours?.traceChunk(offset.plusX()) + offset.x > 0 -> neighbours[Directions.WEST]?.neighbours?.traceChunk(offset.minusX()) else -> Broken() } - - offsetX < 0 -> when { - offsetZ == 0 -> neighbours[1]?.neighbours?.trace(offsetX + 1, offsetZ) - offsetZ < 0 -> neighbours[0]?.neighbours?.trace(offsetX + 1, offsetZ + 1) - offsetZ > 0 -> neighbours[2]?.neighbours?.trace(offsetX + 1, offsetZ - 1) - else -> Broken() - } - - offsetX > 0 -> when { - offsetZ == 0 -> neighbours[6]?.neighbours?.trace(offsetX - 1, offsetZ) - offsetZ < 0 -> neighbours[5]?.neighbours?.trace(offsetX - 1, offsetZ + 1) - offsetZ > 0 -> neighbours[7]?.neighbours?.trace(offsetX - 1, offsetZ - 1) - else -> Broken() - } - - else -> Broken() } fun traceBlock(position: BlockPosition): BlockState? { @@ -148,30 +122,4 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable { fun traceBlock(origin: InChunkPosition, offset: BlockPosition) = traceBlock(offset - origin) fun traceBlock(origin: InChunkPosition, direction: Directions) = traceBlock((BlockPosition(origin) + direction)) - - companion object { - const val COUNT = 8 - const val NORTH = 3 - const val SOUTH = 4 - const val WEST = 1 - const val EAST = 6 - - - /** - * 0 | 3 | 5 - * 1 | - | 6 - * 2 | 4 | 7 - */ - - val OFFSETS = arrayOf( - ChunkPosition(-1, -1), // 0 - ChunkPosition(-1, +0), // 1 - ChunkPosition(-1, +1), // 2 - ChunkPosition(+0, -1), // 3 - ChunkPosition(+0, +1), // 4 - ChunkPosition(+1, -1), // 5 - ChunkPosition(+1, +0), // 6 - ChunkPosition(+1, +1), // 7 - ) - } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt index 65935c069..d0942c002 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt @@ -29,7 +29,7 @@ open class SectionDataProvider( private set val isEmpty: Boolean get() = count == 0 - var minPosition = InSectionPosition(ProtocolDefinition.SECTION_WIDTH_X, ProtocolDefinition.SECTION_HEIGHT_Y, ProtocolDefinition.SECTION_WIDTH_Z) + var minPosition = InSectionPosition(ProtocolDefinition.SECTION_MAX_X, ProtocolDefinition.SECTION_MAX_Y, ProtocolDefinition.SECTION_MAX_Z) private set var maxPosition = InSectionPosition(0, 0, 0) private set diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt index 09883dad7..f7a87805f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt @@ -27,67 +27,61 @@ value class BlockPosition( inline val index: Long, ) : TextFormattable { - init { - TODO() - } - constructor() : this(0, 0, 0) - constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) { - assertPosition(x, 0, ProtocolDefinition.SECTION_MAX_X) - assertPosition(y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y) - assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z) + constructor(x: Int, y: Int, z: Int) : this(((y and MASK_Y).toLong() shl SHIFT_Y) or ((z and MASK_Z).toLong() shl SHIFT_Z) or ((x and MASK_X).toLong() shl SHIFT_X)) { + assertPosition(x, -MAX_X, MAX_X) + assertPosition(y, -MAX_Y, MAX_Y) + assertPosition(z, -MAX_Z, MAX_Z) } constructor(position: InChunkPosition) : this(position.x, position.y, position.z) - constructor(position: InSectionPosition) : this(position.x, position.y, position.z) - inline val x: Int get() = (index and MASK_X) shr SHIFT_X - inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y - inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z - inline val xz: Int get() = (index and MASK_Z or MASK_X) + inline val x: Int get() = (index shr SHIFT_X).toInt() and MASK_X + inline val y: Int get() = (index shr SHIFT_Y).toInt() and MASK_Y + inline val z: Int get() = (index shr SHIFT_Z).toInt() and MASK_Z inline fun plusX(): BlockPosition { - assertPosition(this.x < ProtocolDefinition.SECTION_MAX_X) + assertPosition(this.x < MAX_X) return BlockPosition(index + X * 1) } inline fun plusX(x: Int): BlockPosition { - assertPosition(this.x + x, 0, ProtocolDefinition.SECTION_MAX_X) + assertPosition(this.x + x, -MAX_X, MAX_X) return BlockPosition(index + X * x) } inline fun minusX(): BlockPosition { - assertPosition(this.x > 0) + assertPosition(this.x > -MAX_X) return BlockPosition(index - X * 1) } inline fun plusY(): BlockPosition { - assertPosition(this.y < ProtocolDefinition.CHUNK_MAX_Y) + assertPosition(this.y < MAX_Y) return BlockPosition(index + Y * 1) } inline fun plusY(y: Int): BlockPosition { - assertPosition(this.y + y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y) + assertPosition(this.y + y, -MAX_Y, MAX_Y) return BlockPosition(index + Y * y) } inline fun minusY(): BlockPosition { - assertPosition(this.y > ProtocolDefinition.CHUNK_MIN_Y) + assertPosition(this.y > -MAX_Y) return BlockPosition(index - Y * 1) } inline fun plusZ(): BlockPosition { - assertPosition(this.z < ProtocolDefinition.SECTION_MAX_Z) + assertPosition(this.z < MAX_Y) return BlockPosition(index + Z * 1) } inline fun plusZ(z: Int): BlockPosition { - assertPosition(this.z + z, 0, ProtocolDefinition.SECTION_MAX_Z) + assertPosition(this.z + z, -MAX_Z, MAX_Z) return BlockPosition(index + Z * z) } inline fun minusZ(): BlockPosition { - assertPosition(this.z > 0) + assertPosition(this.z > -MAX_Z) return BlockPosition(index - Z * 1) } @@ -123,19 +117,26 @@ value class BlockPosition( override fun toString() = "b($x $y $z)" companion object { - const val MASK_X = 0x00F + const val BITS_X = 26 + const val MASK_X = (1 shl BITS_X) - 1 const val SHIFT_X = 0 - const val MASK_Z = 0x0F0 - const val SHIFT_Z = 4 + const val BITS_Z = 26 + const val MASK_Z = (1 shl BITS_Z) - 1 + const val SHIFT_Z = BITS_X - const val MASK_Y = 0xFFF00 - const val SHIFT_Y = 8 + const val BITS_Y = 12 + const val MASK_Y = (1 shl BITS_Y) - 1 + const val SHIFT_Y = BITS_X + BITS_Z const val X = 1 shl SHIFT_X const val Z = 1 shl SHIFT_Z const val Y = 1 shl SHIFT_Y + const val MAX_X = 0 + const val MAX_Y = 0 + const val MAX_Z = 0 + val EMPTY = BlockPosition(0, 0, 0) diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPositionUtil.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPositionUtil.kt index 672f3867d..60ff01797 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPositionUtil.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPositionUtil.kt @@ -32,7 +32,7 @@ object BlockPositionUtil { inline fun assertPosition(value: Int, min: Int, max: Int) { if (!DebugOptions.VERIFY_COORDINATES) return if (value < min) throw AssertionError("coordinate out of range: $value < $min") - if (value > min) throw AssertionError("coordinate out of range: $value > $max") + if (value > max) throw AssertionError("coordinate out of range: $value > $max") } diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt index 67dc4ee0b..0b74384b6 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt @@ -13,9 +13,9 @@ package de.bixilon.minosoft.data.world.positions -import de.bixilon.kutil.exception.Broken import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.text.formatting.TextFormattable +import de.bixilon.minosoft.data.world.positions.BlockPositionUtil.assertPosition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.util.KUtil.format @@ -23,53 +23,45 @@ import de.bixilon.minosoft.util.KUtil.format value class ChunkPosition( inline val raw: Long, ) : TextFormattable { - init { - TODO() - } constructor() : this(0, 0) - constructor(x: Int, z: Int) : this((z shl SHIFT_Z) or (x shl SHIFT_X)) { - assert(x >= 0) - assert(x <= ProtocolDefinition.SECTION_MAX_X) - assert(z >= 0) - assert(z <= ProtocolDefinition.SECTION_MAX_Z) + constructor(x: Int, z: Int) : this((z.toLong() shl SHIFT_Z) or (x.toLong() shl SHIFT_X)) { + assertPosition(x, -MAX_X, MAX_X) + assertPosition(z, -MAX_Z, MAX_Z) } - inline val x: Int get() = (raw and MASK_X) shr SHIFT_X - inline val z: Int get() = (raw and MASK_Z) shr SHIFT_Z - inline val xz: Int get() = (raw and MASK_Z or MASK_X) + inline val x: Int get() = (raw ushr SHIFT_X).toInt() and MASK_X + inline val z: Int get() = (raw ushr SHIFT_Z).toInt() and MASK_Z inline fun plusX(): ChunkPosition { - assert(this.x < ProtocolDefinition.SECTION_MAX_X) + assertPosition(this.x < MAX_X) return ChunkPosition(raw + X * 1) } inline fun plusX(x: Int): ChunkPosition { - assert(this.x + x < ProtocolDefinition.SECTION_MAX_X) - assert(this.x + x > 0) + assertPosition(this.x + x, -MAX_X, MAX_X) return ChunkPosition(raw + X * x) } inline fun minusX(): ChunkPosition { - assert(this.x > 0) + assert(this.x > -MAX_X) return ChunkPosition(raw - X * 1) } inline fun plusZ(): ChunkPosition { - assert(this.z < ProtocolDefinition.SECTION_MAX_Z) + assert(this.z < MAX_Z) return ChunkPosition(raw + Z * 1) } inline fun plusZ(z: Int): ChunkPosition { - assert(this.z + z < ProtocolDefinition.SECTION_MAX_Z) - assert(this.z + z > 0) + assertPosition(this.z + z, -MAX_Z, MAX_Z) return ChunkPosition(raw + Z * z) } inline fun minusZ(): ChunkPosition { - assert(this.z > 0) + assert(this.z > -MAX_Z) return ChunkPosition(raw - Z * 1) } @@ -98,28 +90,21 @@ value class ChunkPosition( override fun toString() = "c($x $z)" - val index: Int - get() = when { - x == -1 && z == -1 -> 0 - x == -1 && z == 0 -> 1 - x == -1 && z == 1 -> 2 - x == 0 && z == -1 -> 3 - x == 0 && z == 1 -> 4 - x == 1 && z == -1 -> 5 - x == 1 && z == 0 -> 6 - x == 1 && z == 1 -> 7 - else -> Broken("Can not get neighbour chunk from offset $this") - } - companion object { - const val MASK_X = 0x00F + const val BITS_X = 32 + const val MASK_X = (1 shl BITS_X) - 1 const val SHIFT_X = 0 - const val MASK_Z = 0x0F0 - const val SHIFT_Z = 4 + const val BITS_Z = 32 + const val MASK_Z = (1 shl BITS_Z) - 1 + const val SHIFT_Z = BITS_X - const val X = 1 shl SHIFT_X - const val Z = 1 shl SHIFT_Z + const val X = 1L shl SHIFT_X + const val Z = 1L shl SHIFT_Z + + + const val MAX_X = Int.MAX_VALUE + const val MAX_Z = Int.MAX_VALUE val EMPTY = ChunkPosition(0, 0) diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt index dff6dfe37..2fca2da8e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt @@ -28,16 +28,16 @@ value class InChunkPosition( constructor() : this(0, 0, 0) - constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) { + constructor(x: Int, y: Int, z: Int) : this(((y and 0xFFF) shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) { assertPosition(x, 0, ProtocolDefinition.SECTION_MAX_X) assertPosition(y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y) assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z) } - inline val x: Int get() = (index and MASK_X) shr SHIFT_X - inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y - inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z - inline val xz: Int get() = (index and MASK_Z or MASK_X) + inline val x: Int get() = (index shr SHIFT_X) and MASK_X + inline val y: Int get() = (index and (MASK_Y shl SHIFT_Y)) shl 20 shr 20 + inline val z: Int get() = (index shr SHIFT_Z) and MASK_Z + inline val xz: Int get() = index and ((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z)) inline fun plusX(): InChunkPosition { @@ -99,14 +99,17 @@ value class InChunkPosition( inline val sectionHeight get() = y.sectionHeight companion object { - const val MASK_X = 0x00F + const val BITS_X = 4 + const val MASK_X = (1 shl BITS_X) - 1 const val SHIFT_X = 0 - const val MASK_Z = 0x0F0 - const val SHIFT_Z = 4 + const val BITS_Z = 4 + const val MASK_Z = (1 shl BITS_Z) - 1 + const val SHIFT_Z = BITS_X - const val MASK_Y = 0xFFF00 - const val SHIFT_Y = 8 + const val BITS_Y = 12 + const val MASK_Y = (1 shl BITS_Y) - 1 + const val SHIFT_Y = BITS_X + BITS_Z const val X = 1 shl SHIFT_X const val Z = 1 shl SHIFT_Z diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt index c98e32ccb..9118868d6 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt @@ -32,11 +32,11 @@ value class InSectionPosition( assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z) } - inline val x: Int get() = (index and MASK_X) shr SHIFT_X - inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y - inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z + inline val x: Int get() = (index shr SHIFT_X) and MASK_X + inline val y: Int get() = (index shr SHIFT_Y) and MASK_Y + inline val z: Int get() = (index shr SHIFT_Z) and MASK_Z - inline val xz: Int get() = index and (MASK_X or MASK_Z) + inline val xz: Int get() = index and ((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z)) inline fun plusX(): InSectionPosition { @@ -95,14 +95,17 @@ value class InSectionPosition( override fun toString() = "s($x $y $z)" companion object { - const val MASK_X = 0x00F + const val BITS_X = 4 + const val MASK_X = (1 shl BITS_X) - 1 const val SHIFT_X = 0 - const val MASK_Z = 0x0F0 - const val SHIFT_Z = 4 + const val BITS_Z = 4 + const val MASK_Z = (1 shl BITS_Z) - 1 + const val SHIFT_Z = BITS_X - const val MASK_Y = 0xF00 - const val SHIFT_Y = 8 + const val BITS_Y = 4 + const val MASK_Y = (1 shl BITS_Y) - 1 + const val SHIFT_Y = BITS_X + BITS_Z const val X = 1 shl SHIFT_X const val Z = 1 shl SHIFT_Z diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/camera/frustum/Frustum.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/camera/frustum/Frustum.kt index da2f0865b..75dc4a6a8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/camera/frustum/Frustum.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/camera/frustum/Frustum.kt @@ -156,7 +156,7 @@ class Frustum( return containsRegion(min.x, min.y, min.z, max.x, max.y, max.z) } - fun containsChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = CHUNK_NIN_POSITION, maxPosition: InSectionPosition = ProtocolDefinition.CHUNK_SECTION_SIZE): Boolean { + fun containsChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = SECTION_MIN_POSITION, maxPosition: InSectionPosition = SECTION_MAX_POSITION): Boolean { val offset = camera.offset.offset val baseX = ((chunkPosition.x shl 4) - offset.x).toFloat() val baseY = ((sectionHeight shl 4) - offset.y).toFloat() @@ -215,7 +215,8 @@ class Frustum( } } - private companion object { - val CHUNK_NIN_POSITION = InSectionPosition(0, 0, 0) + companion object { + val SECTION_MIN_POSITION = InSectionPosition(0, 0, 0) + val SECTION_MAX_POSITION = InSectionPosition(ProtocolDefinition.SECTION_MAX_X, ProtocolDefinition.SECTION_MAX_Y, ProtocolDefinition.SECTION_MAX_Z) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/camera/visibility/WorldVisibilityGraph.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/camera/visibility/WorldVisibilityGraph.kt index 701057452..16c84ef71 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/camera/visibility/WorldVisibilityGraph.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/camera/visibility/WorldVisibilityGraph.kt @@ -21,7 +21,6 @@ import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.shapes.aabb.AABB import de.bixilon.minosoft.data.world.chunk.chunk.Chunk -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours import de.bixilon.minosoft.data.world.chunk.update.WorldUpdateEvent import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkUnloadUpdate @@ -31,10 +30,10 @@ import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.camera.Camera +import de.bixilon.minosoft.gui.rendering.camera.frustum.Frustum import de.bixilon.minosoft.gui.rendering.events.VisibilityGraphChangeEvent import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen import de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.ChunkUtil.isInViewDistance -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import it.unimi.dsi.fastutil.ints.IntOpenHashSet /** @@ -149,7 +148,7 @@ class WorldVisibilityGraph( return frustum.containsAABB(aabb) } - fun isSectionVisible(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = DEFAULT_MIN_POSITION, maxPosition: InSectionPosition = ProtocolDefinition.CHUNK_SECTION_SIZE, checkChunk: Boolean = true): Boolean { + fun isSectionVisible(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = Frustum.SECTION_MIN_POSITION, maxPosition: InSectionPosition = Frustum.SECTION_MIN_POSITION, checkChunk: Boolean = true): Boolean { if (checkChunk && !isChunkVisible(chunkPosition)) { return false } @@ -249,7 +248,7 @@ class WorldVisibilityGraph( if (directionX <= 0 && (section?.occlusion?.isOccluded(inverted, Directions.WEST) != true) && chunkPosition.x > chunkMin.x) { val next = chunkPosition.minusX() - val nextChunk = chunk.neighbours[ChunkNeighbours.WEST] + val nextChunk = chunk.neighbours[Directions.WEST] if (nextChunk != null) { val nextVisibilities = getVisibility(next) ?: return if (!nextVisibilities[visibilitySectionIndex]) { @@ -261,7 +260,7 @@ class WorldVisibilityGraph( if (directionX >= 0 && (section?.occlusion?.isOccluded(inverted, Directions.EAST) != true) && chunkPosition.x < chunkMax.x) { val next = chunkPosition.plusX() - val nextChunk = chunk.neighbours[ChunkNeighbours.EAST] + val nextChunk = chunk.neighbours[Directions.EAST] if (nextChunk != null) { val nextVisibilities = getVisibility(next) ?: return if (!nextVisibilities[visibilitySectionIndex]) { @@ -286,7 +285,7 @@ class WorldVisibilityGraph( if (directionZ <= 0 && (section?.occlusion?.isOccluded(inverted, Directions.NORTH) != true) && chunkPosition.z > chunkMin.z) { val next = chunkPosition.minusZ() - val nextChunk = chunk.neighbours[ChunkNeighbours.NORTH] + val nextChunk = chunk.neighbours[Directions.NORTH] if (nextChunk != null) { val nextVisibilities = getVisibility(next) ?: return if (!nextVisibilities[visibilitySectionIndex]) { @@ -298,7 +297,7 @@ class WorldVisibilityGraph( if (directionZ >= 0 && (section?.occlusion?.isOccluded(inverted, Directions.SOUTH) != true) && chunkPosition.z < chunkMax.z) { val next = chunkPosition.plusZ() - val nextChunk = chunk.neighbours[ChunkNeighbours.SOUTH] + val nextChunk = chunk.neighbours[Directions.SOUTH] if (nextChunk != null) { val nextVisibilities = getVisibility(next) ?: return if (!nextVisibilities[visibilitySectionIndex]) { @@ -371,8 +370,4 @@ class WorldVisibilityGraph( calculateGraph() } } - - companion object { - private val DEFAULT_MIN_POSITION = InSectionPosition.EMPTY - } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/ChunkMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/ChunkMesher.kt index e39d2fa6a..d5afcbc56 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/ChunkMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/ChunkMesher.kt @@ -32,15 +32,15 @@ class ChunkMesher( renderer.unload(item) return null } - val neighbours = item.chunk.neighbours.get() - if (neighbours == null) { + val neighbours = item.chunk.neighbours + if (!neighbours.complete) { renderer.unload(item) return null } - val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours, item.chunk, item.section.sectionHeight) + val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours.neighbours, item.chunk, item.section.sectionHeight) val mesh = ChunkMeshes(renderer.context, item.chunkPosition, item.sectionHeight, item.section.smallMesh) try { - solid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, neighbours, sectionNeighbours, mesh) + solid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, neighbours.neighbours, sectionNeighbours, mesh) if (item.section.blocks.hasFluid) { fluid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, mesh) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt index 978d5049a..cfb66729e 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt @@ -254,7 +254,7 @@ class FluidSectionMesher( for (side in 0 until 4) { val now = BlockPosition(x = position.x - (side and 0x01), y = position.y, z = position.z - (side shr 1 and 0x01)) val offset = position.chunkPosition - providedChunk.position - val chunk = neighbours[offset] + val chunk = neighbours[offset] ?: continue val inChunk = now.inChunkPosition diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt index 14722ea90..7cf00306f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt @@ -30,7 +30,7 @@ import de.bixilon.minosoft.data.registries.blocks.types.properties.offset.Offset import de.bixilon.minosoft.data.world.chunk.ChunkSection import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.SectionLight -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours +import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition @@ -59,7 +59,7 @@ class SolidSectionMesher( profile.light::ambientOcclusion.observe(this, true) { this.ambientOcclusion = it } } - fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: Array, neighbours: Array, mesh: ChunkMeshes) { + fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: ChunkNeighbourArray, neighbours: Array, mesh: ChunkMeshes) { val random = if (profile.antiMoirePattern) Random(0L) else null @@ -188,13 +188,13 @@ class SolidSectionMesher( } } - private inline fun setZ(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun setZ(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, neighbourChunks: ChunkNeighbourArray, section: ChunkSection, chunk: Chunk) { if (position.z == 0) { - setNeighbour(neighbourBlocks, position.with(z = ProtocolDefinition.SECTION_MAX_Z), light, neighbours[O_NORTH], neighbourChunks[ChunkNeighbours.NORTH], O_NORTH) + setNeighbour(neighbourBlocks, position.with(z = ProtocolDefinition.SECTION_MAX_Z), light, neighbours[O_NORTH], neighbourChunks[Directions.NORTH]!!, O_NORTH) // TODO: bad assert setNeighbour(neighbourBlocks, position.plusZ(), light, section, chunk, O_SOUTH) } else if (position.z == ProtocolDefinition.SECTION_MAX_Z) { setNeighbour(neighbourBlocks, position.minusZ(), light, section, chunk, O_NORTH) - setNeighbour(neighbourBlocks, position.with(z = 0), light, neighbours[O_SOUTH], neighbourChunks[ChunkNeighbours.SOUTH], O_SOUTH) + setNeighbour(neighbourBlocks, position.with(z = 0), light, neighbours[O_SOUTH], neighbourChunks[Directions.SOUTH]!!, O_SOUTH)// TODO: bad assert } else { setNeighbour(neighbourBlocks, position.minusZ(), light, section, chunk, O_NORTH) setNeighbour(neighbourBlocks, position.plusZ(), light, section, chunk, O_SOUTH) @@ -202,12 +202,12 @@ class SolidSectionMesher( } - private inline fun setX(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun setX(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, neighbourChunks: ChunkNeighbourArray, section: ChunkSection, chunk: Chunk) { if (position.x == 0) { - setNeighbour(neighbourBlocks, position.with(x = ProtocolDefinition.SECTION_MAX_X), light, neighbours[O_WEST], neighbourChunks[ChunkNeighbours.WEST], O_WEST) + setNeighbour(neighbourBlocks, position.with(x = ProtocolDefinition.SECTION_MAX_X), light, neighbours[O_WEST], neighbourChunks[Directions.WEST]!!, O_WEST) // TODO: bad assert setNeighbour(neighbourBlocks, position.plusX(), light, section, chunk, O_EAST) } else if (position.x == ProtocolDefinition.SECTION_MAX_X) { - setNeighbour(neighbourBlocks, position.with(x = 0), light, neighbours[O_EAST], neighbourChunks[ChunkNeighbours.EAST], O_EAST) + setNeighbour(neighbourBlocks, position.with(x = 0), light, neighbours[O_EAST], neighbourChunks[Directions.EAST]!!, O_EAST) // TODO: bad assert setNeighbour(neighbourBlocks, position.minusX(), light, section, chunk, O_WEST) } else { setNeighbour(neighbourBlocks, position.minusX(), light, section, chunk, O_WEST) diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt index 03e1c3136..57865c021 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt @@ -15,6 +15,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.minosoft.config.StaticConfiguration +import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.dimension.DimensionProperties @@ -24,7 +25,7 @@ import de.bixilon.minosoft.data.world.chunk.ChunkSection import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.ChunkPrototype import de.bixilon.minosoft.data.world.chunk.light.LightArray -import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours +import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray import de.bixilon.minosoft.data.world.container.palette.PalettedContainerReader import de.bixilon.minosoft.data.world.container.palette.palettes.BiomePaletteFactory import de.bixilon.minosoft.data.world.container.palette.palettes.BlockStatePaletteFactory @@ -226,80 +227,18 @@ object ChunkUtil { return XZBiomeArray(biomes) } - val Array.fullyLoaded: Boolean - get() { - for (neighbour in this) { - if (neighbour == null) return false - if (neighbour.neighbours.complete) { - continue - } - return false - } - return true - } - - - fun getChunkNeighbourPositions(chunkPosition: ChunkPosition): Array { - return arrayOf( - chunkPosition + ChunkNeighbours.OFFSETS[0], - chunkPosition + ChunkNeighbours.OFFSETS[1], - chunkPosition + ChunkNeighbours.OFFSETS[2], - chunkPosition + ChunkNeighbours.OFFSETS[3], - chunkPosition + ChunkNeighbours.OFFSETS[4], - chunkPosition + ChunkNeighbours.OFFSETS[5], - chunkPosition + ChunkNeighbours.OFFSETS[6], - chunkPosition + ChunkNeighbours.OFFSETS[7], - ) - } /** * @param neighbourChunks: **Fully loaded** direct neighbour chunks */ - fun getDirectNeighbours(neighbourChunks: Array, chunk: Chunk, sectionHeight: SectionHeight): Array { + fun getDirectNeighbours(neighbourChunks: ChunkNeighbourArray, chunk: Chunk, sectionHeight: SectionHeight): Array { return arrayOf( chunk[sectionHeight - 1], chunk[sectionHeight + 1], - neighbourChunks[3][sectionHeight], - neighbourChunks[4][sectionHeight], - neighbourChunks[1][sectionHeight], - neighbourChunks[6][sectionHeight], - ) - } - - /** - * @param neighbourChunks: **Fully loaded** direct neighbour chunks - */ - fun getAllNeighbours(neighbourChunks: Array, chunk: Chunk, sectionHeight: SectionHeight): Array { - return arrayOf( - neighbourChunks[0][sectionHeight - 1], // 0, (-1 | -1) - neighbourChunks[1][sectionHeight - 1], // 1, (-1 | +0) - neighbourChunks[2][sectionHeight - 1], // 2, (-1 | +1) - neighbourChunks[3][sectionHeight - 1], // 3, (+0 | -1) - chunk[sectionHeight - 1], // 4, (+0 | +0) - neighbourChunks[4][sectionHeight - 1], // 5, (+0 | +1) - neighbourChunks[5][sectionHeight - 1], // 6, (-1 | -1) - neighbourChunks[6][sectionHeight - 1], // 7, (-1 | -1) - neighbourChunks[7][sectionHeight - 1], // 8, (-1 | -1) - - neighbourChunks[0][sectionHeight + 0], // 9, (-1 | -1) - neighbourChunks[1][sectionHeight + 0], // 10, (-1 | +0) - neighbourChunks[2][sectionHeight + 0], // 11, (-1 | +1) - neighbourChunks[3][sectionHeight + 0], // 12, (+0 | -1) - chunk[sectionHeight + 0], // 13, (+0 | +0) - neighbourChunks[4][sectionHeight + 0], // 14, (+0 | +1) - neighbourChunks[5][sectionHeight + 0], // 15, (+1 | -1) - neighbourChunks[6][sectionHeight + 0], // 16, (+1 | +0) - neighbourChunks[7][sectionHeight + 0], // 17, (+1 | +1) - - neighbourChunks[0][sectionHeight + 1], // 18, (-1 | -1) - neighbourChunks[1][sectionHeight + 1], // 19, (-1 | +0) - neighbourChunks[2][sectionHeight + 1], // 20, (-1 | +1) - neighbourChunks[3][sectionHeight + 1], // 21, (+0 | -1) - chunk[sectionHeight + 1], // 22, (+0 | +0) - neighbourChunks[4][sectionHeight + 1], // 23, (+0 | +1) - neighbourChunks[5][sectionHeight + 1], // 24, (+1 | -1) - neighbourChunks[6][sectionHeight + 1], // 25, (+1 | +0) - neighbourChunks[7][sectionHeight + 1], // 26, (+1 | +1) + neighbourChunks[Directions.NORTH]!![sectionHeight], + neighbourChunks[Directions.SOUTH]!![sectionHeight], + neighbourChunks[Directions.WEST]!![sectionHeight], + neighbourChunks[Directions.EAST]!![sectionHeight], ) } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java index 38996c22f..49e93c825 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java @@ -15,7 +15,6 @@ package de.bixilon.minosoft.protocol.protocol; import de.bixilon.minosoft.data.text.formatting.color.ChatColors; import de.bixilon.minosoft.data.text.formatting.color.RGBColor; -import de.bixilon.minosoft.data.world.positions.InSectionPosition; import java.util.regex.Pattern; @@ -48,8 +47,6 @@ public final class ProtocolDefinition { public static final int SECTION_HEIGHT_Y = SECTION_LENGTH; public static final int SECTION_MAX_Y = SECTION_HEIGHT_Y - 1; public static final int BLOCKS_PER_SECTION = SECTION_WIDTH_X * SECTION_HEIGHT_Y * SECTION_WIDTH_X; - @Deprecated - public static final InSectionPosition CHUNK_SECTION_SIZE = new InSectionPosition(SECTION_WIDTH_X, SECTION_HEIGHT_Y, SECTION_WIDTH_Z); public static final int CHUNK_MIN_Y = -2048; public static final int CHUNK_MIN_SECTION = CHUNK_MIN_Y / SECTION_HEIGHT_Y; diff --git a/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt b/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt index 325a0a50f..05d84fa66 100644 --- a/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2025 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. * @@ -13,7 +13,7 @@ package de.bixilon.minosoft.data.registries.shapes.aabb -import de.bixilon.kotlinglm.vec3.Vec3i +import de.bixilon.minosoft.data.world.positions.BlockPosition import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -33,7 +33,7 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(1, positions.size) assertTrue(positions.hasNext()) - assertEquals(Vec3i(0, 0, 0), positions.next()) + assertEquals(BlockPosition(0, 0, 0), positions.next()) assertFalse(positions.hasNext()) } @@ -43,7 +43,7 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(24, positions.size) - val set: MutableSet = mutableSetOf() + val set: MutableSet = mutableSetOf() for (position in positions) { set += position @@ -51,30 +51,30 @@ class AABBIteratorTest { assertEquals(24, set.size) assertEquals(setOf( - Vec3i(0, 0, 0), - Vec3i(0, 0, 1), - Vec3i(0, 0, 2), - Vec3i(0, 0, 3), - Vec3i(0, 1, 0), - Vec3i(0, 1, 1), - Vec3i(0, 1, 2), - Vec3i(0, 1, 3), - Vec3i(0, 2, 0), - Vec3i(0, 2, 1), - Vec3i(0, 2, 2), - Vec3i(0, 2, 3), - Vec3i(1, 0, 0), - Vec3i(1, 0, 1), - Vec3i(1, 0, 2), - Vec3i(1, 0, 3), - Vec3i(1, 1, 0), - Vec3i(1, 1, 1), - Vec3i(1, 1, 2), - Vec3i(1, 1, 3), - Vec3i(1, 2, 0), - Vec3i(1, 2, 1), - Vec3i(1, 2, 2), - Vec3i(1, 2, 3), + BlockPosition(0, 0, 0), + BlockPosition(0, 0, 1), + BlockPosition(0, 0, 2), + BlockPosition(0, 0, 3), + BlockPosition(0, 1, 0), + BlockPosition(0, 1, 1), + BlockPosition(0, 1, 2), + BlockPosition(0, 1, 3), + BlockPosition(0, 2, 0), + BlockPosition(0, 2, 1), + BlockPosition(0, 2, 2), + BlockPosition(0, 2, 3), + BlockPosition(1, 0, 0), + BlockPosition(1, 0, 1), + BlockPosition(1, 0, 2), + BlockPosition(1, 0, 3), + BlockPosition(1, 1, 0), + BlockPosition(1, 1, 1), + BlockPosition(1, 1, 2), + BlockPosition(1, 1, 3), + BlockPosition(1, 2, 0), + BlockPosition(1, 2, 1), + BlockPosition(1, 2, 2), + BlockPosition(1, 2, 3), ), set) } @@ -85,7 +85,7 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(1, positions.size) assertTrue(positions.hasNext()) - assertEquals(Vec3i(0, 0, 0), positions.next()) + assertEquals(BlockPosition(0, 0, 0), positions.next()) assertFalse(positions.hasNext()) } @@ -96,9 +96,9 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(2, positions.size) assertTrue(positions.hasNext()) - assertEquals(Vec3i(0, 0, 0), positions.next()) + assertEquals(BlockPosition(0, 0, 0), positions.next()) assertTrue(positions.hasNext()) - assertEquals(Vec3i(1, 0, 0), positions.next()) + assertEquals(BlockPosition(1, 0, 0), positions.next()) assertFalse(positions.hasNext()) } @@ -111,7 +111,7 @@ class AABBIteratorTest { for (x in -2 until 2) { for (y in -2 until 2) { for (z in -2 until 2) { - assertEquals(Vec3i(x, y, z), positions.next()) + assertEquals(BlockPosition(x, y, z), positions.next()) } } } diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt index 1c2557687..aa1c032b6 100644 --- a/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt @@ -22,100 +22,106 @@ class InChunkPositionTest { @Test fun `init correct min`() { - val position = InSectionPosition(0, -2048, 0) + val position = InChunkPosition(0, -2048, 0) } @Test fun `init correct max`() { - val position = InSectionPosition(15, 2047, 15) + val position = InChunkPosition(15, 2047, 15) } @Test fun `init badly`() { - assertThrows { InSectionPosition(-1, -5000, -1) } + assertThrows { InChunkPosition(-1, -5000, -1) } } @Test fun `correct x`() { - val position = InSectionPosition(2, 0xF, 0xF) + val position = InChunkPosition(2, 0xF, 0xF) assertEquals(position.x, 2) } @Test fun `correct plus x`() { - val position = InSectionPosition(2, 0xF, 0xF) + val position = InChunkPosition(2, 0xF, 0xF) assertEquals(position.plusX().x, 3) } @Test fun `correct plus 2 x`() { - val position = InSectionPosition(2, 0xF, 0xF) + val position = InChunkPosition(2, 0xF, 0xF) assertEquals(position.plusX(2).x, 4) } @Test fun `correct minus x`() { - val position = InSectionPosition(2, 0xF, 0xF) + val position = InChunkPosition(2, 0xF, 0xF) assertEquals(position.minusX().x, 1) } @Test fun `correct y`() { - val position = InSectionPosition(0xF, 3, 0xF) + val position = InChunkPosition(0xF, 3, 0xF) assertEquals(position.y, 3) } @Test fun `correct negative y`() { - val position = InSectionPosition(0xF, -1000, 0xF) + val position = InChunkPosition(0xF, -1000, 0xF) assertEquals(position.y, -1000) } @Test fun `correct positive y`() { - val position = InSectionPosition(0xF, 1000, 0xF) + val position = InChunkPosition(0xF, 1000, 0xF) assertEquals(position.y, 1000) } @Test fun `correct plus y`() { - val position = InSectionPosition(0xF, 2, 0xF) + val position = InChunkPosition(0xF, 2, 0xF) assertEquals(position.plusY().y, 3) } @Test fun `correct plus 2 y`() { - val position = InSectionPosition(0xF, 2, 0xF) + val position = InChunkPosition(0xF, 2, 0xF) assertEquals(position.plusY(2).y, 4) } @Test fun `correct minus y`() { - val position = InSectionPosition(0xF, 2, 0xF) + val position = InChunkPosition(0xF, 2, 0xF) assertEquals(position.minusY().y, 1) } + @Test + fun `correct minus y 2`() { + val position = InChunkPosition(0xF, -3, 0xF) + assertEquals(position.minusY().y, -4) + } + @Test fun `correct z`() { - val position = InSectionPosition(0xF, 0xF, 4) + val position = InChunkPosition(0xF, 0xF, 4) assertEquals(position.z, 4) } @Test fun `correct plus z`() { - val position = InSectionPosition(0xF, 0xF, 2) + val position = InChunkPosition(0xF, 0xF, 2) assertEquals(position.plusZ().z, 3) } @Test fun `correct plus 2 z`() { - val position = InSectionPosition(0xF, 0xF, 2) + val position = InChunkPosition(0xF, 0xF, 2) assertEquals(position.plusZ(2).z, 4) } @Test fun `correct minus z`() { - val position = InSectionPosition(0xF, 0xF, 2) + val position = InChunkPosition(0xF, 0xF, 2) assertEquals(position.minusZ().z, 1) } }