From b41a7b369488b1e7fba2f79359a79d150c9de323 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 30 Sep 2020 16:35:02 +0200 Subject: [PATCH] rendering: improve face preparation, fix some bugs in world rendering/handling --- .../game/datatypes/world/BlockPosition.java | 1 + .../minosoft/game/datatypes/world/Chunk.java | 13 +- .../game/datatypes/world/ChunkNibble.java | 20 ++- .../minosoft/game/datatypes/world/World.java | 19 +-- .../clientbound/play/PacketChunkData.java | 6 +- .../play/PacketMultiBlockChange.java | 2 +- .../protocol/protocol/PacketHandler.java | 15 +- .../minosoft/render/WorldRenderer.java | 138 ++++++++++++++---- .../de/bixilon/minosoft/util/ChunkUtil.java | 14 +- 9 files changed, 158 insertions(+), 70 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/BlockPosition.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/BlockPosition.java index 52d1d82f9..0e818ef54 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/world/BlockPosition.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/BlockPosition.java @@ -63,6 +63,7 @@ public class BlockPosition { public ChunkLocation getChunkLocation() { int x = getX() / 16; int z = getZ() / 16; + //ToDo if (getX() < 0) { x--; } diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/Chunk.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/Chunk.java index b5e636736..cc7d5739f 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/world/Chunk.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/Chunk.java @@ -18,14 +18,15 @@ import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Blocks; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Collection of 16 chunks nibbles */ public class Chunk { - final HashMap nibbles; + final ConcurrentHashMap nibbles; - public Chunk(HashMap chunks) { + public Chunk(ConcurrentHashMap chunks) { this.nibbles = chunks; } @@ -46,17 +47,17 @@ public class Chunk { public void setBlock(int x, int y, int z, Block block) { byte section = (byte) (y / 16); - createSection(section); + createSectionIfNotExist(section); nibbles.get(section).setBlock(x, y % 16, z, block); } public void setBlock(InChunkLocation location, Block block) { byte section = (byte) (location.getY() / 16); - createSection(section); + createSectionIfNotExist(section); nibbles.get(section).setBlock(location.getChunkNibbleLocation(), block); } - void createSection(byte section) { + void createSectionIfNotExist(byte section) { if (nibbles.get(section) == null) { // nibble was empty before, creating it nibbles.put(section, new ChunkNibble()); @@ -69,7 +70,7 @@ public class Chunk { } } - public HashMap getNibbles() { + public ConcurrentHashMap getNibbles() { return nibbles; } } diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/ChunkNibble.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/ChunkNibble.java index 2445e1a28..01f633b34 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/world/ChunkNibble.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/ChunkNibble.java @@ -14,25 +14,29 @@ package de.bixilon.minosoft.game.datatypes.world; import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Block; +import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Blocks; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; /** * Collection of 16x16x16 blocks */ public class ChunkNibble { - final HashMap blocks; + final ConcurrentHashMap blocks; - public ChunkNibble(HashMap blocks) { + public ChunkNibble(ConcurrentHashMap blocks) { this.blocks = blocks; } public ChunkNibble() { // empty - this.blocks = new HashMap<>(); + this.blocks = new ConcurrentHashMap<>(); } public Block getBlock(ChunkNibbleLocation loc) { + if (!blocks.containsKey(loc)) { + return null; + } return blocks.get(loc); } @@ -41,14 +45,18 @@ public class ChunkNibble { } public void setBlock(int x, int y, int z, Block block) { - blocks.put(new ChunkNibbleLocation(x, y, z), block); + setBlock(new ChunkNibbleLocation(x, y, z), block); } public void setBlock(ChunkNibbleLocation location, Block block) { + if (block == null || block.equals(Blocks.nullBlock)) { + blocks.remove(location); + return; + } blocks.put(location, block); } - public HashMap getBlocks() { + public ConcurrentHashMap getBlocks() { return blocks; } } diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/World.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/World.java index a5acd585d..c21864766 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/world/World.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/World.java @@ -17,20 +17,20 @@ import de.bixilon.minosoft.game.datatypes.entities.Entity; import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Block; import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Blocks; import de.bixilon.minosoft.game.datatypes.objectLoader.dimensions.Dimension; -import de.bixilon.minosoft.render.GameWindow; import de.bixilon.minosoft.util.nbt.tag.CompoundTag; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Collection of ChunkColumns */ public class World { - final HashMap chunks = new HashMap<>(); - final HashMap entities = new HashMap<>(); + final ConcurrentHashMap chunks = new ConcurrentHashMap<>(); + final ConcurrentHashMap entities = new ConcurrentHashMap<>(); final String name; - final HashMap blockEntityMeta = new HashMap<>(); + final ConcurrentHashMap blockEntityMeta = new ConcurrentHashMap<>(); boolean hardcore; boolean raining; Dimension dimension; // used for sky color, etc @@ -47,7 +47,7 @@ public class World { return chunks.get(loc); } - public HashMap getAllChunks() { + public ConcurrentHashMap getAllChunks() { return chunks; } @@ -65,7 +65,6 @@ public class World { public void setBlock(BlockPosition pos, Block block) { if (getChunk(pos.getChunkLocation()) != null) { getChunk(pos.getChunkLocation()).setBlock(pos.getInChunkLocation(), block); - GameWindow.getRenderer().prepareChunkNibble(pos.getChunkLocation(), (byte) (pos.getY() / 16), getChunk(pos.getChunkLocation()).getNibbles().get((byte) (pos.getY() / 16))); } // do nothing if chunk is unloaded } @@ -76,14 +75,10 @@ public class World { public void setChunk(ChunkLocation location, Chunk chunk) { chunks.put(location, chunk); - GameWindow.getRenderer().queueChunk(location, chunk); } public void setChunks(HashMap chunkMap) { - for (Map.Entry set : chunkMap.entrySet()) { - chunks.put(set.getKey(), set.getValue()); - } - GameWindow.getRenderer().queueChunkBulk(chunkMap); + chunkMap.forEach(chunks::put); } public boolean isHardcore() { @@ -135,7 +130,7 @@ public class World { return blockEntityMeta.get(position); } - public void setBlockEntityData(HashMap blockEntities) { + public void setBlockEntityData(ConcurrentHashMap blockEntities) { for (Map.Entry entrySet : blockEntities.entrySet()) { blockEntityMeta.put(entrySet.getKey(), entrySet.getValue()); } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java index da59b57e0..ed2b09332 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java @@ -24,10 +24,10 @@ import de.bixilon.minosoft.util.ChunkUtil; import de.bixilon.minosoft.util.Util; import de.bixilon.minosoft.util.nbt.tag.CompoundTag; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; public class PacketChunkData implements ClientboundPacket { - final HashMap blockEntities = new HashMap<>(); + final ConcurrentHashMap blockEntities = new ConcurrentHashMap<>(); ChunkLocation location; Chunk chunk; CompoundTag heightMap; @@ -122,7 +122,7 @@ public class PacketChunkData implements ClientboundPacket { return chunk; } - public HashMap getBlockEntities() { + public ConcurrentHashMap getBlockEntities() { return blockEntities; } 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 d46cd8750..54f9cbe68 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 @@ -55,7 +55,7 @@ public class PacketMultiBlockChange implements ClientboundPacket { byte pos = buffer.readByte(); byte y = buffer.readByte(); int blockId = buffer.readVarInt(); - blocks.put(new InChunkLocation((pos & 0xF0 >>> 4) & 0xF, y, pos & 0xF), buffer.getConnection().getMapping().getBlockById(blockId)); + blocks.put(new InChunkLocation((pos >>> 4) & 0xF, y, pos & 0xF), buffer.getConnection().getMapping().getBlockById(blockId)); } return true; } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java index e003f3c01..b775dbced 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java @@ -29,6 +29,7 @@ import de.bixilon.minosoft.game.datatypes.scoreboard.ScoreboardScore; import de.bixilon.minosoft.game.datatypes.scoreboard.Team; import de.bixilon.minosoft.game.datatypes.world.BlockPosition; import de.bixilon.minosoft.game.datatypes.world.Chunk; +import de.bixilon.minosoft.game.datatypes.world.ChunkLocation; import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.protocol.network.Connection; import de.bixilon.minosoft.protocol.packets.clientbound.login.*; @@ -38,10 +39,10 @@ import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusRespo import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse; import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketKeepAliveResponse; import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketResourcePackStatus; -import de.bixilon.minosoft.util.nbt.tag.CompoundTag; -import de.bixilon.minosoft.util.nbt.tag.StringTag; import de.bixilon.minosoft.render.GameWindow; import de.bixilon.minosoft.render.utility.Vec3; +import de.bixilon.minosoft.util.nbt.tag.CompoundTag; +import de.bixilon.minosoft.util.nbt.tag.StringTag; import javax.crypto.SecretKey; import java.math.BigInteger; @@ -67,7 +68,7 @@ public class PacketHandler { if (version == null) { Log.fatal(String.format("Server is running on unknown version or a invalid version was forced (version=%d, brand=\"%s\")", versionId, pkg.getResponse().getServerBrand())); } else { - connection.setVersion(version); + connection.setVersion(version); } Log.info(String.format("Status response received: %s/%s online. MotD: '%s'", pkg.getResponse().getPlayerOnline(), pkg.getResponse().getMaxPlayers(), pkg.getResponse().getMotd().getColoredMessage())); connection.handlePingCallbacks(pkg.getResponse()); @@ -172,6 +173,7 @@ public class PacketHandler { public void handle(PacketChunkBulk pkg) { connection.getPlayer().getWorld().setChunks(pkg.getChunkMap()); + GameWindow.getRenderer().queueChunkBulk(pkg.getChunkMap()); } public void handle(PacketUpdateHealth pkg) { @@ -288,7 +290,10 @@ public class PacketHandler { } public void handle(PacketBlockChange pkg) { - connection.getPlayer().getWorld().setBlock(pkg.getPosition(), pkg.getBlock()); + ChunkLocation chunkLocation = pkg.getPosition().getChunkLocation(); + Chunk chunk = connection.getPlayer().getWorld().getChunk(chunkLocation); + chunk.setBlock(pkg.getPosition().getInChunkLocation(), pkg.getBlock()); + GameWindow.getRenderer().queueChunk(chunkLocation, chunk); // ToDo: only recalculate the changed nibbles } public void handle(PacketMultiBlockChange pkg) { @@ -298,6 +303,7 @@ public class PacketHandler { return; } chunk.setBlocks(pkg.getBlocks()); + GameWindow.getRenderer().queueChunk(pkg.getLocation(), chunk); // ToDo: only recalculate the changed nibbles } public void handle(PacketRespawn pkg) { @@ -328,6 +334,7 @@ public class PacketHandler { public void handle(PacketChunkData pkg) { connection.getPlayer().getWorld().setChunk(pkg.getLocation(), pkg.getChunk()); connection.getPlayer().getWorld().setBlockEntityData(pkg.getBlockEntities()); + GameWindow.getRenderer().queueChunk(pkg.getLocation(), pkg.getChunk()); } public void handle(PacketEntityEffect pkg) { diff --git a/src/main/java/de/bixilon/minosoft/render/WorldRenderer.java b/src/main/java/de/bixilon/minosoft/render/WorldRenderer.java index 4af617faa..ce3b18b77 100644 --- a/src/main/java/de/bixilon/minosoft/render/WorldRenderer.java +++ b/src/main/java/de/bixilon/minosoft/render/WorldRenderer.java @@ -22,8 +22,10 @@ import javafx.util.Pair; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; import static org.lwjgl.opengl.GL11.*; @@ -33,6 +35,11 @@ public class WorldRenderer { private LinkedBlockingQueue> queuedChunks; + public int getCountOfFaces() { + AtomicInteger count = new AtomicInteger(); + faces.forEach((chunkLocation, nibbleMap) -> nibbleMap.forEach((height, faceMap) -> faceMap.forEach(((nibbleLocation, faces) -> faces.forEach((face -> count.incrementAndGet())))))); + return count.get(); + } public void init() { queuedChunks = new LinkedBlockingQueue<>(); @@ -44,7 +51,8 @@ public class WorldRenderer { while (true) { try { Pair current = queuedChunks.take(); - prepareChunk(current.getKey(), current.getValue()); + prepareChunk(current.getKey(), current.getValue(), true); + //Log.verbose(String.format("Count of faces: %d", getCountOfFaces())); } catch (InterruptedException e) { e.printStackTrace(); } @@ -62,52 +70,113 @@ public class WorldRenderer { queuedChunks.add(new Pair<>(location, chunk)); } - public void prepareChunk(ChunkLocation location, Chunk chunk) { + public void prepareChunk(ChunkLocation location, Chunk chunk, boolean checkEdges) { // clear or create current chunk - faces.put(location, new ConcurrentHashMap<>()); - chunk.getNibbles().forEach(((height, chunkNibble) -> prepareChunkNibble(location, height, chunkNibble))); + ConcurrentHashMap>> chunkFaces = new ConcurrentHashMap<>(); + chunk.getNibbles().forEach(((height, chunkNibble) -> chunkFaces.put(height, getFacesForChunkNibble(location, height, chunkNibble)))); + faces.put(location, chunkFaces); + if (!checkEdges) { + return; + } + //ToDo + } - public void prepareChunkNibble(ChunkLocation chunkLocation, byte height, ChunkNibble nibble) { + public ConcurrentHashMap> getFacesForChunkNibble(ChunkLocation chunkLocation, byte sectionHeight, ChunkNibble nibble) { + ConcurrentHashMap world = GameWindow.getConnection().getPlayer().getWorld().getAllChunks(); // clear or create current chunk nibble ConcurrentHashMap> nibbleMap = new ConcurrentHashMap<>(); - faces.get(chunkLocation).put(height, nibbleMap); - HashMap nibbleBlocks = nibble.getBlocks(); - nibbleBlocks.forEach((location, block) -> { + ConcurrentHashMap nibbleBlocks = nibble.getBlocks(); + for (Map.Entry entry : nibbleBlocks.entrySet()) { + ChunkNibbleLocation location = entry.getKey(); + Block block = entry.getValue(); HashSet facesToDraw = new HashSet<>(); for (FaceOrientation orientation : FaceOrientation.values()) { - if ((location.getX() == 0 && orientation == FaceOrientation.WEST) || (location.getX() == 15 && orientation == FaceOrientation.EAST)) { - facesToDraw.add(orientation); - continue; - } - if ((location.getY() == 0 && orientation == FaceOrientation.DOWN) || (location.getY() == 15 && orientation == FaceOrientation.UP)) { - facesToDraw.add(orientation); - continue; - } - if ((location.getZ() == 0 && orientation == FaceOrientation.NORTH) || (location.getZ() == 15 && orientation == FaceOrientation.SOUTH)) { - facesToDraw.add(orientation); - continue; - } - //BlockPosition neighbourPos = location.add(faceDir[orientation.ordinal()]); - boolean isNeighbourFull = switch (orientation) { - case DOWN -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY() - 1, location.getZ()))); - case UP -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY() + 1, location.getZ()))); - case WEST -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX() - 1, location.getY(), location.getZ()))); - case EAST -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX() + 1, location.getY(), location.getZ()))); - case NORTH -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY(), location.getZ() - 1))); - case SOUTH -> assetsLoader.getBlockModelLoader().isFull(nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY(), location.getZ() + 1))); + Block dependedBlock = switch (orientation) { + case DOWN -> { + if (location.getY() == 0) { + // need to check upper section (nibble) + if (sectionHeight == 0) { + // y = 0, there can't be any blocks below me + yield null; + } + // check if block over us is a full block + byte bottomSection = (byte) (sectionHeight - 1); + if (!world.get(chunkLocation).getNibbles().containsKey(bottomSection)) { + yield null; + } + yield world.get(chunkLocation).getNibbles().get(bottomSection).getBlock(location.getX(), 15, location.getZ()); + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY() - 1, location.getZ())); + } + case UP -> { + if (location.getY() == 15) { + // need to check upper section (nibble) + if (sectionHeight == 15) { + // y = 255, there can't be any blocks above me + yield null; + } + // check if block over us is a full block + byte upperSection = (byte) (sectionHeight + 1); + if (!world.get(chunkLocation).getNibbles().containsKey(upperSection)) { + yield null; + } + yield world.get(chunkLocation).getNibbles().get(upperSection).getBlock(location.getX(), 0, location.getZ()); + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY() + 1, location.getZ())); + } + case WEST -> { + if (location.getX() == 0) { + ChunkNibble otherChunkNibble = getChunkNibbleOfWorld(world, new ChunkLocation(chunkLocation.getX() - 1, chunkLocation.getZ()), sectionHeight); + if (otherChunkNibble != null) { + yield otherChunkNibble.getBlock(15, location.getY(), location.getZ()); + } + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX() - 1, location.getY(), location.getZ())); + } + case EAST -> { + if (location.getX() == 15) { + ChunkNibble otherChunkNibble = getChunkNibbleOfWorld(world, new ChunkLocation(chunkLocation.getX() + 1, chunkLocation.getZ()), sectionHeight); + if (otherChunkNibble != null) { + yield otherChunkNibble.getBlock(0, location.getY(), location.getZ()); + } + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX() + 1, location.getY(), location.getZ())); + } + case NORTH -> { + if (location.getZ() == 0) { + ChunkNibble otherChunkNibble = getChunkNibbleOfWorld(world, new ChunkLocation(chunkLocation.getX(), chunkLocation.getZ() - 1), sectionHeight); + if (otherChunkNibble != null) { + yield otherChunkNibble.getBlock(location.getX(), location.getY(), 15); + } + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY(), location.getZ() - 1)); + } + case SOUTH -> { + if (location.getZ() == 15) { + ChunkNibble otherChunkNibble = getChunkNibbleOfWorld(world, new ChunkLocation(chunkLocation.getX(), chunkLocation.getZ() + 1), sectionHeight); + if (otherChunkNibble != null) { + yield otherChunkNibble.getBlock(location.getX(), location.getY(), 0); + } + } + yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY(), location.getZ() + 1)); + } }; - if (!isNeighbourFull) { + if (dependedBlock == null || !assetsLoader.getBlockModelLoader().isFull(dependedBlock)) { facesToDraw.add(orientation); } - } if (facesToDraw.size() > 0) { nibbleMap.put(location, assetsLoader.getBlockModelLoader().prepare(block, facesToDraw)); } - }); + } + return nibbleMap; + } + + public void prepareChunkNibble(ChunkLocation chunkLocation, byte sectionHeight, ChunkNibble nibble) { + faces.get(chunkLocation).put(sectionHeight, getFacesForChunkNibble(chunkLocation, sectionHeight, nibble)); } @@ -122,4 +191,11 @@ public class WorldRenderer { public AssetsLoader getAssetsLoader() { return assetsLoader; } + + private ChunkNibble getChunkNibbleOfWorld(ConcurrentHashMap world, ChunkLocation location, byte sectionHeight) { + if (world.containsKey(location) && world.get(location).getNibbles().containsKey(sectionHeight)) { + return world.get(location).getNibbles().get(sectionHeight); + } + return null; + } } diff --git a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java index a61606332..4d6874510 100644 --- a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java +++ b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java @@ -23,7 +23,7 @@ import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.protocol.protocol.InByteBuffer; import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; public final class ChunkUtil { public static Chunk readChunkPacket(InByteBuffer buffer, short sectionBitMask, short addBitMask, boolean groundUpContinuous, boolean containsSkyLight) { @@ -51,11 +51,11 @@ public final class ChunkUtil { //parse data int arrayPos = 0; - HashMap nibbleMap = new HashMap<>(); + ConcurrentHashMap nibbleMap = new ConcurrentHashMap<>(); for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column if (BitByte.isBitSet(sectionBitMask, c)) { - HashMap blockMap = new HashMap<>(); + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); for (int nibbleY = 0; nibbleY < 16; nibbleY++) { for (int nibbleZ = 0; nibbleZ < 16; nibbleZ++) { @@ -115,12 +115,12 @@ public final class ChunkUtil { } int arrayPos = 0; - HashMap nibbleMap = new HashMap<>(); + ConcurrentHashMap nibbleMap = new ConcurrentHashMap<>(); for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column if (!BitByte.isBitSet(sectionBitMask, c)) { continue; } - HashMap blockMap = new HashMap<>(); + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); for (int nibbleY = 0; nibbleY < 16; nibbleY++) { for (int nibbleZ = 0; nibbleZ < 16; nibbleZ++) { @@ -141,7 +141,7 @@ public final class ChunkUtil { return new Chunk(nibbleMap); } // really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712 - HashMap nibbleMap = new HashMap<>(); + ConcurrentHashMap nibbleMap = new ConcurrentHashMap<>(); for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column if (!BitByte.isBitSet(sectionBitMask, c)) { continue; @@ -155,7 +155,7 @@ public final class ChunkUtil { long[] data = buffer.readLongArray(buffer.readVarInt()); - HashMap blockMap = new HashMap<>(); + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); for (int nibbleY = 0; nibbleY < 16; nibbleY++) { for (int nibbleZ = 0; nibbleZ < 16; nibbleZ++) { for (int nibbleX = 0; nibbleX < 16; nibbleX++) {