rendering: wip lightning, change chunk saving structure

This commit is contained in:
Bixilon 2021-02-26 15:56:08 +01:00
parent 7fe1a40fc5
commit 4c49c733cd
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
32 changed files with 625 additions and 546 deletions

View File

@ -21,8 +21,7 @@ import de.bixilon.minosoft.data.entities.entities.vehicle.*
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.world.BlockInfo
import de.bixilon.minosoft.data.world.Chunk
import de.bixilon.minosoft.data.world.InChunkLocation
import de.bixilon.minosoft.data.world.ChunkSection
import de.bixilon.minosoft.data.world.InChunkSectionLocation
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
@ -88,14 +87,14 @@ object VersionTweaker {
}
@JvmStatic
fun transformChunk(chunk: Chunk, versionId: Int): Chunk {
fun transformSections(sections: Map<Int, ChunkSection>, versionId: Int) {
// some blocks need to be tweaked. eg. Grass with a snow block on top becomes snowy grass block
if (versionId >= ProtocolDefinition.FLATTING_VERSION_ID) {
return chunk
return
}
for ((sectionHeight, section) in chunk.sections) {
for ((sectionHeight, section) in sections) {
for ((location, blockInfo) in section.blocks) {
val newBlock = transformBlock(blockInfo.block, chunk, location, sectionHeight)
val newBlock = transformBlock(blockInfo.block, sections, location, sectionHeight)
if (newBlock === blockInfo.block) {
continue
}
@ -106,16 +105,15 @@ object VersionTweaker {
section.setBlockInfo(location, BlockInfo(newBlock, blockInfo.metaData))
}
}
return chunk
}
// ToDo: Broken
@JvmStatic
fun transformBlock(originalBlock: BlockState, chunk: Chunk, location: InChunkSectionLocation, sectionHeight: Int): BlockState? {
fun transformBlock(originalBlock: BlockState, sections: Map<Int, ChunkSection>, location: InChunkSectionLocation, sectionHeight: Int): BlockState? {
when (originalBlock.owner.resourceLocation) {
ResourceLocation("minecraft:grass") -> {
getBlockAbove(chunk, location, sectionHeight)?.let {
getBlockAbove(sections, location, sectionHeight)?.let {
if (it.owner.resourceLocation == TweakBlocks.SNOW_RESOURCE_LOCATION || it.owner.resourceLocation == TweakBlocks.SNOW_LAYER_RESOURCE_LOCAION) {
return TweakBlocks.GRASS_BLOCK_SNOWY_YES
}
@ -126,8 +124,7 @@ object VersionTweaker {
return originalBlock
}
private fun getBlockAbove(chunk: Chunk, location: InChunkSectionLocation, sectionHeight: Int): BlockState? {
val above = location.getInChunkLocation(sectionHeight)
return chunk.getBlockInfo(InChunkLocation(above.x, above.y + 1, above.z))?.block
private fun getBlockAbove(sections: Map<Int, ChunkSection>, location: InChunkSectionLocation, sectionHeight: Int): BlockState? {
return sections[sectionHeight]?.getBlockInfo(location)?.block
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
data class BlockPosition(val x: Int, val y: Int, val z: Int) {
constructor(chunkLocation: ChunkLocation, sectionHeight: Int, inChunkSectionLocation: InChunkSectionLocation) : this(chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X + inChunkSectionLocation.x, sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + inChunkSectionLocation.y, chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z + inChunkSectionLocation.z) // ToDo
@ -56,6 +57,10 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) {
}
}
fun toVec3(): Vec3 {
return Vec3(x, y, z)
}
override fun toString(): String {
return "($x $y $z)"
}

View File

@ -14,18 +14,25 @@ package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.world.biome.BiomeAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
import java.util.*
/**
* Collection of chunks sections (allocated in y)
*/
class Chunk(
val sections: MutableMap<Int, ChunkSection> = mutableMapOf(),
var biomeAccessor: BiomeAccessor,
var sections: MutableMap<Int, ChunkSection>? = null,
var biomeAccessor: BiomeAccessor? = null,
var lightAccessor: LightAccessor? = null,
) {
private val lock = Object()
val isFullyLoaded: Boolean
get() {
return sections != null && biomeAccessor != null && lightAccessor != null
}
fun getBlockInfo(location: InChunkLocation): BlockInfo? {
return sections[location.getSectionHeight()]?.getBlockInfo(location.getInChunkSectionLocation())
return sections?.get(location.getSectionHeight())?.getBlockInfo(location.getInChunkSectionLocation())
}
fun getBlockInfo(x: Int, y: Int, z: Int): BlockInfo? {
@ -38,6 +45,29 @@ class Chunk(
}
}
fun setData(data: ChunkData, merge: Boolean = false) {
synchronized(lock) {
data.blocks?.let {
if (sections == null) {
sections = mutableMapOf()
}
if (!merge) {
sections?.clear()
}
// replace all chunk sections
for ((sectionHeight, chunkSection) in it) {
getSectionOrCreate(sectionHeight).setData(chunkSection, merge)
}
}
data.biomeAccessor?.let {
this.biomeAccessor = it
}
data.lightAccessor?.let {
this.lightAccessor = it
}
}
}
fun setRawBlocks(blocks: HashMap<InChunkLocation, BlockState?>) {
for ((location, blockInfo) in blocks) {
setRawBlock(location, blockInfo)
@ -61,11 +91,14 @@ class Chunk(
}
fun getSectionOrCreate(sectionHeight: Int): ChunkSection {
return sections[sectionHeight].let {
if (sections == null) {
throw IllegalStateException("Chunk not received/initialized yet!")
}
return sections!![sectionHeight].let {
var section = it
if (section == null) {
section = ChunkSection()
sections[sectionHeight] = section
sections!![sectionHeight] = section
}
section
}

View File

@ -0,0 +1,35 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.world.biome.BiomeAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
data class ChunkData(
var blocks: Map<Int, ChunkSection>? = null,
var biomeAccessor: BiomeAccessor? = null,
var lightAccessor: LightAccessor? = null,
) {
fun replace(data: ChunkData) {
data.blocks?.let {
this.blocks = it
}
data.biomeAccessor?.let {
this.biomeAccessor = it
}
data.lightAccessor?.let {
this.lightAccessor = it
}
}
}

View File

@ -37,10 +37,6 @@ class ChunkSection constructor(
return getBlockInfo(InChunkSectionLocation(x, y, z))
}
fun updateStaticData() {
}
fun setRawBlock(location: InChunkSectionLocation, block: BlockState?) {
if (block == null) {
setBlockInfo(location, null)
@ -48,4 +44,11 @@ class ChunkSection constructor(
}
setBlockInfo(location, BlockInfo(block))
}
fun setData(chunkSection: ChunkSection, merge: Boolean = false) {
if (!merge) {
this.blocks.clear()
}
this.blocks.putAll(chunkSection.blocks)
}
}

View File

@ -1,152 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world;
import com.google.common.collect.HashBiMap;
import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData;
import de.bixilon.minosoft.data.entities.entities.Entity;
import de.bixilon.minosoft.data.mappings.Dimension;
import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Collection of chunks
*/
public class World {
private final ConcurrentHashMap<ChunkLocation, Chunk> chunks = new ConcurrentHashMap<>();
private final HashBiMap<Integer, Entity> entityIdMap = HashBiMap.create();
private final HashBiMap<UUID, Entity> entityUUIDMap = HashBiMap.create();
private boolean hardcore;
private boolean raining;
private Dimension dimension; // used for sky color, etc
public ConcurrentHashMap<ChunkLocation, Chunk> getAllChunks() {
return this.chunks;
}
@Nullable
public BlockInfo getBlockInfo(BlockPosition pos) {
ChunkLocation loc = pos.getChunkLocation();
if (getChunk(loc) != null) {
return getChunk(loc).getBlockInfo(pos.getInChunkLocation());
}
return null;
}
public Chunk getChunk(ChunkLocation loc) {
return this.chunks.get(loc);
}
public void setBlock(BlockPosition pos, BlockState block) {
if (getChunk(pos.getChunkLocation()) != null) {
getChunk(pos.getChunkLocation()).setRawBlock(pos.getInChunkLocation(), block);
}
// do nothing if chunk is unloaded
}
public void unloadChunk(ChunkLocation location) {
this.chunks.remove(location);
}
public void setChunk(ChunkLocation location, Chunk chunk) {
this.chunks.put(location, chunk);
}
public void setChunks(HashMap<ChunkLocation, Chunk> chunkMap) {
chunkMap.forEach(this.chunks::put);
}
public boolean isHardcore() {
return this.hardcore;
}
public void setHardcore(boolean hardcore) {
this.hardcore = hardcore;
}
public boolean isRaining() {
return this.raining;
}
public void setRaining(boolean raining) {
this.raining = raining;
}
public void addEntity(Entity entity) {
this.entityIdMap.put(entity.getEntityId(), entity);
this.entityUUIDMap.put(entity.getUUID(), entity);
}
public Entity getEntity(int id) {
return this.entityIdMap.get(id);
}
public Entity getEntity(UUID uuid) {
return this.entityUUIDMap.get(uuid);
}
public void removeEntity(Entity entity) {
this.entityIdMap.inverse().remove(entity);
this.entityUUIDMap.inverse().remove(entity);
}
public void removeEntity(int entityId) {
removeEntity(this.entityIdMap.get(entityId));
}
public void removeEntity(UUID entityUUID) {
removeEntity(this.entityUUIDMap.get(entityUUID));
}
public Dimension getDimension() {
return this.dimension;
}
public void setDimension(Dimension dimension) {
this.dimension = dimension;
}
public void setBlockEntityData(BlockPosition position, BlockEntityMetaData data) {
Chunk chunk = this.chunks.get(position.getChunkLocation());
if (chunk == null) {
return;
}
var section = chunk.getSections().get(position.getSectionHeight());
if (section == null) {
return;
}
var blockInfo = section.getBlockInfo(position.getInChunkSectionLocation());
if (blockInfo == null) {
return;
}
blockInfo.setMetaData(data);
}
public void setBlockEntityData(HashMap<BlockPosition, BlockEntityMetaData> blockEntities) {
blockEntities.forEach(this::setBlockEntityData);
}
public HashBiMap<Integer, Entity> getEntityIdMap() {
return this.entityIdMap;
}
public HashBiMap<UUID, Entity> getEntityUUIDMap() {
return this.entityUUIDMap;
}
}

View File

@ -0,0 +1,105 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world
import com.google.common.collect.HashBiMap
import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData
import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.mappings.Dimension
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* Collection of chunks
*/
class World {
val chunks = ConcurrentHashMap<ChunkLocation, Chunk>()
val entityIdMap = HashBiMap.create<Int, Entity>()
val entityUUIDMap = HashBiMap.create<UUID, Entity>()
var isHardcore = false
var isRaining = false
var dimension: Dimension? = null
fun getBlockInfo(blockPosition: BlockPosition): BlockInfo? {
val chunkLocation = blockPosition.getChunkLocation()
return chunks[chunkLocation]?.getBlockInfo(blockPosition.getInChunkLocation())
}
fun getChunk(loc: ChunkLocation): Chunk? {
return chunks[loc]
}
fun getOrCreateChunk(chunkLocation: ChunkLocation): Chunk {
return chunks[chunkLocation] ?: run {
val chunk = Chunk()
chunks[chunkLocation] = chunk
chunk
}
}
fun setBlock(blockPosition: BlockPosition, block: BlockState?) {
chunks[blockPosition.getChunkLocation()]?.setRawBlock(blockPosition.getInChunkLocation(), block)
}
fun unloadChunk(location: ChunkLocation) {
chunks.remove(location)
}
fun replaceChunk(location: ChunkLocation, chunk: Chunk) {
chunks[location] = chunk
}
fun replaceChunks(chunkMap: HashMap<ChunkLocation, Chunk>) {
for ((chunkLocation, chunk) in chunkMap) {
chunks[chunkLocation] = chunk
}
}
fun addEntity(entity: Entity) {
entityIdMap[entity.entityId] = entity
entityUUIDMap[entity.uuid] = entity
}
fun getEntity(id: Int): Entity? {
return entityIdMap[id]
}
fun getEntity(uuid: UUID): Entity? {
return entityUUIDMap[uuid]
}
fun removeEntity(entity: Entity) {
entityIdMap.inverse().remove(entity)
entityUUIDMap.inverse().remove(entity)
}
fun removeEntity(entityId: Int) {
entityIdMap[entityId]?.let { removeEntity(it) }
}
fun removeEntity(entityUUID: UUID) {
entityUUIDMap[entityUUID]?.let { removeEntity(it) }
}
fun setBlockEntityData(position: BlockPosition, data: BlockEntityMetaData?) {
chunks[position.getChunkLocation()]?.sections?.get(position.getSectionHeight())?.getBlockInfo(position.getInChunkSectionLocation())?.metaData = data
}
fun setBlockEntityData(blockEntities: HashMap<BlockPosition, BlockEntityMetaData>) {
for ((blockPosition, entityMetaData) in blockEntities) {
setBlockEntityData(blockPosition, entityMetaData)
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.light
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.InChunkLocation
class ChunkLightAccessor(
val blockLightLevel: MutableMap<InChunkLocation, Byte> = mutableMapOf(),
val skyLightLevel: MutableMap<InChunkLocation, Byte> = mutableMapOf(),
) : LightAccessor {
override fun getLightLevel(blockPosition: BlockPosition, direction: Directions): Int {
val inChunkLocation = blockPosition.getInChunkLocation()
return blockLightLevel[inChunkLocation]?.toInt() ?: 0
}
}

View File

@ -0,0 +1,31 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.light
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.BlockPosition
object DummyLightAccessor : LightAccessor {
override fun getLightLevel(blockPosition: BlockPosition, direction: Directions): Int {
return when (direction) {
Directions.NORTH -> 5
Directions.SOUTH -> 7
Directions.DOWN -> 3
Directions.UP -> 9
Directions.WEST -> 11
Directions.EAST -> 13
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.light
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.BlockPosition
interface LightAccessor {
fun getLightLevel(blockPosition: BlockPosition, direction: Directions): Int
}

View File

@ -10,25 +10,27 @@
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.palette
package de.bixilon.minosoft.data.world.palette;
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
interface Palette {
public interface Palette {
static Palette choosePalette(int bitsPerBlock) {
if (bitsPerBlock <= 4) {
return new IndirectPalette(4);
} else if (bitsPerBlock <= 8) {
return new IndirectPalette(bitsPerBlock);
fun blockById(id: Int): BlockState?
val bitsPerBlock: Int
fun read(buffer: InByteBuffer)
companion object {
fun choosePalette(bitsPerBlock: Int): Palette {
if (bitsPerBlock <= 4) {
return IndirectPalette(4)
} else if (bitsPerBlock <= 8) {
return IndirectPalette(bitsPerBlock)
}
return DirectPalette()
}
return new DirectPalette();
}
BlockState blockById(int id);
int getBitsPerBlock();
void read(InByteBuffer buffer);
}

View File

@ -29,7 +29,7 @@ class ChunkMesh {
private var vbo: Int = 0
private var trianglesCount: Int = 0
fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?) {
fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Float = 0.9f) {
data.add(position.x)
data.add(position.y)
data.add(position.z)
@ -47,6 +47,8 @@ class ChunkMesh {
} else {
data.add(Float.fromBits(tintColor.color))
}
data.add(lightLevel)
}
fun load() {
@ -74,6 +76,9 @@ class ChunkMesh {
glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (9 * Float.BYTES).toLong())
glEnableVertexAttribArray(index++)
glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (10 * Float.BYTES).toLong())
glEnableVertexAttribArray(index++)
// don't remove the ++ above!
index.let { }
@ -92,6 +97,6 @@ class ChunkMesh {
}
companion object {
private const val FLOATS_PER_VERTEX = 10
private const val FLOATS_PER_VERTEX = 11
}
}

View File

@ -43,8 +43,8 @@ class Frustum(val camera: Camera) {
}
fun containsChunk(chunkLocation: ChunkLocation, connection: Connection): Boolean {
val from = Vec3(chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, connection.player.world.dimension.minY, chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z)
val to = from + Vec3(ProtocolDefinition.SECTION_WIDTH_X, connection.player.world.dimension.logicalHeight, ProtocolDefinition.SECTION_WIDTH_Z)
val from = Vec3(chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, connection.player.world.dimension!!.minY, chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z)
val to = from + Vec3(ProtocolDefinition.SECTION_WIDTH_X, connection.player.world.dimension!!.logicalHeight, ProtocolDefinition.SECTION_WIDTH_Z)
val frustum = Frustum(connection.renderer.renderWindow.camera)
return frustum.containsRegion(from, to)
}

View File

@ -27,7 +27,6 @@ import de.bixilon.minosoft.gui.rendering.textures.TextureArray
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.logging.Log
import glm_.vec3.Vec3
import org.lwjgl.opengl.GL11.GL_CULL_FACE
import org.lwjgl.opengl.GL11.glEnable
import org.lwjgl.opengl.GL13.glDisable
@ -48,16 +47,16 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
val mesh = ChunkMesh()
val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1)
val above = world.allChunks[chunkLocation]?.sections?.get(sectionHeight + 1)
val below = world.chunks[chunkLocation]?.sections?.get(sectionHeight - 1)
val above = world.chunks[chunkLocation]?.sections?.get(sectionHeight + 1)
//val north = (world.allChunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight)
//val south = (world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?: throw ChunkNotLoadedException("South not loaded")).sections?.get(sectionHeight)
//val west = (world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?: throw ChunkNotLoadedException("West not loaded")).sections?.get(sectionHeight)
//val east = (world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?: throw ChunkNotLoadedException("North not loaded")).sections?.get(sectionHeight)
val north = world.allChunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?.sections?.get(sectionHeight)
val south = world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight)
val west = world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight)
val east = world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight)
val north = world.chunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?.sections?.get(sectionHeight)
val south = world.chunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight)
val west = world.chunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight)
val east = world.chunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight)
for ((position, blockInfo) in section.blocks) {
val blockBelow: BlockInfo? = if (position.y == 0 && below != null) {
@ -90,13 +89,11 @@ class WorldRenderer(private val connection: Connection, private val world: World
} else {
section.getBlockInfo(position.getLocationByDirection(Directions.EAST))
}
val worldPosition = Vec3(position.x + chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, position.y + sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y, position.z + chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z)
val blockPosition = BlockPosition(chunkLocation, sectionHeight, position)
if (blockPosition == BlockPosition(-103, 3, 288)) {
Log.debug("")
}
val biome = chunk.biomeAccessor.getBiome(blockPosition)
val biome = chunk.biomeAccessor!!.getBiome(blockPosition)
var tintColor: RGBColor? = null
if (StaticConfiguration.BIOME_DEBUG_MODE) {
@ -113,7 +110,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
blockInfo.block.tintColor?.let { tintColor = it }
}
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, tintColor, worldPosition, mesh, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, chunk.lightAccessor!!, tintColor, blockPosition, mesh, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast))
}
return mesh
}
@ -145,7 +142,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
for ((chunkLocation, map) in chunkSectionsToDraw) {
if (! visibleChunks.contains(chunkLocation)) {
if (!visibleChunks.contains(chunkLocation)) {
continue
}
for ((_, mesh) in map) {
@ -171,7 +168,10 @@ class WorldRenderer(private val connection: Connection, private val world: World
fun prepareChunk(chunkLocation: ChunkLocation, chunk: Chunk) {
chunkSectionsToDraw[chunkLocation] = ConcurrentHashMap()
for ((sectionHeight, section) in chunk.sections) {
if (!chunk.isFullyLoaded) {
return
}
for ((sectionHeight, section) in chunk.sections!!) {
prepareChunkSection(chunkLocation, sectionHeight, section, chunk)
}
}
@ -217,7 +217,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
private fun prepareWorld(world: World) {
for ((chunkLocation, chunk) in world.allChunks) {
for ((chunkLocation, chunk) in world.chunks) {
prepareChunk(chunkLocation, chunk)
}
}
@ -236,4 +236,8 @@ class WorldRenderer(private val connection: Connection, private val world: World
}
}
}
fun getChunkSize(): Int {
return chunkSectionsToDraw.size
}
}

View File

@ -17,11 +17,12 @@ import com.google.gson.JsonObject
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockInfo
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.chunk.ChunkMesh
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel
import de.bixilon.minosoft.gui.rendering.textures.Texture
import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
class BlockRenderer(data: JsonObject, parent: BlockModel) {
private val transparentFaces: MutableSet<Directions> = mutableSetOf()
@ -70,8 +71,8 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
}
}
fun render(blockInfo: BlockInfo, tintColor: RGBColor?, position: Vec3, mesh: ChunkMesh, neighbourBlocks: Array<BlockInfo?>) {
val modelMatrix = Mat4().translate(position)
fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: ChunkMesh, neighbourBlocks: Array<BlockInfo?>) {
val modelMatrix = Mat4().translate(position.toVec3())
for (direction in Directions.DIRECTIONS) {
for (element in elements) {
@ -91,7 +92,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
continue
}
element.render(tintColor, textureMapping, modelMatrix, direction, mesh)
element.render(tintColor, lightAccessor.getLightLevel(position, direction) / 15f, textureMapping, modelMatrix, direction, mesh)
}
}
}

View File

@ -50,7 +50,7 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
}
fun render(tintColor: RGBColor?, textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, mesh: ChunkMesh) {
fun render(tintColor: RGBColor?, lightLevel: Float, textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, mesh: ChunkMesh) {
val realDirection = directionMapping[direction]!!
val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
@ -75,7 +75,8 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
tintColor
} else {
null
}
},
lightLevel = lightLevel,
)
}
}

View File

@ -49,6 +49,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf(
"FPS: ${getFPS()}",
"Timings: avg ${getAvgFrameTime()}ms, min ${getMinFrameTime()}ms, max ${getMaxFrameTime()}ms",
"Chunks: ${hudTextElement.renderWindow.worldRenderer.getChunkSize()} / ${hudTextElement.connection.player.world.chunks.size}",
"Connected to ${hudTextElement.connection.address} with ${hudTextElement.connection.version}",
"",
"XYZ ${getLocation()}",

View File

@ -32,9 +32,9 @@ class TextureArray(val textures: List<Texture>, val maxWidth: Int, val maxHeight
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR) // ToDo: This breaks transparency again
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, maxWidth, maxHeight, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?)
for (texture in textures) {

View File

@ -1,62 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event.events;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketChunkData;
import de.bixilon.minosoft.util.nbt.tag.CompoundTag;
/**
* Fired when a new chunk is received or a full chunk changes
*/
public class ChunkDataChangeEvent extends ConnectionEvent {
private final ChunkLocation location;
private final Chunk chunk;
private final CompoundTag heightMap;
public ChunkDataChangeEvent(Connection connection, ChunkLocation location, Chunk chunk, CompoundTag heightMap) {
super(connection);
this.location = location;
this.chunk = chunk;
this.heightMap = heightMap;
}
public ChunkDataChangeEvent(Connection connection, ChunkLocation location, Chunk chunk) {
super(connection);
this.location = location;
this.chunk = chunk;
this.heightMap = new CompoundTag();
}
public ChunkDataChangeEvent(Connection connection, PacketChunkData pkg) {
super(connection);
this.location = pkg.getLocation();
this.chunk = pkg.getChunk();
this.heightMap = pkg.getHeightMap();
}
public ChunkLocation getLocation() {
return this.location;
}
public Chunk getChunk() {
return this.chunk;
}
public CompoundTag getHeightMap() {
return this.heightMap;
}
}

View File

@ -0,0 +1,46 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event.events
import de.bixilon.minosoft.data.world.ChunkData
import de.bixilon.minosoft.data.world.ChunkLocation
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketChunkData
import de.bixilon.minosoft.util.nbt.tag.CompoundTag
/**
* Fired when a new chunk is received or a full chunk changes
*/
class ChunkDataChangeEvent : ConnectionEvent {
val location: ChunkLocation
val chunkData: ChunkData
val heightMap: CompoundTag?
constructor(connection: Connection?, location: ChunkLocation, chunkData: ChunkData, heightMap: CompoundTag?) : super(connection) {
this.location = location
this.chunkData = chunkData
this.heightMap = heightMap
}
constructor(connection: Connection?, location: ChunkLocation, chunkData: ChunkData) : super(connection) {
this.location = location
this.chunkData = chunkData
heightMap = CompoundTag()
}
constructor(connection: Connection, pkg: PacketChunkData) : super(connection) {
location = pkg.location
chunkData = pkg.chunkData
heightMap = pkg.heightMap
}
}

View File

@ -57,7 +57,7 @@ public class PacketBlockChange extends ClientboundPacket {
// tweak
if (!connection.getVersion().isFlattened()) {
BlockState block = VersionTweaker.transformBlock(getBlock(), chunk, this.position.getInChunkSectionLocation(), this.position.getSectionHeight());
BlockState block = VersionTweaker.transformBlock(getBlock(), chunk.getSections(), this.position.getInChunkSectionLocation(), this.position.getSectionHeight());
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block);
} else {
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock());

View File

@ -1,101 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.mappings.Dimension;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.ChunkUtil;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.logging.Log;
import java.util.HashMap;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W26A;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W28A;
public class PacketChunkBulk extends ClientboundPacket {
private final HashMap<ChunkLocation, Chunk> chunks = new HashMap<>();
@Override
public boolean read(InByteBuffer buffer) {
Dimension dimension = buffer.getConnection().getPlayer().getWorld().getDimension();
if (buffer.getVersionId() < V_14W26A) {
int chunkCount = buffer.readUnsignedShort();
int dataLen = buffer.readInt();
boolean containsSkyLight = buffer.readBoolean();
// decompress chunk data
InByteBuffer decompressed;
if (buffer.getVersionId() < V_14W28A) {
decompressed = Util.decompress(buffer.readBytes(dataLen), buffer.getConnection());
} else {
decompressed = buffer;
}
// chunk meta data
for (int i = 0; i < chunkCount; i++) {
int x = buffer.readInt();
int z = buffer.readInt();
long[] sectionBitMask = {buffer.readUnsignedShort()};
int addBitMask = buffer.readUnsignedShort();
this.chunks.put(new ChunkLocation(x, z), ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMask, addBitMask, true, containsSkyLight));
}
return true;
}
boolean containsSkyLight = buffer.readBoolean();
int chunkCount = buffer.readVarInt();
int[] x = new int[chunkCount];
int[] z = new int[chunkCount];
long[][] sectionBitMask = new long[chunkCount][];
// ToDo: this was still compressed in 14w28a
for (int i = 0; i < chunkCount; i++) {
x[i] = buffer.readInt();
z[i] = buffer.readInt();
sectionBitMask[i] = new long[]{buffer.readUnsignedShort()};
}
for (int i = 0; i < chunkCount; i++) {
this.chunks.put(new ChunkLocation(x[i], z[i]), ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask[i], (short) 0, true, containsSkyLight));
}
return true;
}
@Override
public void handle(Connection connection) {
this.chunks.values().forEach((chunk) -> VersionTweaker.transformChunk(chunk, connection.getVersion().getVersionId()));
getChunks().forEach(((location, chunk) -> connection.fireEvent(new ChunkDataChangeEvent(connection, location, chunk))));
connection.getPlayer().getWorld().setChunks(getChunks());
getChunks().forEach(((location, chunk) -> connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(location, chunk)));
}
@Override
public void log() {
Log.protocol(String.format("[IN] Chunk bulk packet received (chunks=%s)", this.chunks.size()));
}
public HashMap<ChunkLocation, Chunk> getChunks() {
return this.chunks;
}
}

View File

@ -0,0 +1,88 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker
import de.bixilon.minosoft.data.world.ChunkData
import de.bixilon.minosoft.data.world.ChunkLocation
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.ChunkUtil
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.logging.Log
import java.util.*
class PacketChunkBulk : ClientboundPacket() {
val data = HashMap<ChunkLocation, ChunkData>()
override fun read(buffer: InByteBuffer): Boolean {
val dimension = buffer.connection.player.world.dimension!!
if (buffer.versionId < ProtocolVersions.V_14W26A) {
val chunkCount = buffer.readUnsignedShort()
val dataLength = buffer.readInt()
val containsSkyLight = buffer.readBoolean()
// decompress chunk data
val decompressed: InByteBuffer = if (buffer.versionId < ProtocolVersions.V_14W28A) {
Util.decompress(buffer.readBytes(dataLength), buffer.connection)
} else {
buffer
}
// chunk meta data
for (i in 0 until chunkCount) {
val chunkLocation = buffer.readChunkLocation()
val sectionBitMask = longArrayOf(buffer.readUnsignedShort().toLong())
val addBitMask = buffer.readUnsignedShort()
data[chunkLocation] = ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMask, addBitMask, true, containsSkyLight)
}
return true
}
val containsSkyLight = buffer.readBoolean()
val chunkCount = buffer.readVarInt()
val x = IntArray(chunkCount)
val z = IntArray(chunkCount)
val sectionBitMask = arrayOfNulls<LongArray>(chunkCount)
// ToDo: this was still compressed in 14w28a
for (i in 0 until chunkCount) {
x[i] = buffer.readInt()
z[i] = buffer.readInt()
sectionBitMask[i] = longArrayOf(buffer.readUnsignedShort().toLong())
}
for (i in 0 until chunkCount) {
data[ChunkLocation(x[i], z[i])] = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask[i], 0, true, containsSkyLight)
}
return true
}
override fun handle(connection: Connection) {
// transform data
for ((chunkLocation, data) in data) {
data.blocks?.let {
VersionTweaker.transformSections(it, connection.version.versionId)
}
connection.fireEvent(ChunkDataChangeEvent(connection, chunkLocation, data))
val chunk = connection.player.world.getOrCreateChunk(chunkLocation)
chunk.setData(data)
connection.renderer.renderWindow.worldRenderer.prepareChunk(chunkLocation, chunk)
}
}
override fun log() {
Log.protocol("[IN] Chunk bulk packet received (chunks${data.size})")
}
}

View File

@ -1,153 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData;
import de.bixilon.minosoft.data.mappings.Dimension;
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.data.world.biome.BiomeAccessor;
import de.bixilon.minosoft.data.world.biome.NoiseBiomeAccessor;
import de.bixilon.minosoft.modding.event.events.BlockEntityMetaDataChangeEvent;
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.ChunkUtil;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.logging.Log;
import de.bixilon.minosoft.util.nbt.tag.CompoundTag;
import java.util.HashMap;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*;
public class PacketChunkData extends ClientboundPacket {
private final HashMap<BlockPosition, BlockEntityMetaData> blockEntities = new HashMap<>();
private ChunkLocation location;
private Chunk chunk;
private CompoundTag heightMap;
private boolean ignoreOldData;
@Override
public boolean read(InByteBuffer buffer) {
Dimension dimension = buffer.getConnection().getPlayer().getWorld().getDimension();
this.location = new ChunkLocation(buffer.readInt(), buffer.readInt());
boolean fullChunk = true;
if (buffer.getVersionId() < V_20W45A) {
fullChunk = buffer.readBoolean();
}
if (buffer.getVersionId() < V_14W26A) {
long[] sectionBitMasks = {buffer.readUnsignedShort()};
int addBitMask = buffer.readUnsignedShort();
// decompress chunk data
InByteBuffer decompressed;
if (buffer.getVersionId() < V_14W28A) {
decompressed = Util.decompress(buffer.readBytes(buffer.readInt()), buffer.getConnection());
} else {
decompressed = buffer;
}
this.chunk = ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMasks, addBitMask, fullChunk, dimension.getHasSkyLight());
return true;
}
long[] sectionBitMasks;
if (buffer.getVersionId() < V_15W34C) {
sectionBitMasks = new long[]{buffer.readUnsignedShort()};
} else if (buffer.getVersionId() < V_15W36D) {
sectionBitMasks = new long[]{buffer.readInt()};
} else if (buffer.getVersionId() < V_21W03A) {
sectionBitMasks = new long[]{buffer.readVarInt()};
} else {
sectionBitMasks = buffer.readLongArray();
}
if (buffer.getVersionId() >= V_1_16_PRE7 && buffer.getVersionId() < V_1_16_2_PRE2) {
this.ignoreOldData = buffer.readBoolean();
}
if (buffer.getVersionId() >= V_18W44A) {
this.heightMap = (CompoundTag) buffer.readNBT();
}
BiomeAccessor biomeAccessor = null;
if (fullChunk) {
biomeAccessor = new NoiseBiomeAccessor(buffer.readBiomeArray());
}
int size = buffer.readVarInt();
int lastPos = buffer.getPosition();
if (size > 0) {
this.chunk = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMasks, 0, fullChunk, dimension.getHasSkyLight());
if (this.chunk != null) {
if (biomeAccessor != null) {
this.chunk.setBiomeAccessor(biomeAccessor);
}
}
// set position of the byte buffer, because of some reasons HyPixel makes some weird stuff and sends way to much 0 bytes. (~ 190k), thanks @pokechu22
buffer.setPosition(size + lastPos);
}
if (buffer.getVersionId() >= V_1_9_4) {
int blockEntitiesCount = buffer.readVarInt();
for (int i = 0; i < blockEntitiesCount; i++) {
CompoundTag tag = (CompoundTag) buffer.readNBT();
BlockEntityMetaData data = BlockEntityMetaData.getData(buffer.getConnection(), null, tag);
if (data == null) {
continue;
}
this.blockEntities.put(new BlockPosition(tag.getIntTag("x").getValue(), (short) tag.getIntTag("y").getValue(), tag.getIntTag("z").getValue()), data);
}
}
return true;
}
@Override
public void handle(Connection connection) {
getBlockEntities().forEach(((position, compoundTag) -> connection.fireEvent(new BlockEntityMetaDataChangeEvent(connection, position, null, compoundTag))));
VersionTweaker.transformChunk(this.chunk, connection.getVersion().getVersionId());
connection.fireEvent(new ChunkDataChangeEvent(connection, this));
connection.getPlayer().getWorld().setChunk(getLocation(), getChunk());
connection.getPlayer().getWorld().setBlockEntityData(getBlockEntities());
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(this.location, this.chunk);
}
@Override
public void log() {
Log.protocol(String.format("[IN] Chunk packet received (chunk: %s)", this.location));
}
public ChunkLocation getLocation() {
return this.location;
}
public Chunk getChunk() {
return this.chunk;
}
public HashMap<BlockPosition, BlockEntityMetaData> getBlockEntities() {
return this.blockEntities;
}
public CompoundTag getHeightMap() {
return this.heightMap;
}
}

View File

@ -0,0 +1,117 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData
import de.bixilon.minosoft.data.mappings.tweaker.VersionTweaker
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.ChunkData
import de.bixilon.minosoft.data.world.ChunkLocation
import de.bixilon.minosoft.data.world.biome.NoiseBiomeAccessor
import de.bixilon.minosoft.modding.event.events.BlockEntityMetaDataChangeEvent
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.ChunkUtil
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.nbt.tag.CompoundTag
import java.util.*
class PacketChunkData : ClientboundPacket() {
private val blockEntities = HashMap<BlockPosition, BlockEntityMetaData>()
lateinit var location: ChunkLocation
val chunkData: ChunkData = ChunkData()
var heightMap: CompoundTag? = null
private var shouldMerge = false
override fun read(buffer: InByteBuffer): Boolean {
val dimension = buffer.connection.player.world.dimension
location = ChunkLocation(buffer.readInt(), buffer.readInt())
if (buffer.versionId < ProtocolVersions.V_20W45A) {
shouldMerge = !buffer.readBoolean()
}
if (buffer.versionId < ProtocolVersions.V_14W26A) {
val sectionBitMasks = longArrayOf(buffer.readUnsignedShort().toLong())
val addBitMask = buffer.readUnsignedShort()
// decompress chunk data
val decompressed: InByteBuffer = if (buffer.versionId < ProtocolVersions.V_14W28A) {
Util.decompress(buffer.readBytes(buffer.readInt()), buffer.connection)
} else {
buffer
}
chunkData.replace(ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMasks, addBitMask, !shouldMerge, dimension!!.hasSkyLight))
return true
}
val sectionBitMasks: LongArray = when {
buffer.versionId < ProtocolVersions.V_15W34C -> {
longArrayOf(buffer.readUnsignedShort().toLong())
}
buffer.versionId < ProtocolVersions.V_15W36D -> {
longArrayOf(buffer.readInt().toLong())
}
buffer.versionId < ProtocolVersions.V_21W03A -> {
longArrayOf(buffer.readVarInt().toLong())
}
else -> {
buffer.readLongArray()
}
}
if (buffer.versionId >= ProtocolVersions.V_1_16_PRE7 && buffer.versionId < ProtocolVersions.V_1_16_2_PRE2) {
shouldMerge = buffer.readBoolean()
}
if (buffer.versionId >= ProtocolVersions.V_18W44A) {
heightMap = buffer.readNBT() as CompoundTag
}
if (!shouldMerge) {
chunkData.biomeAccessor = NoiseBiomeAccessor(buffer.readBiomeArray())
}
val size = buffer.readVarInt()
val lastPos = buffer.position
if (size > 0) {
chunkData.replace(ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMasks, 0, !shouldMerge, dimension!!.hasSkyLight))
// set position of the byte buffer, because of some reasons HyPixel makes some weird stuff and sends way to much 0 bytes. (~ 190k), thanks @pokechu22
buffer.position = size + lastPos
}
if (buffer.versionId >= ProtocolVersions.V_1_9_4) {
val blockEntitiesCount = buffer.readVarInt()
for (i in 0 until blockEntitiesCount) {
val tag = buffer.readNBT() as CompoundTag
val data = BlockEntityMetaData.getData(buffer.connection, null, tag) ?: continue
blockEntities[BlockPosition(tag.getNumberTag("x").asInt, tag.getNumberTag("y").asInt, tag.getNumberTag("z").asInt)] = data
}
}
return true
}
override fun handle(connection: Connection) {
for ((position, blockEntityMetaData) in blockEntities) {
connection.fireEvent(BlockEntityMetaDataChangeEvent(connection, position, null, blockEntityMetaData))
}
chunkData.blocks?.let {
VersionTweaker.transformSections(it, connection.version.versionId)
}
connection.fireEvent(ChunkDataChangeEvent(connection, this))
val chunk = connection.player.world.getOrCreateChunk(location)
chunk.setData(chunkData)
connection.player.world.setBlockEntityData(blockEntities)
connection.renderer.renderWindow.worldRenderer.prepareChunk(location, chunk)
}
override fun log() {
Log.protocol(String.format("[IN] Chunk packet received (chunk: %s)", location))
}
}

View File

@ -94,7 +94,7 @@ public class PacketMultiBlockChange extends ClientboundPacket {
// tweak
if (!connection.getVersion().isFlattened()) {
for (Map.Entry<InChunkLocation, BlockState> entry : getBlocks().entrySet()) {
BlockState block = VersionTweaker.transformBlock(entry.getValue(), chunk, entry.getKey().getInChunkSectionLocation(), entry.getKey().getSectionHeight());
BlockState block = VersionTweaker.transformBlock(entry.getValue(), chunk.getSections(), entry.getKey().getInChunkSectionLocation(), entry.getKey().getSectionHeight());
if (block == entry.getValue()) {
continue;
}

View File

@ -87,7 +87,7 @@ class PacketRespawn : ClientboundPacket() {
}
// clear all chunks
connection.player.world.allChunks.clear()
connection.player.world.chunks.clear()
connection.player.world.dimension = dimension
connection.player.isSpawnConfirmed = false
connection.player.gameMode = gameMode

View File

@ -13,7 +13,10 @@
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.data.world.light.LightAccessor;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.ChunkUtil;
@ -23,7 +26,8 @@ import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_16_PRE3
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_20W49A;
public class PacketUpdateLight extends ClientboundPacket {
ChunkLocation location;
private ChunkLocation location;
private LightAccessor lightAccessor;
@Override
public boolean read(InByteBuffer buffer) {
@ -48,7 +52,7 @@ public class PacketUpdateLight extends ClientboundPacket {
emptySkyLightMask = buffer.readLongArray();
emptyBlockLightMask = buffer.readLongArray();
}
ChunkUtil.readSkyLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask);
this.lightAccessor = ChunkUtil.readSkyLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask);
return true;
}
@ -56,4 +60,11 @@ public class PacketUpdateLight extends ClientboundPacket {
public void log() {
Log.protocol(String.format("[IN] Received light update (location=%s)", this.location));
}
@Override
public void handle(Connection connection) {
Chunk chunk = connection.getPlayer().getWorld().getOrCreateChunk(this.location);
chunk.setLightAccessor(this.lightAccessor);
connection.getRenderer().getRenderWindow().getWorldRenderer().prepareChunk(this.location, chunk);
}
}

View File

@ -35,6 +35,7 @@ import de.bixilon.minosoft.data.mappings.particle.data.ParticleData;
import de.bixilon.minosoft.data.mappings.recipes.Ingredient;
import de.bixilon.minosoft.data.text.ChatComponent;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.data.world.ChunkLocation;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.nbt.tag.*;
@ -591,4 +592,7 @@ public class InByteBuffer {
return new ResourceLocation(resourceLocation);
}
public ChunkLocation readChunkLocation() {
return new ChunkLocation(readInt(), readInt());
}
}

View File

@ -17,12 +17,13 @@ import de.bixilon.minosoft.data.mappings.Dimension;
import de.bixilon.minosoft.data.mappings.biomes.Biome;
import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.data.world.BlockInfo;
import de.bixilon.minosoft.data.world.Chunk;
import de.bixilon.minosoft.data.world.ChunkData;
import de.bixilon.minosoft.data.world.ChunkSection;
import de.bixilon.minosoft.data.world.InChunkSectionLocation;
import de.bixilon.minosoft.data.world.biome.BiomeAccessor;
import de.bixilon.minosoft.data.world.biome.DummyBiomeAccessor;
import de.bixilon.minosoft.data.world.biome.XZBiomeAccessor;
import de.bixilon.minosoft.data.world.light.DummyLightAccessor;
import de.bixilon.minosoft.data.world.light.LightAccessor;
import de.bixilon.minosoft.data.world.palette.Palette;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
@ -33,7 +34,7 @@ import java.util.HashMap;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*;
public final class ChunkUtil {
public static Chunk readChunkPacket(InByteBuffer buffer, Dimension dimension, long[] sectionBitMasks, int addBitMask, boolean fullChunk, boolean containsSkyLight) {
public static ChunkData readChunkPacket(InByteBuffer buffer, Dimension dimension, long[] sectionBitMasks, int addBitMask, boolean fullChunk, boolean containsSkyLight) {
if (buffer.getVersionId() < V_14W26A) {
if (sectionBitMasks[0] == 0x00 && fullChunk) {
// unload chunk
@ -97,7 +98,7 @@ public final class ChunkUtil {
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); // ToDo
}
}
return new Chunk(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)));
return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE);
}
if (buffer.getVersionId() < V_15W35A) { // ToDo: was this really changed in 62?
byte sections = BitByte.getBitCount(sectionBitMasks[0]);
@ -143,7 +144,7 @@ public final class ChunkUtil {
}
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap));
}
return new Chunk(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0))); // ToDo
return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE); // ToDo
}
// really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712
HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
@ -155,7 +156,7 @@ public final class ChunkUtil {
if (buffer.getVersionId() >= V_18W43A) {
buffer.readShort(); // block count
}
Palette palette = Palette.choosePalette(buffer.readUnsignedByte());
Palette palette = Palette.Companion.choosePalette(buffer.readUnsignedByte());
palette.read(buffer);
int individualValueMask = ((1 << palette.getBitsPerBlock()) - 1);
@ -206,21 +207,23 @@ public final class ChunkUtil {
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap));
}
BiomeAccessor biomeAccessor = new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0));
ChunkData chunkData = new ChunkData();
chunkData.setBlocks(sectionMap);
if (buffer.getVersionId() < V_19W36A && fullChunk) {
Biome[] biomes = new Biome[256];
for (int i = 0; i < biomes.length; i++) {
biomes[i] = buffer.getConnection().getMapping().getBiomeRegistry().get(buffer.readInt());
}
biomeAccessor = new XZBiomeAccessor(biomes);
chunkData.setBiomeAccessor(new XZBiomeAccessor(biomes));
}
return new Chunk(sectionMap, biomeAccessor);
return chunkData;
}
public static void readSkyLightPacket(InByteBuffer buffer, long[] skyLightMask, long[] blockLightMask, long[] emptyBlockLightMask, long[] emptySkyLightMask) {
public static LightAccessor readSkyLightPacket(InByteBuffer buffer, long[] skyLightMask, long[] blockLightMask, long[] emptyBlockLightMask, long[] emptySkyLightMask) {
readLightArray(buffer, BitSet.valueOf(skyLightMask));
readLightArray(buffer, BitSet.valueOf(blockLightMask));
// ToDo
return DummyLightAccessor.INSTANCE;
}
private static void readLightArray(InByteBuffer buffer, BitSet lightMask) {

View File

@ -17,6 +17,7 @@ out vec4 outColor;
in vec3 passTextureCoordinates;
in vec4 passTintColor;
in float passLightLevel;
uniform sampler2DArray blockTextureArray;
@ -30,5 +31,5 @@ void main() {
if (passTintColor.a > 0.0f){
mixedColor *= passTintColor.rgb;
}
outColor = vec4(mixedColor, texelColor.a);
outColor = vec4(mixedColor * passLightLevel, texelColor.a);
}

View File

@ -20,9 +20,11 @@ layout (location = 2) in int textureLayer;
layout (location = 3) in vec3 animatedTextureData;
layout (location = 4) in uint tintColor;
layout (location = 5) in float lightLevel;
out vec3 passTextureCoordinates;
out vec4 passTintColor;
out float passLightLevel;
uniform mat4 viewProjectionMatrix;
uniform int animationTick;
@ -32,6 +34,8 @@ void main() {
gl_Position = viewProjectionMatrix * vec4(inPosition, 1.0f);
passTintColor = vec4(((tintColor >> 24u) & 0xFFu) / 255.0f, ((tintColor >> 16u) & 0xFFu) / 255.0f, ((tintColor >> 8u) & 0xFFu) / 255.0f, (tintColor & 0xFFu) / 255.0f);
passLightLevel = lightLevel;
if (animatedTextureData.y == 1.0f) {
passTextureCoordinates = vec3(textureIndex, textureLayer);
return;