diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.java b/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.java deleted file mode 100644 index 863c3af43..000000000 --- a/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.java +++ /dev/null @@ -1,139 +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 . - * - * This software is not affiliated with Mojang AB, the original developer of Minecraft. - */ - -package de.bixilon.minosoft.data.mappings.tweaker; - -import de.bixilon.minosoft.data.entities.EntityMetaData; -import de.bixilon.minosoft.data.entities.EntityMetaDataFields; -import de.bixilon.minosoft.data.entities.entities.Entity; -import de.bixilon.minosoft.data.entities.entities.animal.horse.*; -import de.bixilon.minosoft.data.entities.entities.monster.*; -import de.bixilon.minosoft.data.entities.entities.vehicle.*; -import de.bixilon.minosoft.data.mappings.blocks.Block; -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.protocol.protocol.ProtocolDefinition; - -import java.util.Map; - -import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_8_9; - -public class VersionTweaker { - // some data was packed in mata data in early versions (1.8). This function converts it to the real identifier - @SuppressWarnings("deprecation") - public static Class getRealEntityClass(Class fakeClass, EntityMetaData metaData, int versionId) { - if (fakeClass == ZombiePigman.class) { - return ZombifiedPiglin.class; - } else if (fakeClass == Zombie.class) { - if (versionId > V_1_8_9) { // ToDo: No clue here - return fakeClass; - } - if (metaData.getSets().getInt(EntityMetaDataFields.ZOMBIE_SPECIAL_TYPE) == 1) { - return ZombieVillager.class; - } - } else if (fakeClass == Skeleton.class) { - if (versionId > V_1_8_9) { // ToDo: No clue here - return fakeClass; - } - if (metaData.getSets().getInt(EntityMetaDataFields.LEGACY_SKELETON_TYPE) == 1) { - return WitherSkeleton.class; - } - } else if (fakeClass == Guardian.class) { - if (versionId > V_1_8_9) { // ToDo: No clue here - return fakeClass; - } - if (metaData.getSets().getBitMask(EntityMetaDataFields.LEGACY_GUARDIAN_FLAGS, 0x02)) { - return ElderGuardian.class; - } - } else if (fakeClass == Horse.class) { - if (versionId > V_1_8_9) { // ToDo: No clue here - return fakeClass; - } - return switch (metaData.getSets().getByte(EntityMetaDataFields.LEGACY_HORSE_SPECIAL_TYPE)) { - default -> fakeClass; - case 1 -> Donkey.class; - case 2 -> Mule.class; - case 3 -> ZombieHorse.class; - case 4 -> SkeletonHorse.class; - }; - - } - return fakeClass; - } - - public static Class getRealEntityObjectClass(Class fakeClass, int data, int versionId) { - if (fakeClass == Minecart.class) { - if (versionId > V_1_8_9) { // ToDo: No clue here - return fakeClass; - } - return switch (data) { - default -> fakeClass; - case 1 -> MinecartChest.class; - case 2 -> MinecartFurnace.class; - case 3 -> MinecartTNT.class; - case 4 -> MinecartSpawner.class; - case 5 -> MinecartHopper.class; - case 6 -> MinecartCommandBlock.class; - }; - } - return fakeClass; - } - - public static Chunk transformChunk(Chunk chunk, int versionId) { - // some blocks need to be tweaked. eg. Grass with a snow block on top becomes snowy grass block - if (versionId >= ProtocolDefinition.FLATTING_VERSION_ID) { - return chunk; - } - for (Map.Entry sectionEntry : chunk.getSections().entrySet()) { - for (Map.Entry blockEntry : sectionEntry.getValue().getBlocks().entrySet()) { - Block newBlock = transformBlock(blockEntry.getValue(), chunk, blockEntry.getKey(), sectionEntry.getKey()); - if (newBlock == blockEntry.getValue()) { - continue; - } - sectionEntry.getValue().setBlock(blockEntry.getKey(), newBlock); - } - } - return chunk; - } - - public static Block transformBlock(Block originalBlock, Chunk chunk, InChunkLocation location) { - return transformBlock(originalBlock, chunk, location.getInChunkSectionLocation(), (byte) (location.getY() / ProtocolDefinition.SECTION_HEIGHT_Y)); - } - - public static Block transformBlock(Block originalBlock, Chunk chunk, InChunkSectionLocation location, int sectionHeight) { - if (originalBlock == null) { - return null; - } - switch (originalBlock.getIdentifier().getFullIdentifier()) { - case "minecraft:grass" -> { - Block above = getBlockAbove(chunk, location, sectionHeight); - if (above == null) { - break; - } - if (above.equals(TweakBlocks.SNOW) || above.equals(TweakBlocks.SNOW_LAYER)) { - return TweakBlocks.GRASS_BLOCK_SNOWY_YES; - } else { - return TweakBlocks.GRASS_BLOCK_SNOWY_NO; - } - } - // ToDo: all blocks. e.g. doors, etc - } - return originalBlock; - } - - private static Block getBlockAbove(Chunk chunk, InChunkSectionLocation location, int sectionHeight) { - return chunk.getBlock(location.getInChunkLocation(sectionHeight)); - } -} diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.kt b/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.kt new file mode 100644 index 000000000..eb31ee94f --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/mappings/tweaker/VersionTweaker.kt @@ -0,0 +1,128 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ +package de.bixilon.minosoft.data.mappings.tweaker + +import de.bixilon.minosoft.data.entities.EntityMetaData +import de.bixilon.minosoft.data.entities.EntityMetaDataFields +import de.bixilon.minosoft.data.entities.entities.Entity +import de.bixilon.minosoft.data.entities.entities.animal.horse.* +import de.bixilon.minosoft.data.entities.entities.monster.* +import de.bixilon.minosoft.data.entities.entities.vehicle.* +import de.bixilon.minosoft.data.mappings.blocks.Block +import de.bixilon.minosoft.data.world.* +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.protocol.protocol.ProtocolVersions + +object VersionTweaker { + // some data was packed in mata data in early versions (1.8). This function converts it to the real identifier + @JvmStatic + fun getRealEntityClass(fakeClass: Class, metaData: EntityMetaData, versionId: Int): Class { + if (versionId > ProtocolVersions.V_1_8_9) { // ToDo: No clue here + return fakeClass + } + when (fakeClass) { + ZombiePigman::class.java -> { + return ZombifiedPiglin::class.java + } + Zombie::class.java -> { + if (metaData.sets.getInt(EntityMetaDataFields.ZOMBIE_SPECIAL_TYPE) == 1) { + return ZombieVillager::class.java + } + } + Skeleton::class.java -> { + if (metaData.sets.getInt(EntityMetaDataFields.LEGACY_SKELETON_TYPE) == 1) { + return WitherSkeleton::class.java + } + } + Guardian::class.java -> { + if (metaData.sets.getBitMask(EntityMetaDataFields.LEGACY_GUARDIAN_FLAGS, 0x02)) { + return ElderGuardian::class.java + } + } + Horse::class.java -> { + return when (metaData.sets.getByte(EntityMetaDataFields.LEGACY_HORSE_SPECIAL_TYPE).toInt()) { + 1 -> Donkey::class.java + 2 -> Mule::class.java + 3 -> ZombieHorse::class.java + 4 -> SkeletonHorse::class.java + else -> fakeClass + } + } + } + return fakeClass + } + + @JvmStatic + fun getRealEntityObjectClass(fakeClass: Class, data: Int, versionId: Int): Class { + if (versionId > ProtocolVersions.V_1_8_9) { // ToDo: No clue here + return fakeClass + } + when (fakeClass) { + Minecart::class.java -> { + return when (data) { + 1 -> MinecartChest::class.java + 2 -> MinecartFurnace::class.java + 3 -> MinecartTNT::class.java + 4 -> MinecartSpawner::class.java + 5 -> MinecartHopper::class.java + 6 -> MinecartCommandBlock::class.java + else -> fakeClass + } + } + } + return fakeClass + } + + @JvmStatic + fun transformChunk(chunk: Chunk, versionId: Int): Chunk { + // some blocks need to be tweaked. eg. Grass with a snow block on top becomes snowy grass block + if (versionId >= ProtocolDefinition.FLATTING_VERSION_ID) { + return chunk + } + for ((sectionHeight, section) in chunk.sections) { + for ((location, blockInfo) in section.blocks) { + val newBlock = transformBlock(blockInfo.block, chunk, location, sectionHeight) + if (newBlock === blockInfo.block) { + continue + } + if (newBlock == null) { + section.setBlockInfo(location, null) + continue + } + section.setBlockInfo(location, BlockInfo(newBlock, blockInfo.metaData, section.blocksStaticInfo[location] ?: BlockStaticInfo())) + } + } + return chunk + } + + + @JvmStatic + fun transformBlock(originalBlock: Block, chunk: Chunk, location: InChunkSectionLocation, sectionHeight: Int): Block? { + when (originalBlock.identifier.fullIdentifier) { + "minecraft:grass" -> { + getBlockAbove(chunk, location, sectionHeight)?.let { + if (it == TweakBlocks.SNOW || it == TweakBlocks.SNOW_LAYER) { + return TweakBlocks.GRASS_BLOCK_SNOWY_YES + } + } + return TweakBlocks.GRASS_BLOCK_SNOWY_NO + } + } + return originalBlock + } + + private fun getBlockAbove(chunk: Chunk, location: InChunkSectionLocation, sectionHeight: Int): Block? { + val above = location.getInChunkLocation(sectionHeight) + return chunk.getBlockInfo(InChunkLocation(above.x, above.y + 1, above.z))?.block + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/BlockInfo.kt b/src/main/java/de/bixilon/minosoft/data/world/BlockInfo.kt new file mode 100644 index 000000000..a5f5d02ee --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/BlockInfo.kt @@ -0,0 +1,25 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world + +import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData +import de.bixilon.minosoft.data.mappings.blocks.Block + +data class BlockInfo( + val block: Block, + var metaData: BlockEntityMetaData? = null, + val info: BlockStaticInfo = BlockStaticInfo(), +) { + constructor(block: Block) : this(block, null) // ToDo: For java compatibility +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/BlockPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/BlockPosition.kt index 2f551dcd7..4260191c2 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/BlockPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/BlockPosition.kt @@ -45,7 +45,7 @@ data class BlockPosition(val x: Int, val y: Int, val z: Int) { fun getInChunkSectionLocation(): InChunkSectionLocation { val location = getInChunkLocation() - return InChunkSectionLocation(location.x, this.y % ProtocolDefinition.SECTION_HEIGHT_Y, location.z) + return InChunkSectionLocation(location.x, getSectionHeight(), location.z) } fun getSectionHeight(): Int { diff --git a/src/main/java/de/bixilon/minosoft/data/world/BlockStaticInfo.kt b/src/main/java/de/bixilon/minosoft/data/world/BlockStaticInfo.kt new file mode 100644 index 000000000..21f577571 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/BlockStaticInfo.kt @@ -0,0 +1,20 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world + +data class BlockStaticInfo( + var light: Int = 15, + var skyLight: Int = 15, + // ToDo: Biome +) diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java b/src/main/java/de/bixilon/minosoft/data/world/Chunk.java deleted file mode 100644 index f1b804d87..000000000 --- a/src/main/java/de/bixilon/minosoft/data/world/Chunk.java +++ /dev/null @@ -1,99 +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 . - * - * This software is not affiliated with Mojang AB, the original developer of Minecraft. - */ - -package de.bixilon.minosoft.data.world; - -import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData; -import de.bixilon.minosoft.data.mappings.blocks.Block; -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; - -import java.util.HashMap; - -/** - * Collection of 16 chunks sections - */ -public class Chunk { - private final HashMap sections; - - public Chunk(HashMap sections) { - this.sections = sections; - } - - public Block getBlock(InChunkLocation location) { - return getBlock(location.getX(), location.getY(), location.getZ()); - } - - public Block getBlock(int x, int y, int z) { - int section = (y / ProtocolDefinition.SECTION_HEIGHT_Y); - if (!this.sections.containsKey(section)) { - return null; - } - return this.sections.get(section).getBlock(x, y % ProtocolDefinition.SECTION_HEIGHT_Y, z); - } - - public void setBlock(int x, int y, int z, Block block) { - int section = y / ProtocolDefinition.SECTION_HEIGHT_Y; - createSection(section); - this.sections.get(section).setBlock(x, y % 16, z, block); - } - - void createSection(int height) { - if (this.sections.get(height) == null) { - // section was empty before, creating it - this.sections.put(height, new ChunkSection()); - } - } - - public void setBlocks(HashMap blocks) { - blocks.forEach(this::setBlock); - } - - public void setBlock(InChunkLocation location, Block block) { - int section = (location.getY() / ProtocolDefinition.SECTION_HEIGHT_Y); - createSection(section); - this.sections.get(section).setBlock(location.getInChunkSectionLocation(), block); - } - - public void setBlockEntityData(InChunkLocation position, BlockEntityMetaData data) { - ChunkSection section = this.sections.get((position.getY() / ProtocolDefinition.SECTION_HEIGHT_Y)); - if (section == null) { - return; - } - section.setBlockEntityData(position.getInChunkSectionLocation(), data); - } - - public BlockEntityMetaData getBlockEntityData(InChunkLocation position) { - ChunkSection section = this.sections.get((position.getY() / ProtocolDefinition.SECTION_HEIGHT_Y)); - if (section == null) { - return null; - } - return section.getBlockEntityData(position.getInChunkSectionLocation()); - } - - public void setBlockEntityData(HashMap blockEntities) { - blockEntities.forEach(this::setBlockEntityData); - } - - public HashMap getSections() { - return this.sections; - } - - public ChunkSection getSectionOrCreate(int sectionHeight) { - ChunkSection section = this.sections.get(sectionHeight); - if (section == null) { - section = new ChunkSection(); - this.sections.put(sectionHeight, section); - } - return section; - } -} diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt new file mode 100644 index 000000000..5a3d0d0ac --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt @@ -0,0 +1,69 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ +package de.bixilon.minosoft.data.world + +import de.bixilon.minosoft.data.mappings.blocks.Block +import java.util.* + +/** + * Collection of chunks sections (allocated in y) + */ +class Chunk(val sections: MutableMap = mutableMapOf()) { + + fun getBlockInfo(location: InChunkLocation): BlockInfo? { + return sections[location.getSectionHeight()]?.getBlockInfo(location.getInChunkSectionLocation()) + } + + fun getBlockInfo(x: Int, y: Int, z: Int): BlockInfo? { + return getBlockInfo(InChunkLocation(x, y, z)) + } + + fun setBlocks(blocks: HashMap) { + for ((location, blockInfo) in blocks) { + setBlock(location, blockInfo) + } + } + + fun setRawBlocks(blocks: HashMap) { + for ((location, blockInfo) in blocks) { + setRawBlock(location, blockInfo) + } + } + + fun setBlock(location: InChunkLocation, block: BlockInfo?) { + getSectionOrCreate(location.getSectionHeight()).setBlockInfo(location.getInChunkSectionLocation(), block) + } + + fun setRawBlock(location: InChunkLocation, block: Block?) { + getSectionOrCreate(location.getSectionHeight()).let { + val inChunkSectionLocation = location.getInChunkSectionLocation() + if (block == null) { + it.blocks.remove(inChunkSectionLocation) + return + } + it.setBlockInfo(inChunkSectionLocation, BlockInfo(block, info = it.blocksStaticInfo[inChunkSectionLocation] ?: BlockStaticInfo())) + } + + } + + fun getSectionOrCreate(sectionHeight: Int): ChunkSection { + return sections[sectionHeight].let { + var section = it + if (section == null) { + section = ChunkSection() + sections[sectionHeight] = section + } + section + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.java b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.java deleted file mode 100644 index ba1eda6ab..000000000 --- a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.java +++ /dev/null @@ -1,94 +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 . - * - * This software is not affiliated with Mojang AB, the original developer of Minecraft. - */ - -package de.bixilon.minosoft.data.world; - -import de.bixilon.minosoft.data.entities.block.BlockEntityMetaData; -import de.bixilon.minosoft.data.mappings.blocks.Block; - -import java.util.HashMap; - -/** - * Collection of 16x16x16 blocks - */ -public class ChunkSection { - private final HashMap blocks; - private final HashMap blockEntityMeta = new HashMap<>(); - private final HashMap light; - private final HashMap skyLight; - - public ChunkSection(HashMap blocks) { - this(blocks, new HashMap<>(), new HashMap<>()); - } - - public ChunkSection(HashMap blocks, HashMap light, HashMap skyLight) { - this.blocks = blocks; - this.light = light; - this.skyLight = skyLight; - } - - public ChunkSection() { - this(new HashMap<>()); - } - - public Block getBlock(int x, int y, int z) { - return getBlock(new InChunkSectionLocation(x, y, z)); - } - - public Block getBlock(InChunkSectionLocation loc) { - return this.blocks.get(loc); - } - - public void setBlock(int x, int y, int z, Block block) { - setBlock(new InChunkSectionLocation(x, y, z), block); - } - - public void setBlock(InChunkSectionLocation location, Block block) { - if (block == null) { - this.blocks.remove(location); - this.blockEntityMeta.remove(location); - return; - } - this.blocks.put(location, block); - this.blockEntityMeta.remove(location); - } - - public void setBlockEntityData(InChunkSectionLocation position, BlockEntityMetaData data) { - // ToDo check if block is really a block entity (command block, spawner, skull, flower pot) - this.blockEntityMeta.put(position, data); - } - - public HashMap getBlocks() { - return this.blocks; - } - - public HashMap getBlockEntityMeta() { - return this.blockEntityMeta; - } - - public HashMap getLight() { - return this.light; - } - - public HashMap getSkyLight() { - return this.skyLight; - } - - public BlockEntityMetaData getBlockEntityData(InChunkSectionLocation position) { - return this.blockEntityMeta.get(position); - } - - public void setBlockEntityData(HashMap blockEntities) { - blockEntities.forEach(this.blockEntityMeta::put); - } -} diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt new file mode 100644 index 000000000..c182b3f3d --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt @@ -0,0 +1,52 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ +package de.bixilon.minosoft.data.world + +import de.bixilon.minosoft.data.mappings.blocks.Block + +/** + * Collection of 16x16x16 blocks + */ +class ChunkSection constructor( + val blocks: MutableMap = mutableMapOf(), + val blocksStaticInfo: MutableMap = mutableMapOf(), +) { + + fun getBlockInfo(location: InChunkSectionLocation): BlockInfo? { + return blocks[location] + } + + fun setBlockInfo(location: InChunkSectionLocation, blockInfo: BlockInfo?) { + if (blockInfo == null) { + blocks.remove(location) + return + } + blocks[location] = blockInfo + } + + fun getBlockInfo(x: Int, y: Int, z: Int): BlockInfo? { + return getBlockInfo(InChunkSectionLocation(x, y, z)) + } + + fun updateStaticData() { + + } + + fun setRawBlock(location: InChunkSectionLocation, block: Block?) { + if (block == null) { + setBlockInfo(location, null) + return + } + setBlockInfo(location, BlockInfo(block, info = blocksStaticInfo[location] ?: BlockStaticInfo())) + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/World.java b/src/main/java/de/bixilon/minosoft/data/world/World.java index 01c7cb700..0b11e1441 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/World.java +++ b/src/main/java/de/bixilon/minosoft/data/world/World.java @@ -30,19 +30,19 @@ public class World { private final HashMap chunks = new HashMap<>(); private final HashBiMap entityIdMap = HashBiMap.create(); private final HashBiMap entityUUIDMap = HashBiMap.create(); - boolean hardcore; - boolean raining; - Dimension dimension; // used for sky color, etc + private boolean hardcore; + private boolean raining; + private Dimension dimension; // used for sky color, etc public HashMap getAllChunks() { return this.chunks; } @Nullable - public Block getBlock(BlockPosition pos) { + public BlockInfo getBlockInfo(BlockPosition pos) { ChunkLocation loc = pos.getChunkLocation(); if (getChunk(loc) != null) { - return getChunk(loc).getBlock(pos.getInChunkLocation()); + return getChunk(loc).getBlockInfo(pos.getInChunkLocation()); } return null; } @@ -53,7 +53,7 @@ public class World { public void setBlock(BlockPosition pos, Block block) { if (getChunk(pos.getChunkLocation()) != null) { - getChunk(pos.getChunkLocation()).setBlock(pos.getInChunkLocation(), block); + getChunk(pos.getChunkLocation()).setRawBlock(pos.getInChunkLocation(), block); } // do nothing if chunk is unloaded } @@ -125,16 +125,17 @@ public class World { if (chunk == null) { return; } - chunk.setBlockEntityData(position.getInChunkLocation(), data); + 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 BlockEntityMetaData getBlockEntityData(BlockPosition position) { - Chunk chunk = this.chunks.get(position.getChunkLocation()); - if (chunk == null) { - return null; - } - return chunk.getBlockEntityData(position.getInChunkLocation()); - } public void setBlockEntityData(HashMap blockEntities) { blockEntities.forEach(this::setBlockEntityData); diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkRenderer.kt index 16fddfd68..408ec33ba 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkRenderer.kt @@ -53,38 +53,40 @@ class ChunkRenderer(private val connection: Connection, private val world: World val west = world.allChunks[chunkLocation.getLocationByDirection(Directions.WEST)]?.sections?.get(sectionHeight) val east = world.allChunks[chunkLocation.getLocationByDirection(Directions.EAST)]?.sections?.get(sectionHeight) - for ((position, block) in section.blocks) { - val blockBelow: Block? = if (position.y == 0 && below != null) { - below.getBlock(position.x, ProtocolDefinition.SECTION_HEIGHT_Y - 1, position.z) + for ((position, blockInfo) in section.blocks) { + val blockBelow: BlockInfo? = if (position.y == 0 && below != null) { + below.getBlockInfo(position.x, ProtocolDefinition.SECTION_HEIGHT_Y - 1, position.z) } else { - section.getBlock(position.getLocationByDirection(Directions.DOWN)) + section.getBlockInfo(position.getLocationByDirection(Directions.DOWN)) } - val blockAbove: Block? = if (position.y == ProtocolDefinition.SECTION_HEIGHT_Y - 1 && above != null) { - above.getBlock(position.x, 0, position.z) + val blockAbove: BlockInfo? = if (position.y == ProtocolDefinition.SECTION_HEIGHT_Y - 1 && above != null) { + above.getBlockInfo(position.x, 0, position.z) } else { - section.getBlock(position.getLocationByDirection(Directions.UP)) + section.getBlockInfo(position.getLocationByDirection(Directions.UP)) } - val blockNorth: Block? = if (position.z == 0 && north != null) { - north.getBlock(position.x, position.y, ProtocolDefinition.SECTION_WIDTH_Z - 1) + val blockNorth: BlockInfo? = if (position.z == 0 && north != null) { + north.getBlockInfo(position.x, position.y, ProtocolDefinition.SECTION_WIDTH_Z - 1) } else { - section.getBlock(position.getLocationByDirection(Directions.NORTH)) + section.getBlockInfo(position.getLocationByDirection(Directions.NORTH)) } - val blockSouth: Block? = if (position.z == ProtocolDefinition.SECTION_WIDTH_Z - 1 && south != null) { - south.getBlock(position.x, position.y, 0) + val blockSouth: BlockInfo? = if (position.z == ProtocolDefinition.SECTION_WIDTH_Z - 1 && south != null) { + south.getBlockInfo(position.x, position.y, 0) } else { - section.getBlock(position.getLocationByDirection(Directions.SOUTH)) + section.getBlockInfo(position.getLocationByDirection(Directions.SOUTH)) } - val blockWest: Block? = if (position.x == 0 && west != null) { - west.getBlock(ProtocolDefinition.SECTION_WIDTH_X - 1, position.y, position.x) + val blockWest: BlockInfo? = if (position.x == 0 && west != null) { + west.getBlockInfo(ProtocolDefinition.SECTION_WIDTH_X - 1, position.y, position.x) } else { - section.getBlock(position.getLocationByDirection(Directions.WEST)) + section.getBlockInfo(position.getLocationByDirection(Directions.WEST)) } - val blockEast: Block? = if (position.x == ProtocolDefinition.SECTION_WIDTH_X - 1 && east != null) { - east.getBlock(0, position.y, position.z) + val blockEast: BlockInfo? = if (position.x == ProtocolDefinition.SECTION_WIDTH_X - 1 && east != null) { + east.getBlockInfo(0, position.y, position.z) } else { - section.getBlock(position.getLocationByDirection(Directions.EAST)) + section.getBlockInfo(position.getLocationByDirection(Directions.EAST)) } - block.getBlockRenderer(BlockPosition(chunkLocation, sectionHeight, position)).render(Vec3(position.x + chunkLocation.x * ProtocolDefinition.SECTION_WIDTH_X, position.y + sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y, position.z + chunkLocation.z * ProtocolDefinition.SECTION_WIDTH_Z), data, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast)) + 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) + + blockInfo.block.getBlockRenderer(BlockPosition(chunkLocation, sectionHeight, position)).render(blockInfo, worldPosition, arrayOf(blockBelow, blockAbove, blockNorth, blockSouth, blockWest, blockEast)) } return data.toFloatArray() } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketBlockChange.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketBlockChange.java index 37d647e20..e11b10eb4 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketBlockChange.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketBlockChange.java @@ -57,11 +57,10 @@ public class PacketBlockChange extends ClientboundPacket { // tweak if (!connection.getVersion().isFlattened()) { - Block block = VersionTweaker.transformBlock(getBlock(), chunk, getPosition().getInChunkLocation()); - section.setBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block); + Block block = VersionTweaker.transformBlock(getBlock(), chunk, this.position.getInChunkSectionLocation(), this.position.getSectionHeight()); + section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), block); } else { - Log.debug("Replacing %s with %s", section.getBlock(getPosition().getInChunkLocation().getInChunkSectionLocation()), this.block); - section.setBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock()); + section.setRawBlock(getPosition().getInChunkLocation().getInChunkSectionLocation(), getBlock()); } connection.getRenderer().getRenderWindow().getChunkRenderer().prepareChunkSection(getPosition().getChunkLocation(), sectionHeight, section); diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketMultiBlockChange.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketMultiBlockChange.java index 39b6e8491..69842e3e4 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketMultiBlockChange.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketMultiBlockChange.java @@ -33,7 +33,7 @@ import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*; public class PacketMultiBlockChange extends ClientboundPacket { private final HashMap blocks = new HashMap<>(); - ChunkLocation location; + private ChunkLocation location; @Override public boolean read(InByteBuffer buffer) { @@ -89,16 +89,16 @@ public class PacketMultiBlockChange extends ClientboundPacket { return; } connection.fireEvent(new MultiBlockChangeEvent(connection, this)); - chunk.setBlocks(getBlocks()); + chunk.setRawBlocks(getBlocks()); // tweak if (!connection.getVersion().isFlattened()) { for (Map.Entry entry : getBlocks().entrySet()) { - Block block = VersionTweaker.transformBlock(entry.getValue(), chunk, entry.getKey()); + Block block = VersionTweaker.transformBlock(entry.getValue(), chunk, entry.getKey().getInChunkSectionLocation(), entry.getKey().getSectionHeight()); if (block == entry.getValue()) { continue; } - chunk.setBlock(entry.getKey(), block); + chunk.setRawBlock(entry.getKey(), block); } } diff --git a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java index e67876a2d..fa7cf332d 100644 --- a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java +++ b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java @@ -15,6 +15,7 @@ package de.bixilon.minosoft.util; import de.bixilon.minosoft.data.mappings.Dimension; import de.bixilon.minosoft.data.mappings.blocks.Block; +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.InChunkSectionLocation; @@ -24,6 +25,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import java.util.BitSet; import java.util.HashMap; +import java.util.Map; import static de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*; @@ -56,7 +58,7 @@ public final class ChunkUtil { HashMap 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)) { - HashMap blockMap = new HashMap<>(); + HashMap blockMap = new HashMap<>(); for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { @@ -84,12 +86,12 @@ public final class ChunkUtil { continue; } Block block = buffer.getConnection().getMapping().getBlock(fullBlockId); - blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), block); + blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); arrayPos++; } } } - sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); + sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap, Map.of())); // ToDo } } return new Chunk(sectionMap); @@ -120,7 +122,7 @@ public final class ChunkUtil { if (!BitByte.isBitSet(sectionBitMasks[0], c)) { continue; } - HashMap blockMap = new HashMap<>(); + HashMap blockMap = new HashMap<>(); for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { @@ -131,12 +133,12 @@ public final class ChunkUtil { arrayPos++; continue; } - blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), block); + blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); arrayPos++; } } } - sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); + sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap, Map.of())); } return new Chunk(sectionMap); } @@ -156,7 +158,7 @@ public final class ChunkUtil { long[] data = buffer.readLongArray(); - HashMap blockMap = new HashMap<>(); + HashMap blockMap = new HashMap<>(); for (int nibbleY = 0; nibbleY < ProtocolDefinition.SECTION_HEIGHT_Y; nibbleY++) { for (int nibbleZ = 0; nibbleZ < ProtocolDefinition.SECTION_WIDTH_Z; nibbleZ++) { for (int nibbleX = 0; nibbleX < ProtocolDefinition.SECTION_WIDTH_X; nibbleX++) { @@ -187,7 +189,7 @@ public final class ChunkUtil { if (block == null) { continue; } - blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), block); + blockMap.put(new InChunkSectionLocation(nibbleX, nibbleY, nibbleZ), new BlockInfo(block)); } } } @@ -199,7 +201,7 @@ public final class ChunkUtil { } } - sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap)); + sectionMap.put(dimension.getLowestSection() + c, new ChunkSection(blockMap, Map.of())); } if (buffer.getVersionId() < V_19W36A) { byte[] biomes = buffer.readBytes(256);