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

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.world package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
data class BlockPosition(val x: Int, val y: Int, val z: Int) { 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 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 { override fun toString(): String {
return "($x $y $z)" 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.mappings.blocks.BlockState
import de.bixilon.minosoft.data.world.biome.BiomeAccessor import de.bixilon.minosoft.data.world.biome.BiomeAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
import java.util.* import java.util.*
/** /**
* Collection of chunks sections (allocated in y) * Collection of chunks sections (allocated in y)
*/ */
class Chunk( class Chunk(
val sections: MutableMap<Int, ChunkSection> = mutableMapOf(), var sections: MutableMap<Int, ChunkSection>? = null,
var biomeAccessor: BiomeAccessor, 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? { 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? { 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?>) { fun setRawBlocks(blocks: HashMap<InChunkLocation, BlockState?>) {
for ((location, blockInfo) in blocks) { for ((location, blockInfo) in blocks) {
setRawBlock(location, blockInfo) setRawBlock(location, blockInfo)
@ -61,11 +91,14 @@ class Chunk(
} }
fun getSectionOrCreate(sectionHeight: Int): ChunkSection { 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 var section = it
if (section == null) { if (section == null) {
section = ChunkSection() section = ChunkSection()
sections[sectionHeight] = section sections!![sectionHeight] = section
} }
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)) return getBlockInfo(InChunkSectionLocation(x, y, z))
} }
fun updateStaticData() {
}
fun setRawBlock(location: InChunkSectionLocation, block: BlockState?) { fun setRawBlock(location: InChunkSectionLocation, block: BlockState?) {
if (block == null) { if (block == null) {
setBlockInfo(location, null) setBlockInfo(location, null)
@ -48,4 +44,11 @@ class ChunkSection constructor(
} }
setBlockInfo(location, BlockInfo(block)) 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. * 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; interface Palette {
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
public interface Palette { fun blockById(id: Int): BlockState?
static Palette choosePalette(int bitsPerBlock) {
val bitsPerBlock: Int
fun read(buffer: InByteBuffer)
companion object {
fun choosePalette(bitsPerBlock: Int): Palette {
if (bitsPerBlock <= 4) { if (bitsPerBlock <= 4) {
return new IndirectPalette(4); return IndirectPalette(4)
} else if (bitsPerBlock <= 8) { } else if (bitsPerBlock <= 8) {
return new IndirectPalette(bitsPerBlock); 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 vbo: Int = 0
private var trianglesCount: 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.x)
data.add(position.y) data.add(position.y)
data.add(position.z) data.add(position.z)
@ -47,6 +47,8 @@ class ChunkMesh {
} else { } else {
data.add(Float.fromBits(tintColor.color)) data.add(Float.fromBits(tintColor.color))
} }
data.add(lightLevel)
} }
fun load() { fun load() {
@ -74,6 +76,9 @@ class ChunkMesh {
glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (9 * Float.BYTES).toLong()) glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (9 * Float.BYTES).toLong())
glEnableVertexAttribArray(index++) glEnableVertexAttribArray(index++)
glVertexAttribPointer(index, 1, GL_FLOAT, false, FLOATS_PER_VERTEX * Float.BYTES, (10 * Float.BYTES).toLong())
glEnableVertexAttribArray(index++)
// don't remove the ++ above! // don't remove the ++ above!
index.let { } index.let { }
@ -92,6 +97,6 @@ class ChunkMesh {
} }
companion object { 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 { 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 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 to = from + Vec3(ProtocolDefinition.SECTION_WIDTH_X, connection.player.world.dimension!!.logicalHeight, ProtocolDefinition.SECTION_WIDTH_Z)
val frustum = Frustum(connection.renderer.renderWindow.camera) val frustum = Frustum(connection.renderer.renderWindow.camera)
return frustum.containsRegion(from, to) 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.network.Connection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import glm_.vec3.Vec3
import org.lwjgl.opengl.GL11.GL_CULL_FACE import org.lwjgl.opengl.GL11.GL_CULL_FACE
import org.lwjgl.opengl.GL11.glEnable import org.lwjgl.opengl.GL11.glEnable
import org.lwjgl.opengl.GL13.glDisable import org.lwjgl.opengl.GL13.glDisable
@ -48,16 +47,16 @@ class WorldRenderer(private val connection: Connection, private val world: World
} }
val mesh = ChunkMesh() val mesh = ChunkMesh()
val below = world.allChunks[chunkLocation]?.sections?.get(sectionHeight - 1) val below = world.chunks[chunkLocation]?.sections?.get(sectionHeight - 1)
val above = world.allChunks[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 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 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 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 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 north = world.chunks[chunkLocation.getLocationByDirection(Directions.NORTH)]?.sections?.get(sectionHeight)
val south = world.allChunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight) val south = world.chunks[chunkLocation.getLocationByDirection(Directions.SOUTH)]?.sections?.get(sectionHeight)
val west = world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight) val west = world.chunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight)
val east = world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight) val east = world.chunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight)
for ((position, blockInfo) in section.blocks) { for ((position, blockInfo) in section.blocks) {
val blockBelow: BlockInfo? = if (position.y == 0 && below != null) { val blockBelow: BlockInfo? = if (position.y == 0 && below != null) {
@ -90,13 +89,11 @@ class WorldRenderer(private val connection: Connection, private val world: World
} else { } else {
section.getBlockInfo(position.getLocationByDirection(Directions.EAST)) 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) val blockPosition = BlockPosition(chunkLocation, sectionHeight, position)
if (blockPosition == BlockPosition(-103, 3, 288)) { if (blockPosition == BlockPosition(-103, 3, 288)) {
Log.debug("") Log.debug("")
} }
val biome = chunk.biomeAccessor.getBiome(blockPosition) val biome = chunk.biomeAccessor!!.getBiome(blockPosition)
var tintColor: RGBColor? = null var tintColor: RGBColor? = null
if (StaticConfiguration.BIOME_DEBUG_MODE) { 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.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 return mesh
} }
@ -171,7 +168,10 @@ class WorldRenderer(private val connection: Connection, private val world: World
fun prepareChunk(chunkLocation: ChunkLocation, chunk: Chunk) { fun prepareChunk(chunkLocation: ChunkLocation, chunk: Chunk) {
chunkSectionsToDraw[chunkLocation] = ConcurrentHashMap() chunkSectionsToDraw[chunkLocation] = ConcurrentHashMap()
for ((sectionHeight, section) in chunk.sections) { if (!chunk.isFullyLoaded) {
return
}
for ((sectionHeight, section) in chunk.sections!!) {
prepareChunkSection(chunkLocation, sectionHeight, section, chunk) prepareChunkSection(chunkLocation, sectionHeight, section, chunk)
} }
} }
@ -217,7 +217,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
} }
private fun prepareWorld(world: World) { private fun prepareWorld(world: World) {
for ((chunkLocation, chunk) in world.allChunks) { for ((chunkLocation, chunk) in world.chunks) {
prepareChunk(chunkLocation, chunk) 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.Directions
import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockInfo 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.ChunkMesh
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel
import de.bixilon.minosoft.gui.rendering.textures.Texture import de.bixilon.minosoft.gui.rendering.textures.Texture
import glm_.mat4x4.Mat4 import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
class BlockRenderer(data: JsonObject, parent: BlockModel) { class BlockRenderer(data: JsonObject, parent: BlockModel) {
private val transparentFaces: MutableSet<Directions> = mutableSetOf() 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?>) { fun render(blockInfo: BlockInfo, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: ChunkMesh, neighbourBlocks: Array<BlockInfo?>) {
val modelMatrix = Mat4().translate(position) val modelMatrix = Mat4().translate(position.toVec3())
for (direction in Directions.DIRECTIONS) { for (direction in Directions.DIRECTIONS) {
for (element in elements) { for (element in elements) {
@ -91,7 +92,7 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
continue 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 realDirection = directionMapping[direction]!!
val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal] val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[realDirection.ordinal]
@ -75,7 +75,8 @@ class ElementRenderer(element: BlockModelElement, rotation: Vec3, uvLock: Boolea
tintColor tintColor
} else { } else {
null null
} },
lightLevel = lightLevel,
) )
} }
} }

View File

@ -49,6 +49,7 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf( chatComponents[FontBindings.LEFT_UP]!!.addAll(listOf(
"FPS: ${getFPS()}", "FPS: ${getFPS()}",
"Timings: avg ${getAvgFrameTime()}ms, min ${getMinFrameTime()}ms, max ${getMaxFrameTime()}ms", "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}", "Connected to ${hudTextElement.connection.address} with ${hudTextElement.connection.version}",
"", "",
"XYZ ${getLocation()}", "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_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, 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)
// 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) 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?) 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) { 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 // tweak
if (!connection.getVersion().isFlattened()) { 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); section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block);
} else { } else {
section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock()); 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 // tweak
if (!connection.getVersion().isFlattened()) { if (!connection.getVersion().isFlattened()) {
for (Map.Entry<InChunkLocation, BlockState> entry : getBlocks().entrySet()) { 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()) { if (block == entry.getValue()) {
continue; continue;
} }

View File

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

View File

@ -13,7 +13,10 @@
package de.bixilon.minosoft.protocol.packets.clientbound.play; 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.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.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer; import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.ChunkUtil; 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; import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_20W49A;
public class PacketUpdateLight extends ClientboundPacket { public class PacketUpdateLight extends ClientboundPacket {
ChunkLocation location; private ChunkLocation location;
private LightAccessor lightAccessor;
@Override @Override
public boolean read(InByteBuffer buffer) { public boolean read(InByteBuffer buffer) {
@ -48,7 +52,7 @@ public class PacketUpdateLight extends ClientboundPacket {
emptySkyLightMask = buffer.readLongArray(); emptySkyLightMask = buffer.readLongArray();
emptyBlockLightMask = buffer.readLongArray(); emptyBlockLightMask = buffer.readLongArray();
} }
ChunkUtil.readSkyLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask); this.lightAccessor = ChunkUtil.readSkyLightPacket(buffer, skyLightMask, blockLightMask, emptyBlockLightMask, emptySkyLightMask);
return true; return true;
} }
@ -56,4 +60,11 @@ public class PacketUpdateLight extends ClientboundPacket {
public void log() { public void log() {
Log.protocol(String.format("[IN] Received light update (location=%s)", this.location)); 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.mappings.recipes.Ingredient;
import de.bixilon.minosoft.data.text.ChatComponent; import de.bixilon.minosoft.data.text.ChatComponent;
import de.bixilon.minosoft.data.world.BlockPosition; 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.protocol.network.Connection;
import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.Util;
import de.bixilon.minosoft.util.nbt.tag.*; import de.bixilon.minosoft.util.nbt.tag.*;
@ -591,4 +592,7 @@ public class InByteBuffer {
return new ResourceLocation(resourceLocation); 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.biomes.Biome;
import de.bixilon.minosoft.data.mappings.blocks.BlockState; import de.bixilon.minosoft.data.mappings.blocks.BlockState;
import de.bixilon.minosoft.data.world.BlockInfo; 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.ChunkSection;
import de.bixilon.minosoft.data.world.InChunkSectionLocation; 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.DummyBiomeAccessor;
import de.bixilon.minosoft.data.world.biome.XZBiomeAccessor; 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.data.world.palette.Palette;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer; import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
@ -33,7 +34,7 @@ import java.util.HashMap;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*; import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*;
public final class ChunkUtil { 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 (buffer.getVersionId() < V_14W26A) {
if (sectionBitMasks[0] == 0x00 && fullChunk) { if (sectionBitMasks[0] == 0x00 && fullChunk) {
// unload chunk // unload chunk
@ -97,7 +98,7 @@ public final class ChunkUtil {
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); // ToDo 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? if (buffer.getVersionId() < V_15W35A) { // ToDo: was this really changed in 62?
byte sections = BitByte.getBitCount(sectionBitMasks[0]); byte sections = BitByte.getBitCount(sectionBitMasks[0]);
@ -143,7 +144,7 @@ public final class ChunkUtil {
} }
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); 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 // really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712
HashMap<Integer, ChunkSection> sectionMap = new HashMap<>(); HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
@ -155,7 +156,7 @@ public final class ChunkUtil {
if (buffer.getVersionId() >= V_18W43A) { if (buffer.getVersionId() >= V_18W43A) {
buffer.readShort(); // block count buffer.readShort(); // block count
} }
Palette palette = Palette.choosePalette(buffer.readUnsignedByte()); Palette palette = Palette.Companion.choosePalette(buffer.readUnsignedByte());
palette.read(buffer); palette.read(buffer);
int individualValueMask = ((1 << palette.getBitsPerBlock()) - 1); int individualValueMask = ((1 << palette.getBitsPerBlock()) - 1);
@ -206,21 +207,23 @@ public final class ChunkUtil {
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); 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) { if (buffer.getVersionId() < V_19W36A && fullChunk) {
Biome[] biomes = new Biome[256]; Biome[] biomes = new Biome[256];
for (int i = 0; i < biomes.length; i++) { for (int i = 0; i < biomes.length; i++) {
biomes[i] = buffer.getConnection().getMapping().getBiomeRegistry().get(buffer.readInt()); 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(skyLightMask));
readLightArray(buffer, BitSet.valueOf(blockLightMask)); readLightArray(buffer, BitSet.valueOf(blockLightMask));
// ToDo // ToDo
return DummyLightAccessor.INSTANCE;
} }
private static void readLightArray(InByteBuffer buffer, BitSet lightMask) { private static void readLightArray(InByteBuffer buffer, BitSet lightMask) {

View File

@ -17,6 +17,7 @@ out vec4 outColor;
in vec3 passTextureCoordinates; in vec3 passTextureCoordinates;
in vec4 passTintColor; in vec4 passTintColor;
in float passLightLevel;
uniform sampler2DArray blockTextureArray; uniform sampler2DArray blockTextureArray;
@ -30,5 +31,5 @@ void main() {
if (passTintColor.a > 0.0f){ if (passTintColor.a > 0.0f){
mixedColor *= passTintColor.rgb; 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 = 3) in vec3 animatedTextureData;
layout (location = 4) in uint tintColor; layout (location = 4) in uint tintColor;
layout (location = 5) in float lightLevel;
out vec3 passTextureCoordinates; out vec3 passTextureCoordinates;
out vec4 passTintColor; out vec4 passTintColor;
out float passLightLevel;
uniform mat4 viewProjectionMatrix; uniform mat4 viewProjectionMatrix;
uniform int animationTick; uniform int animationTick;
@ -32,6 +34,8 @@ void main() {
gl_Position = viewProjectionMatrix * vec4(inPosition, 1.0f); 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); 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) { if (animatedTextureData.y == 1.0f) {
passTextureCoordinates = vec3(textureIndex, textureLayer); passTextureCoordinates = vec3(textureIndex, textureLayer);
return; return;