fix heightmap calculation, calculate skylight

This commit is contained in:
Bixilon 2022-09-18 16:53:00 +02:00
parent 958c19b4d2
commit c537e674a0
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 88 additions and 61 deletions

View File

@ -132,8 +132,9 @@ Support for macOS is a delicate topic. Let's say it works for now, but it is not
5. Build and run Minosoft with `mvn clean verify exec:java`. If any errors occur, feel free to open an issue. In this early stage it might be helpful to delete its configuration files 5. Build and run Minosoft with `mvn clean verify exec:java`. If any errors occur, feel free to open an issue. In this early stage it might be helpful to delete its configuration files
6. (Optional) Build a fat jar with `mvn package`. You'll find the jar with all dependencies in `target/`. Then you don't need to recompile everytime 6. (Optional) Build a fat jar with `mvn package`. You'll find the jar with all dependencies in `target/`. Then you don't need to recompile everytime
Using IntelliJ IDEA for building or developing is strongly recommended. There you have features Using IntelliJ IDEA for building or developing is strongly recommended. There you have features like build caching.
like build caching. You might need to increase heap memory for the compiler (`File` -> `Settings` -> `Build, Execution and Compiler` -> `Compiler` -> `Shared build process heap size`). If you run into errors, please ensure you have the latest version of it.
You might need to increase heap memory for the compiler (`File` -> `Settings` -> `Build, Execution and Compiler` -> `Compiler` -> `Shared build process heap size`).
Allow at least 1500 MBytes. Allow at least 1500 MBytes.
## Code mirrors ## Code mirrors

View File

@ -19,12 +19,9 @@ import de.bixilon.minosoft.data.registries.VoxelShape
import de.bixilon.minosoft.data.registries.blocks.cube.CubeDirections import de.bixilon.minosoft.data.registries.blocks.cube.CubeDirections
class DirectedProperty(private val directions: BooleanArray) : LightProperties { class DirectedProperty(private val directions: BooleanArray) : LightProperties {
override val propagatesSkylight: Boolean = propagatesLight(Directions.UP, Directions.DOWN)
override fun propagatesSkylight(from: Directions, to: Directions): Boolean { override fun propagatesLight(from: Directions, to: Directions): Boolean {
return directions[CubeDirections.getIndex(from, to)]
}
override fun propagatesBlockLight(from: Directions, to: Directions): Boolean {
return directions[CubeDirections.getIndex(from, to)] return directions[CubeDirections.getIndex(from, to)]
} }

View File

@ -16,10 +16,9 @@ package de.bixilon.minosoft.data.registries.blocks.light
import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.direction.Directions
interface LightProperties { interface LightProperties {
val propagatesBlockLight: Boolean get() = false val propagatesLight: Boolean get() = false
val propagatesSkylight: Boolean get() = false val propagatesSkylight: Boolean get() = false
fun propagatesSkylight(from: Directions, to: Directions): Boolean = propagatesSkylight fun propagatesLight(from: Directions, to: Directions): Boolean = propagatesLight
fun propagatesBlockLight(from: Directions, to: Directions): Boolean = propagatesBlockLight
} }

View File

@ -14,7 +14,7 @@
package de.bixilon.minosoft.data.registries.blocks.light package de.bixilon.minosoft.data.registries.blocks.light
object TransparentProperty : LightProperties { object TransparentProperty : LightProperties {
override val propagatesBlockLight: Boolean override val propagatesLight: Boolean
get() = true get() = true
override val propagatesSkylight: Boolean override val propagatesSkylight: Boolean
get() = true get() = true

View File

@ -58,7 +58,7 @@ class Chunk(
var blocksInitialized = false // All block data was received var blocksInitialized = false // All block data was received
var biomesInitialized = false // All biome data is initialized (aka. cache built, or similar) var biomesInitialized = false // All biome data is initialized (aka. cache built, or similar)
val heightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z) val skylightHeightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z)
var neighbours: Array<Chunk>? = null var neighbours: Array<Chunk>? = null
@Synchronized set(value) { @Synchronized set(value) {
@ -304,7 +304,7 @@ class Chunk(
return topLight[index].toInt() or 0xF0 // top has always sky=15 return topLight[index].toInt() or 0xF0 // top has always sky=15
} }
var light = this[sectionHeight]?.light?.get(index)?.toInt() ?: 0x00 var light = this[sectionHeight]?.light?.get(index)?.toInt() ?: 0x00
if (y >= heightmap[heightmapIndex]) { if (y >= skylightHeightmap[heightmapIndex]) {
// set sky=15 // set sky=15
light = light or 0xF0 light = light or 0xF0
} }
@ -435,7 +435,7 @@ class Chunk(
val maxY = highestSection * ProtocolDefinition.SECTION_HEIGHT_Y val maxY = highestSection * ProtocolDefinition.SECTION_HEIGHT_Y
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
z@ for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
checkHeightmapY(x, maxY, z) checkHeightmapY(x, maxY, z)
} }
} }
@ -444,37 +444,31 @@ class Chunk(
private fun checkHeightmapY(x: Int, startY: Int, z: Int) { private fun checkHeightmapY(x: Int, startY: Int, z: Int) {
val minY = lowestSection * ProtocolDefinition.SECTION_HEIGHT_Y val minY = lowestSection * ProtocolDefinition.SECTION_HEIGHT_Y
var y = startY
var sectionHeight = y.sectionHeight val sections = sections ?: return
var section: ChunkSection? = this[sectionHeight]
while (y > minY) { var y = minY
val nextSectionHeight = y.sectionHeight
if (sectionHeight != nextSectionHeight) { sectionLoop@ for (sectionIndex in (sections.size - 1) downTo 0) {
sectionHeight = nextSectionHeight val section = sections[sectionIndex] ?: continue
section = this[sectionHeight]
} for (sectionY in ProtocolDefinition.SECTION_MAX_Y downTo 0) {
if (section == null) { val light = section.blocks[x, sectionY, z]?.lightProperties ?: continue
y -= ProtocolDefinition.SECTION_HEIGHT_Y if (light.propagatesSkylight) {
continue continue
} }
val block = section.blocks[x, y.inSectionHeight, z] y = (sectionIndex + lowestSection) * ProtocolDefinition.SECTION_HEIGHT_Y + sectionY
if (block == null || !block.isSolid) { break@sectionLoop
y--
continue
} }
heightmap[(z shl 4) or x] = y
return
} }
heightmap[(z shl 4) or x] = minY skylightHeightmap[(z shl 4) or x] = y
} }
@Synchronized @Synchronized
private fun updateHeightmap(x: Int, y: Int, z: Int, place: Boolean) { private fun updateHeightmap(x: Int, y: Int, z: Int, place: Boolean) {
val index = (z shl 4) or x val index = (z shl 4) or x
val current = heightmap[index] val current = skylightHeightmap[index]
if (current > y) { if (current > y) {
// our block is/was not the highest, ignore everything // our block is/was not the highest, ignore everything
@ -483,7 +477,7 @@ class Chunk(
if (current < y) { if (current < y) {
if (place) { if (place) {
// we are the highest block now // we are the highest block now
heightmap[index] = y skylightHeightmap[index] = y
} }
return return
} }
@ -497,41 +491,73 @@ class Chunk(
} }
private fun recalculateSkylight() { private fun recalculateSkylight() {
println("Chunk: $chunkPosition")
if (world.dimension?.hasSkyLight != true) { if (world.dimension?.hasSkyLight != true) {
// no need to calculate it // no need to calculate it
return return
} }
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
calculateSkylight(x, z) traceSkylightDown(x, z)
}
}
for (x in 1 until ProtocolDefinition.SECTION_WIDTH_X - 1) {
for (z in 1 until ProtocolDefinition.SECTION_WIDTH_Z - 1) {
startSkylightFloodFill(x, z)
} }
} }
} }
private fun calculateSkylight(x: Int, z: Int) { private fun traceSkylightDown(x: Int, z: Int) {
val heightmapIndex = (z shl 4) or x val heightmapIndex = (z shl 4) or x
val maxHeight = heightmap[heightmapIndex] val maxHeight = skylightHeightmap[heightmapIndex]
// ToDo: only update changed ones // ToDo: only update changed ones
for (sectionHeight in highestSection - 1 downTo maxHeight.sectionHeight + 1) { for (sectionHeight in highestSection - 1 downTo maxHeight.sectionHeight + 1) {
val section = sections?.get(sectionHeight - lowestSection) ?: continue val section = sections?.get(sectionHeight - lowestSection) ?: continue
section.light.update = true section.light.update = true
} }
val maxSection = sections?.get(maxHeight.sectionHeight - lowestSection) val maxSection = sections?.get(maxHeight.sectionHeight - lowestSection)
if (maxSection != null) { if (maxSection != null) {
for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight + 2) { for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight) {
val index = (y shl 8) or heightmapIndex val index = (y shl 8) or heightmapIndex
val block = maxSection.blocks[index]?.lightProperties val block = maxSection.blocks[index]?.lightProperties
if (block != null && (!block.propagatesSkylight || block.propagatesSkylight(Directions.UP, Directions.DOWN))) { if (block != null && !block.propagatesSkylight) {
continue continue
} }
maxSection.light.light[index] = (maxSection.light.light[index].toInt() and 0x0F or 0xF0).toByte() maxSection.light.light[index] = (maxSection.light.light[index].toInt() and 0x0F or 0xF0).toByte()
} }
maxSection.light.update = true maxSection.light.update = true
// ToDo: This must run for every block
maxSection.light.traceSkylight(x, maxHeight.inSectionHeight + 1, z, ProtocolDefinition.LIGHT_LEVELS - 1, Directions.UP, true)
} }
} }
private fun startSkylightFloodFill(x: Int, z: Int) {
val heightmapIndex = (z shl 4) or x
val maxHeight = skylightHeightmap[heightmapIndex]
for (sectionHeight in highestSection - 1 downTo maxHeight.sectionHeight + 1) {
val section = sections?.get(sectionHeight - lowestSection) ?: continue
section.light.update = true
for (y in ProtocolDefinition.SECTION_MAX_Y downTo 0) {
section.light.traceSkylight(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL.toInt(), Directions.UP, true)
}
}
val maxSection = sections?.get(maxHeight.sectionHeight - lowestSection)
if (maxSection != null) {
for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight) {
val index = (y shl 8) or heightmapIndex
val block = maxSection.blocks[index]?.lightProperties
if (block != null && !block.propagatesSkylight) {
continue
}
maxSection.light.traceSkylight(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL.toInt(), Directions.UP, true)
}
maxSection.light.update = true
}
}
} }

View File

@ -29,7 +29,7 @@ class SectionLight(
val previousLuminance = previous?.luminance ?: 0 val previousLuminance = previous?.luminance ?: 0
val luminance = now?.luminance ?: 0 val luminance = now?.luminance ?: 0
if (previousLuminance == luminance && previous?.lightProperties?.propagatesBlockLight == now?.lightProperties?.propagatesBlockLight && previous?.lightProperties?.propagatesSkylight == now?.lightProperties?.propagatesSkylight) { if (previousLuminance == luminance && previous?.lightProperties?.propagatesLight == now?.lightProperties?.propagatesLight && previous?.lightProperties?.propagatesSkylight == now?.lightProperties?.propagatesSkylight) {
// no change for light data // no change for light data
return return
} }
@ -91,7 +91,7 @@ class SectionLight(
val index = getIndex(x, y, z) val index = getIndex(x, y, z)
val block = section.blocks.unsafeGet(index) val block = section.blocks.unsafeGet(index)
val blockLuminance = block?.luminance ?: 0 val blockLuminance = block?.luminance ?: 0
if (block != null && !block.lightProperties.propagatesBlockLight && blockLuminance == 0) { if (block != null && !block.lightProperties.propagatesLight && blockLuminance == 0) {
// light can not pass through the block // light can not pass through the block
return return
} }
@ -128,7 +128,7 @@ class SectionLight(
val neighbourLuminance = nextLuminance - 1 val neighbourLuminance = nextLuminance - 1
if (source == null || (source != Directions.DOWN && block?.lightProperties?.propagatesBlockLight(source, Directions.DOWN) != false)) { if (source == null || (source != Directions.DOWN && block?.lightProperties?.propagatesLight(source, Directions.DOWN) != false)) {
if (y > 0) { if (y > 0) {
traceIncrease(x, y - 1, z, neighbourLuminance, Directions.UP) traceIncrease(x, y - 1, z, neighbourLuminance, Directions.UP)
} else if (section.sectionHeight == section.chunk?.lowestSection) { } else if (section.sectionHeight == section.chunk?.lowestSection) {
@ -137,7 +137,7 @@ class SectionLight(
(neighbours[Directions.O_DOWN] ?: section.chunk?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, neighbourLuminance, Directions.UP) (neighbours[Directions.O_DOWN] ?: section.chunk?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, neighbourLuminance, Directions.UP)
} }
} }
if (source == null || (source != Directions.UP && block?.lightProperties?.propagatesBlockLight(source, Directions.UP) != false)) { if (source == null || (source != Directions.UP && block?.lightProperties?.propagatesLight(source, Directions.UP) != false)) {
if (y < ProtocolDefinition.SECTION_MAX_Y) { if (y < ProtocolDefinition.SECTION_MAX_Y) {
traceIncrease(x, y + 1, z, neighbourLuminance, Directions.DOWN) traceIncrease(x, y + 1, z, neighbourLuminance, Directions.DOWN)
} else if (section.sectionHeight == section.chunk?.highestSection) { } else if (section.sectionHeight == section.chunk?.highestSection) {
@ -147,28 +147,28 @@ class SectionLight(
} }
} }
if (source == null || (source != Directions.NORTH && block?.lightProperties?.propagatesBlockLight(source, Directions.NORTH) != false)) { if (source == null || (source != Directions.NORTH && block?.lightProperties?.propagatesLight(source, Directions.NORTH) != false)) {
if (z > 0) { if (z > 0) {
traceIncrease(x, y, z - 1, neighbourLuminance, Directions.SOUTH) traceIncrease(x, y, z - 1, neighbourLuminance, Directions.SOUTH)
} else { } else {
(neighbours[Directions.O_NORTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.NORTH)?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance, Directions.SOUTH) (neighbours[Directions.O_NORTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.NORTH)?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance, Directions.SOUTH)
} }
} }
if (source == null || (source != Directions.SOUTH && block?.lightProperties?.propagatesBlockLight(source, Directions.SOUTH) != false)) { if (source == null || (source != Directions.SOUTH && block?.lightProperties?.propagatesLight(source, Directions.SOUTH) != false)) {
if (z < ProtocolDefinition.SECTION_MAX_Y) { if (z < ProtocolDefinition.SECTION_MAX_Y) {
traceIncrease(x, y, z + 1, neighbourLuminance, Directions.NORTH) traceIncrease(x, y, z + 1, neighbourLuminance, Directions.NORTH)
} else { } else {
(neighbours[Directions.O_SOUTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.SOUTH)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(x, y, 0, neighbourLuminance, Directions.NORTH) (neighbours[Directions.O_SOUTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.SOUTH)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(x, y, 0, neighbourLuminance, Directions.NORTH)
} }
} }
if (source == null || (source != Directions.WEST && block?.lightProperties?.propagatesBlockLight(source, Directions.WEST) != false)) { if (source == null || (source != Directions.WEST && block?.lightProperties?.propagatesLight(source, Directions.WEST) != false)) {
if (x > 0) { if (x > 0) {
traceIncrease(x - 1, y, z, neighbourLuminance, Directions.EAST) traceIncrease(x - 1, y, z, neighbourLuminance, Directions.EAST)
} else { } else {
(neighbours[Directions.O_WEST] ?: section.chunk?.neighbours?.get(ChunkNeighbours.WEST)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, neighbourLuminance, Directions.EAST) (neighbours[Directions.O_WEST] ?: section.chunk?.neighbours?.get(ChunkNeighbours.WEST)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, neighbourLuminance, Directions.EAST)
} }
} }
if (source == null || (source != Directions.EAST && block?.lightProperties?.propagatesBlockLight(source, Directions.EAST) != false)) { if (source == null || (source != Directions.EAST && block?.lightProperties?.propagatesLight(source, Directions.EAST) != false)) {
if (x < ProtocolDefinition.SECTION_MAX_X) { if (x < ProtocolDefinition.SECTION_MAX_X) {
traceIncrease(x + 1, y, z, neighbourLuminance, Directions.WEST) traceIncrease(x + 1, y, z, neighbourLuminance, Directions.WEST)
} else { } else {
@ -248,29 +248,29 @@ class SectionLight(
val light = section.blocks[index]?.lightProperties val light = section.blocks[index]?.lightProperties
if (light != null && !light.propagatesSkylight) { if (light != null && !light.propagatesLight) {
return return
} }
this.light[index] = ((currentLight and BLOCK_LIGHT_MASK) or (nextLevel shl 4)).toByte() this.light[index] = ((currentLight and BLOCK_LIGHT_MASK) or (nextLevel shl 4)).toByte()
val nextNeighbourLevel = nextLevel - 1 val nextNeighbourLevel = nextLevel - 1
// ToDo: Neighbours // ToDo: Neighbours
if (y > 0 && (light == null || light.propagatesSkylight(direction, Directions.DOWN))) { if (y > 0 && (light == null || light.propagatesLight(direction, Directions.DOWN))) {
traceSkylight(x, y - 1, z, nextNeighbourLevel, Directions.DOWN, false) traceSkylight(x, y - 1, z, nextNeighbourLevel, Directions.DOWN, false)
} }
if (y < ProtocolDefinition.SECTION_MAX_Y && (light == null || light.propagatesSkylight(direction, Directions.UP))) { if (y < ProtocolDefinition.SECTION_MAX_Y && (light == null || light.propagatesLight(direction, Directions.UP))) {
traceSkylight(x, y + 1, z, nextNeighbourLevel, Directions.UP, false) traceSkylight(x, y + 1, z, nextNeighbourLevel, Directions.UP, false)
} }
if (z > 0 && (light == null || light.propagatesSkylight(direction, Directions.NORTH))) { if (z > 0 && (light == null || light.propagatesLight(direction, Directions.NORTH))) {
traceSkylight(x, y, z - 1, nextNeighbourLevel, Directions.NORTH, false) traceSkylight(x, y, z - 1, nextNeighbourLevel, Directions.NORTH, false)
} }
if (z < ProtocolDefinition.SECTION_MAX_Z && (light == null || light.propagatesSkylight(direction, Directions.SOUTH))) { if (z < ProtocolDefinition.SECTION_MAX_Z && (light == null || light.propagatesLight(direction, Directions.SOUTH))) {
traceSkylight(x, y, z + 1, nextNeighbourLevel, Directions.SOUTH, false) traceSkylight(x, y, z + 1, nextNeighbourLevel, Directions.SOUTH, false)
} }
if (x > 0 && (light == null || light.propagatesSkylight(direction, Directions.WEST))) { if (x > 0 && (light == null || light.propagatesLight(direction, Directions.WEST))) {
traceSkylight(x - 1, y, z, nextNeighbourLevel, Directions.WEST, false) traceSkylight(x - 1, y, z, nextNeighbourLevel, Directions.WEST, false)
} }
if (x < ProtocolDefinition.SECTION_MAX_X && (light == null || light.propagatesSkylight(direction, Directions.EAST))) { if (x < ProtocolDefinition.SECTION_MAX_X && (light == null || light.propagatesLight(direction, Directions.EAST))) {
traceSkylight(x + 1, y, z, nextNeighbourLevel, Directions.EAST, false) traceSkylight(x + 1, y, z, nextNeighbourLevel, Directions.EAST, false)
} }
} }

View File

@ -171,6 +171,9 @@ class GLFWWindow(
initLatch.await() // wait for async glfw init initLatch.await() // wait for async glfw init
glfwDefaultWindowHints() glfwDefaultWindowHints()
if (renderWindow.preferQuads) { if (renderWindow.preferQuads) {
// yes, this is dirty. for using a geometry shader we need 3.3+. The thing is 3.3+ does not allow us to use GL_QUAD.
// we can still bind it to a lower version and use features that need a more recent version of opengl.
// most drivers allow us to do this, if not it'll crash
setOpenGLVersion(3, 0, false) setOpenGLVersion(3, 0, false)
} else { } else {
setOpenGLVersion(3, 3, true) setOpenGLVersion(3, 3, true)

View File

@ -91,7 +91,7 @@ class SolidCullSectionPreparer(
light[SELF_LIGHT_INDEX] = sectionLight[index] light[SELF_LIGHT_INDEX] = sectionLight[index]
position = Vec3i(offsetX + x, offsetY + y, offsetZ + z) position = Vec3i(offsetX + x, offsetY + y, offsetZ + z)
val maxHeight = chunk.heightmap[baseIndex] val maxHeight = chunk.skylightHeightmap[baseIndex]
if (position.y >= maxHeight) { if (position.y >= maxHeight) {
light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or 0xF0).toByte() light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or 0xF0).toByte()
} }
@ -209,7 +209,7 @@ class SolidCullSectionPreparer(
val neighbourIndex = y shl 8 or nextBaseIndex val neighbourIndex = y shl 8 or nextBaseIndex
neighbourBlocks[ordinal] = blocks?.unsafeGet(neighbourIndex) neighbourBlocks[ordinal] = blocks?.unsafeGet(neighbourIndex)
light[ordinal] = sectionLight[neighbourIndex] light[ordinal] = sectionLight[neighbourIndex]
if (position.y > chunk.heightmap[nextBaseIndex]) { if (position.y > chunk.skylightHeightmap[nextBaseIndex]) {
light[ordinal] = (light[ordinal].toInt() or 0xF0).toByte() light[ordinal] = (light[ordinal].toInt() or 0xF0).toByte()
} }
} }

View File

@ -89,6 +89,7 @@ public final class ProtocolDefinition {
public static final float TICKS_PER_DAYf = (float) TICKS_PER_DAY; public static final float TICKS_PER_DAYf = (float) TICKS_PER_DAY;
public static final byte LIGHT_LEVELS = 16; public static final byte LIGHT_LEVELS = 16;
public static final byte MAX_LIGHT_LEVEL = LIGHT_LEVELS - 1;
static { static {
InetAddress inetAddress; InetAddress inetAddress;