rendering: proper light

This commit is contained in:
Bixilon 2021-02-27 21:54:58 +01:00
parent 312486cfb4
commit 93c1e32a49
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
20 changed files with 284 additions and 347 deletions

View File

@ -1,243 +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;
import de.bixilon.minosoft.data.accounts.Account;
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity;
import de.bixilon.minosoft.data.inventory.Inventory;
import de.bixilon.minosoft.data.inventory.InventoryProperties;
import de.bixilon.minosoft.data.inventory.InventorySlots;
import de.bixilon.minosoft.data.inventory.Slot;
import de.bixilon.minosoft.data.player.PlayerListItem;
import de.bixilon.minosoft.data.scoreboard.ScoreboardManager;
import de.bixilon.minosoft.data.text.ChatComponent;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.data.world.World;
import java.util.HashMap;
import java.util.UUID;
import static de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.PLAYER_INVENTORY_ID;
public class Player {
public final HashMap<UUID, PlayerListItem> playerList = new HashMap<>();
private final Account account;
private final ScoreboardManager scoreboardManager = new ScoreboardManager();
private final World world = new World();
private final HashMap<Integer, Inventory> inventories = new HashMap<>();
private float health;
private int food;
private float saturation;
private BlockPosition spawnPosition;
private GameModes gameMode;
private byte selectedSlot;
private int level;
private int totalExperience;
private PlayerEntity entity;
private boolean spawnConfirmed;
private UUID uuid;
private String playerName;
private ChatComponent tabHeader = ChatComponent.valueOf("");
private ChatComponent tabFooter = ChatComponent.valueOf("");
public Player(Account account) {
this.account = account;
this.uuid = account.getUUID();
this.playerName = account.getUsername();
// create our own inventory without any properties
this.inventories.put(PLAYER_INVENTORY_ID, new Inventory(null));
}
public String getPlayerName() {
return this.playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
public UUID getPlayerUUID() {
return this.uuid;
}
public void setPlayerUUID(UUID uuid) {
this.uuid = uuid;
}
public Account getAccount() {
return this.account;
}
public float getHealth() {
return this.health;
}
public void setHealth(float health) {
this.health = health;
}
public int getFood() {
return this.food;
}
public void setFood(int food) {
this.food = food;
}
public float getSaturation() {
return this.saturation;
}
public void setSaturation(float saturation) {
this.saturation = saturation;
}
public BlockPosition getSpawnPosition() {
return this.spawnPosition;
}
public void setSpawnPosition(BlockPosition spawnPosition) {
this.spawnPosition = spawnPosition;
}
public GameModes getGameMode() {
return this.gameMode;
}
public void setGameMode(GameModes gameMode) {
this.gameMode = gameMode;
}
public World getWorld() {
return this.world;
}
public byte getSelectedSlot() {
return this.selectedSlot;
}
public void setSelectedSlot(byte selectedSlot) {
this.selectedSlot = selectedSlot;
}
public int getLevel() {
return this.level;
}
public void setLevel(int level) {
this.level = level;
}
public int getTotalExperience() {
return this.totalExperience;
}
public void setTotalExperience(int totalExperience) {
this.totalExperience = totalExperience;
}
public Inventory getPlayerInventory() {
return getInventory(PLAYER_INVENTORY_ID);
}
public void setPlayerInventory(Slot[] data) {
setInventory(PLAYER_INVENTORY_ID, data);
}
public void setInventory(int windowId, Slot[] data) {
for (int i = 0; i < data.length; i++) {
setSlot(windowId, i, data[i]);
}
}
public void setSlot(int windowId, int slot, Slot data) {
this.inventories.get(windowId).setSlot(slot, data);
}
public Inventory getInventory(int id) {
return this.inventories.get(id);
}
public Slot getSlot(int windowId, InventorySlots.InventoryInterface slot, int versionId) {
return getSlot(windowId, slot.getId(versionId));
}
public Slot getSlot(int windowId, int slot) {
return this.inventories.get(windowId).getSlot(slot);
}
public void setSlot(int windowId, InventorySlots.InventoryInterface slot, int versionId, Slot data) {
setSlot(windowId, slot.getId(versionId), data);
}
public void createInventory(InventoryProperties properties) {
this.inventories.put(properties.getWindowId(), new Inventory(properties));
}
public void deleteInventory(int windowId) {
this.inventories.remove(windowId);
}
public boolean isSpawnConfirmed() {
return this.spawnConfirmed;
}
public void setSpawnConfirmed(boolean spawnConfirmed) {
this.spawnConfirmed = spawnConfirmed;
}
public ScoreboardManager getScoreboardManager() {
return this.scoreboardManager;
}
public HashMap<UUID, PlayerListItem> getPlayerList() {
return this.playerList;
}
public PlayerListItem getPlayerListItem(String name) {
// only legacy
for (PlayerListItem listItem : this.playerList.values()) {
if (listItem.getName().equals(name)) {
return listItem;
}
}
return null;
}
public ChatComponent getTabHeader() {
return this.tabHeader;
}
public void setTabHeader(ChatComponent tabHeader) {
this.tabHeader = tabHeader;
}
public ChatComponent getTabFooter() {
return this.tabFooter;
}
public void setTabFooter(ChatComponent tabFooter) {
this.tabFooter = tabFooter;
}
public PlayerEntity getEntity() {
return this.entity;
}
public void setEntity(PlayerEntity entity) {
this.entity = entity;
}
}

View File

@ -0,0 +1,104 @@
/*
* 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
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.inventory.Inventory
import de.bixilon.minosoft.data.inventory.InventoryProperties
import de.bixilon.minosoft.data.inventory.InventorySlots.InventoryInterface
import de.bixilon.minosoft.data.inventory.Slot
import de.bixilon.minosoft.data.player.PlayerListItem
import de.bixilon.minosoft.data.scoreboard.ScoreboardManager
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import java.util.*
class Player(val account: Account) {
val playerList = HashMap<UUID, PlayerListItem>()
val scoreboardManager = ScoreboardManager()
val world = World()
private val inventories = HashMap<Int, Inventory>()
var health = 0f
var food = 0
var saturation = 0f
var spawnPosition: BlockPosition? = null
var gameMode: GameModes? = null
var selectedSlot: Byte = 0
var level = 0
var totalExperience = 0
var entity: PlayerEntity? = null
var isSpawnConfirmed = false
var playerUUID: UUID = account.uuid
var playerName: String = account.username
var tabHeader = ChatComponent.valueOf("")!!
var tabFooter = ChatComponent.valueOf("")!!
val playerInventory: Inventory?
get() = getInventory(ProtocolDefinition.PLAYER_INVENTORY_ID)
fun setPlayerInventory(data: Array<Slot?>) {
setInventory(ProtocolDefinition.PLAYER_INVENTORY_ID, data)
}
fun setInventory(windowId: Int, data: Array<Slot?>) {
for (i in data.indices) {
setSlot(windowId, i, data[i])
}
}
fun setSlot(windowId: Int, slot: Int, data: Slot?) {
inventories[windowId]!!.setSlot(slot, data)
}
fun getInventory(id: Int): Inventory? {
return inventories[id]
}
fun getSlot(windowId: Int, slot: InventoryInterface, versionId: Int): Slot {
return getSlot(windowId, slot.getId(versionId))
}
fun getSlot(windowId: Int, slot: Int): Slot {
return inventories[windowId]!!.getSlot(slot)
}
fun setSlot(windowId: Int, slot: InventoryInterface, versionId: Int, data: Slot?) {
setSlot(windowId, slot.getId(versionId), data)
}
fun createInventory(properties: InventoryProperties) {
inventories[properties.windowId] = Inventory(properties)
}
fun deleteInventory(windowId: Int) {
inventories.remove(windowId)
}
fun getPlayerListItem(name: String): PlayerListItem? {
// only legacy
for (listItem in playerList.values) {
if (listItem.name == name) {
return listItem
}
}
return null
}
init {
// create our own inventory without any properties
inventories[ProtocolDefinition.PLAYER_INVENTORY_ID] = Inventory(null)
}
}

View File

@ -45,12 +45,15 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) {
return InChunkPosition(x, this.y, z)
}
infix operator fun plus(vec3: Vec3): BlockPosition {
infix operator fun plus(vec3: Vec3?): BlockPosition {
if (vec3 == null) {
return this
}
return BlockPosition((x + vec3.x).toInt(), (y + vec3.y).toInt(), (z + vec3.z).toInt())
}
operator fun plus(directions: Directions): BlockPosition {
return this + directions.directionVector
infix operator fun plus(directions: Directions?): BlockPosition {
return this + directions?.directionVector
}
fun getInChunkSectionPosition(): InChunkSectionPosition {

View File

@ -12,6 +12,7 @@
*/
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
@ -34,10 +35,17 @@ data class InChunkPosition(val x: Int, val y: Int, val z: Int) {
}
}
operator fun plus(vec3: Vec3): InChunkPosition {
operator fun plus(vec3: Vec3?): InChunkPosition {
if (vec3 == null) {
return this
}
return InChunkPosition((x + vec3.x).toInt(), (y + vec3.y).toInt(), (z + vec3.z).toInt())
}
operator fun plus(direction: Directions?): InChunkPosition {
return this + direction?.directionVector
}
override fun toString(): String {
return "($x $y $z)"
}

View File

@ -55,6 +55,11 @@ data class InChunkSectionPosition(val x: Int, val y: Int, val z: Int) {
return InChunkSectionPosition(nextX, nextY, nextZ)
}
fun isEdge(): Boolean {
return x == 0 || y == 0 || z == 0 || x == ProtocolDefinition.SECTION_MAX_X || y == ProtocolDefinition.SECTION_MAX_Y || z == ProtocolDefinition.SECTION_MAX_Z
}
operator fun plus(directions: Directions): InChunkSectionPosition {
return this + directions.directionVector
}

View File

@ -13,10 +13,12 @@
package de.bixilon.minosoft.data.world
import com.google.common.collect.HashBiMap
import de.bixilon.minosoft.data.Difficulties
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 de.bixilon.minosoft.data.world.light.WorldLightAccessor
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@ -30,7 +32,9 @@ class World {
var isHardcore = false
var isRaining = false
var dimension: Dimension? = null
var difficulty: Difficulties? = null
var difficultyLocked = false
val worldLightAccessor = WorldLightAccessor(this)
fun getBlockInfo(blockPosition: BlockPosition): BlockInfo? {
val chunkLocation = blockPosition.getChunkPosition()

View File

@ -13,24 +13,28 @@
package de.bixilon.minosoft.data.world.light
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.InChunkPosition
import de.bixilon.minosoft.data.world.World
class ChunkLightAccessor(
val blockLightLevel: MutableMap<InChunkPosition, Byte> = mutableMapOf(),
val skyLightLevel: MutableMap<InChunkPosition, Byte> = mutableMapOf(),
val world: World,
private val blockLightLevel: MutableMap<Int, MutableMap<InChunkPosition, Byte>> = mutableMapOf(),
private val skyLightLevel: MutableMap<Int, MutableMap<InChunkPosition, Byte>> = mutableMapOf(),
) : LightAccessor {
override fun getLightLevel(blockPosition: BlockPosition, direction: Directions): Int {
val inChunkPosition = blockPosition.getInChunkPosition()
val lightLevel = blockLightLevel[inChunkPosition] ?: skyLightLevel[inChunkPosition]
if (lightLevel == null) {
return 1
override fun getSkyLight(blockPosition: BlockPosition): Byte {
return skyLightLevel[blockPosition.getSectionHeight()]?.get(blockPosition.getInChunkPosition()) ?: 0
}
return lightLevel.toInt()
override fun getBlockLight(blockPosition: BlockPosition): Byte {
return blockLightLevel[blockPosition.getSectionHeight()]?.get(blockPosition.getInChunkPosition()) ?: 0
}
fun merge(chunkLightAccessor: ChunkLightAccessor) {
for ((sectionHeight, section) in chunkLightAccessor.blockLightLevel) {
blockLightLevel[sectionHeight] = section
}
for ((sectionHeight, section) in chunkLightAccessor.skyLightLevel) {
skyLightLevel[sectionHeight] = section
}
}
}

View File

@ -13,19 +13,15 @@
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 getSkyLight(blockPosition: BlockPosition): Byte {
return 15
}
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
}
override fun getBlockLight(blockPosition: BlockPosition): Byte {
return 15
}
}

View File

@ -13,10 +13,21 @@
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
fun getSkyLight(blockPosition: BlockPosition): Byte
fun getBlockLight(blockPosition: BlockPosition): Byte
fun getLightLevel(blockPosition: BlockPosition): Byte {
val blockLight = getBlockLight(blockPosition)
val skyLight = getSkyLight(blockPosition)
if (blockLight > skyLight) {
return blockLight
}
return skyLight
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.world.BlockPosition
import de.bixilon.minosoft.data.world.World
class WorldLightAccessor(
private val world: World,
) : LightAccessor {
override fun getSkyLight(blockPosition: BlockPosition): Byte {
return world.chunks[blockPosition.getChunkPosition()]?.lightAccessor?.getSkyLight(blockPosition) ?: 0
}
override fun getBlockLight(blockPosition: BlockPosition): Byte {
return world.chunks[blockPosition.getChunkPosition()]?.lightAccessor?.getBlockLight(blockPosition) ?: 0
}
}

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?, lightLevel: Float = 0.9f) {
fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Byte = 14) {
data.add(position.x)
data.add(position.y)
data.add(position.z)
@ -48,7 +48,7 @@ class ChunkMesh {
data.add(Float.fromBits(tintColor.color))
}
data.add(lightLevel)
data.add(lightLevel / MAX_LIGHT_LEVEL)
}
fun load() {
@ -99,5 +99,6 @@ class ChunkMesh {
companion object {
private const val FLOATS_PER_VERTEX = 11
private const val MAX_LIGHT_LEVEL = 15f
}
}

View File

@ -77,7 +77,7 @@ class WorldRenderer(private val connection: Connection, private val world: World
blockInfo.block.tintColor?.let { tintColor = it }
}
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, chunk.lightAccessor!!, tintColor, blockPosition, mesh, neighborBlocks)
blockInfo.block.getBlockRenderer(blockPosition).render(blockInfo, world.worldLightAccessor, tintColor, blockPosition, mesh, neighborBlocks)
}
return mesh
}

View File

@ -92,7 +92,8 @@ class BlockRenderer(data: JsonObject, parent: BlockModel) {
continue
}
element.render(tintColor, lightAccessor.getLightLevel(position, direction) / 15f, textureMapping, modelMatrix, direction, mesh)
// ToDo: Lightning is determined by cullface attribute
element.render(tintColor, lightAccessor.getLightLevel(position + direction), textureMapping, modelMatrix, direction, mesh)
}
}
}

View File

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

View File

@ -59,6 +59,15 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
"Facing ${getFacing()}",
"Dimension ${hudTextElement.connection.player.world.dimension}",
"Biome ${camera.currentBiome}",
"",
"Difficulty ${hudTextElement.connection.player.world.difficulty?.name?.toLowerCase()}, ${
if (hudTextElement.connection.player.world.difficultyLocked) {
"locked"
} else {
"unlocked"
}
}",
"Client light: ${hudTextElement.connection.player.world.worldLightAccessor.getLightLevel(camera.blockPosition)} (sky=${hudTextElement.connection.player.world.worldLightAccessor.getSkyLight(camera.blockPosition)}, block=${hudTextElement.connection.player.world.worldLightAccessor.getBlockLight(camera.blockPosition)})"
))
chatComponents[FontBindings.RIGHT_UP]!!.addAll(listOf(
"Java: ${Runtime.version()} ${System.getProperty("sun.arch.data.model")}bit",
@ -160,7 +169,8 @@ class HUDDebugScreenElement(private val hudTextElement: HUDTextElement) : HUDTex
private fun getFacing(): String {
val yaw = hudTextElement.renderWindow.camera.yaw
val pitch = hudTextElement.renderWindow.camera.pitch
return "${Directions.byDirection(camera.cameraFront).name.toLowerCase()} (${formatRotation(yaw)} / ${formatRotation(pitch)})"
val direction = Directions.byDirection(camera.cameraFront)
return "${Directions.byDirection(camera.cameraFront).name.toLowerCase()} (${direction.directionVector} (${formatRotation(yaw)} / ${formatRotation(pitch)})"
}

View File

@ -1,44 +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.Difficulties;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.util.logging.Log;
import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_19W11A;
public class PacketServerDifficulty extends ClientboundPacket {
Difficulties difficulty;
boolean locked;
@Override
public boolean read(InByteBuffer buffer) {
this.difficulty = Difficulties.byId(buffer.readUnsignedByte());
if (buffer.getVersionId() > V_19W11A) {
this.locked = buffer.readBoolean();
}
return true;
}
@Override
public void log() {
Log.protocol(String.format("[IN] Received server difficulty (difficulty=%s)", this.difficulty));
}
public Difficulties getDifficulty() {
return this.difficulty;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.Difficulties
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.util.logging.Log
class PacketServerDifficulty : ClientboundPacket() {
lateinit var difficulty: Difficulties
var locked = false
override fun read(buffer: InByteBuffer): Boolean {
difficulty = Difficulties.byId(buffer.readUnsignedByte().toInt())
if (buffer.versionId > ProtocolVersions.V_19W11A) {
locked = buffer.readBoolean()
}
return true
}
override fun handle(connection: Connection) {
connection.player.world.difficulty = difficulty
connection.player.world.difficultyLocked = locked
}
override fun log() {
Log.protocol("[IN] Received server difficulty (difficulty=$difficulty, locked=${locked})")
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.protocol.packets.clientbound.play
import de.bixilon.minosoft.data.world.ChunkPosition
import de.bixilon.minosoft.data.world.light.ChunkLightAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.packets.ClientboundPacket
@ -27,7 +28,7 @@ class PacketUpdateLight : ClientboundPacket() {
override fun read(buffer: InByteBuffer): Boolean {
position = ChunkPosition(buffer.readVarInt(), buffer.readVarInt())
if (position == ChunkPosition(-1, 21)) {
if (position == ChunkPosition(-6, 20)) {
Log.debug("")
}
if (buffer.versionId >= ProtocolVersions.V_1_16_PRE3) {
@ -61,7 +62,11 @@ class PacketUpdateLight : ClientboundPacket() {
override fun handle(connection: Connection) {
val chunk = connection.player.world.getOrCreateChunk(position!!)
if (chunk.lightAccessor != null && chunk.lightAccessor is ChunkLightAccessor && lightAccessor is ChunkLightAccessor) {
(chunk.lightAccessor as ChunkLightAccessor).merge(lightAccessor as ChunkLightAccessor)
} else {
chunk.lightAccessor = lightAccessor
}
connection.renderer.renderWindow.worldRenderer.prepareChunk(position!!, chunk)
}
}

View File

@ -60,8 +60,8 @@ public final class ChunkUtil {
// parse data
int arrayPos = 0;
HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMasks[0], c)) {
for (int sectionHeight = dimension.getLowestSection(); sectionHeight < dimension.getHighestSection(); sectionHeight++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) {
HashMap<InChunkSectionPosition, BlockInfo> blockMap = new HashMap<>();
for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) {
@ -73,13 +73,13 @@ public final class ChunkUtil {
if (arrayPos % 2 == 0) {
// high bits
singleMeta = (byte) (meta[arrayPos / 2] & 0xF);
if (BitByte.isBitSet(addBitMask, c)) {
if (BitByte.isBitSet(addBitMask, sectionHeight)) {
singeBlockId = (short) ((singeBlockId << 4) | (addBlockTypes[arrayPos / 2] >>> 4));
}
} else {
// low 4 bits
singleMeta = (byte) ((meta[arrayPos / 2] >>> 4) & 0xF);
if (BitByte.isBitSet(addBitMask, c)) {
if (BitByte.isBitSet(addBitMask, sectionHeight)) {
singeBlockId = (short) ((singeBlockId << 4) | (addBlockTypes[arrayPos / 2] & 0xF));
}
}
@ -95,7 +95,7 @@ public final class ChunkUtil {
}
}
}
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); // ToDo
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap)); // ToDo
}
}
return new ChunkData(sectionMap, new DummyBiomeAccessor(buffer.getConnection().getMapping().getBiomeRegistry().get(0)), DummyLightAccessor.INSTANCE);
@ -122,8 +122,8 @@ public final class ChunkUtil {
int arrayPos = 0;
HashMap<Integer, ChunkSection> sectionMap = new HashMap<>();
for (int c = 0; c < ProtocolDefinition.SECTIONS_PER_CHUNK; c++) { // max sections per chunks in chunk column
if (!BitByte.isBitSet(sectionBitMasks[0], c)) {
for (int sectionHeight = dimension.getLowestSection(); sectionHeight < dimension.getHighestSection(); sectionHeight++) { // max sections per chunks in chunk column
if (!BitByte.isBitSet(sectionBitMasks[0], sectionHeight)) {
continue;
}
HashMap<InChunkSectionPosition, BlockInfo> blockMap = new HashMap<>();
@ -142,15 +142,15 @@ public final class ChunkUtil {
}
}
}
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap));
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap));
}
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<>();
BitSet sectionBitSet = BitSet.valueOf(sectionBitMasks);
for (int c = 0; c < sectionBitSet.length(); c++) { // max sections per chunks in chunk column
if (!sectionBitSet.get(c)) {
for (int sectionHeight = dimension.getLowestSection(); sectionHeight < sectionBitSet.length(); sectionHeight++) { // max sections per chunks in chunk column
if (!sectionBitSet.get(sectionHeight)) {
continue;
}
if (buffer.getVersionId() >= V_18W43A) {
@ -206,7 +206,7 @@ public final class ChunkUtil {
// ToDo
}
sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap));
sectionMap.put(dimension.getLowestSection() + sectionHeight, new ChunkSection(blockMap));
}
ChunkData chunkData = new ChunkData();
chunkData.setBlocks(sectionMap);

View File

@ -16,7 +16,6 @@ package de.bixilon.minosoft.util.chunk
import de.bixilon.minosoft.data.mappings.Dimension
import de.bixilon.minosoft.data.world.InChunkPosition
import de.bixilon.minosoft.data.world.light.ChunkLightAccessor
import de.bixilon.minosoft.data.world.light.DummyLightAccessor
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.protocol.protocol.InByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
@ -26,16 +25,16 @@ object LightUtil {
fun readLightPacket(buffer: InByteBuffer, skyLightMask: LongArray, blockLightMask: LongArray, emptyBlockLightMask: LongArray, emptySkyLightMask: LongArray, dimension: Dimension): LightAccessor {
// ToDo
val blockLight = readLightArray(buffer, BitSet.valueOf(blockLightMask), dimension)
if (!dimension.hasSkyLight) {
return ChunkLightAccessor(blockLight, world = buffer.connection.player.world)
val skyLight = if (dimension.hasSkyLight) {
readLightArray(buffer, BitSet.valueOf(skyLightMask), dimension)
} else {
mutableMapOf()
}
val skyLight = readLightArray(buffer, BitSet.valueOf(skyLightMask), dimension)
return DummyLightAccessor
return ChunkLightAccessor(blockLight, skyLight, buffer.connection.player.world)
val blockLight = readLightArray(buffer, BitSet.valueOf(blockLightMask), dimension)
return ChunkLightAccessor(blockLight, skyLight)
}
private fun readLightArray(buffer: InByteBuffer, lightMask: BitSet, dimension: Dimension): MutableMap<InChunkPosition, Byte> {
private fun readLightArray(buffer: InByteBuffer, lightMask: BitSet, dimension: Dimension): MutableMap<Int, MutableMap<InChunkPosition, Byte>> {
var highestSectionIndex = dimension.highestSection + 1
val lowesSectionIndex = dimension.lowestSection - 1
if (buffer.versionId >= ProtocolVersions.V_20W49A) {
@ -43,10 +42,11 @@ object LightUtil {
highestSectionIndex = lightMask.length()
}
val lightLevels: MutableMap<InChunkPosition, Byte> = mutableMapOf()
val lightLevels: MutableMap<Int, MutableMap<InChunkPosition, Byte>> = mutableMapOf()
for ((arrayIndex, c) in (lowesSectionIndex until highestSectionIndex).withIndex()) { // light sections
for ((arrayIndex, sectionHeight) in (lowesSectionIndex until highestSectionIndex).withIndex()) { // light sections
val currentSectionLightLevel: MutableMap<InChunkPosition, Byte> = mutableMapOf()
if (!lightMask[arrayIndex]) {
continue
}
@ -55,12 +55,13 @@ object LightUtil {
for (y in 0 until 16) {
for (z in 0 until 16) {
for (x in 0 until 16 step 2) {
lightLevels[InChunkPosition(x, y + c * 16, z)] = (lightArray[index].toInt() and 0x0F).toByte()
lightLevels[InChunkPosition(x + 1, y + c * 16, z)] = ((lightArray[index].toInt() ushr 4) and 0x0F).toByte()
currentSectionLightLevel[InChunkPosition(x, y + sectionHeight * 16, z)] = (lightArray[index].toInt() and 0x0F).toByte()
currentSectionLightLevel[InChunkPosition(x + 1, y + sectionHeight * 16, z)] = ((lightArray[index].toInt() ushr 4) and 0x0F).toByte()
index++
}
}
}
lightLevels[sectionHeight] = currentSectionLightLevel
}
return lightLevels
}