From 60b514dbb1fe47c113d6b78bcd737dc4a6ab30ff Mon Sep 17 00:00:00 2001 From: SuperCoder7979 Date: Sun, 1 Mar 2020 14:14:48 -0500 Subject: [PATCH] more optimizations --- .../halotroop/litecraft/world/Chunk.java | 32 ++++++++++++------- .../halotroop/litecraft/world/World.java | 25 +++++++++------ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/github/halotroop/litecraft/world/Chunk.java b/src/main/java/com/github/halotroop/litecraft/world/Chunk.java index 3af5b7a..f7061f4 100644 --- a/src/main/java/com/github/halotroop/litecraft/world/Chunk.java +++ b/src/main/java/com/github/halotroop/litecraft/world/Chunk.java @@ -1,6 +1,8 @@ package com.github.halotroop.litecraft.world; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.function.ToIntFunction; import org.joml.Vector3f; @@ -20,13 +22,13 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable * @param x in-chunk x coordinate. * @param y in-chunk y coordinate. * @param z in-chunk z coordinate. - * @return creates a long that represents a coordinate, for use as a key in the array. + * @return creates a long that represents a coordinate, for use as a key in arrays. */ public static int index(int x, int y, int z) { return (x & MAX_POS) | ((y & MAX_POS) << POS_SHIFT) | ((z & MAX_POS) << DOUBLE_SHIFT); } private final Block[] blocks = new Block[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; - private BlockInstance[] blockEntities = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + private BlockInstance[] blockInstances = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; private boolean shouldRender = false; public final int chunkX, chunkY, chunkZ; public final int chunkStartX, chunkStartY, chunkStartZ; @@ -34,6 +36,10 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable public final int dimension; private boolean dirty = true; private World world; + /** + * A holder for the rendered blocks in this chunk. This array is *NOT* safe to use for getting BIs at a position! + * It can vary in size from 0 to 512 elements long and must only be read linearly. + */ private BlockInstance[] renderedBlocks = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; public Chunk(World world, int chunkX, int chunkY, int chunkZ, int dimension) @@ -66,7 +72,7 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable { if (x > CHUNK_SIZE || y > CHUNK_SIZE || z > CHUNK_SIZE || x < 0 || y < 0 || z < 0) { throw new RuntimeException("BlockInstance [" + x + ", " + y + ", " + z + "] out of chunk bounds!"); } - return this.blockEntities[index(x, y, z)]; + return this.blockInstances[index(x, y, z)]; } public void render(BlockRenderer blockRenderer) @@ -74,7 +80,9 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable if (shouldRender) { if (dirty) + { dirty = false; + List tempList = new ArrayList<>(); Arrays.fill(renderedBlocks, null); for (int x = 0; x < CHUNK_SIZE; x++) for (int y = 0; y < CHUNK_SIZE; y++) @@ -85,26 +93,28 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable // Check for chunk edges to avoid errors when get the neighboring blocks, TODO fix this if (x == 0 || x == CHUNK_SIZE - 1 || z == 0 || z == CHUNK_SIZE - 1 || y == 0 || y == CHUNK_SIZE - 1) { - renderedBlocks[index(x, y, z)] = block; + tempList.add(block); continue; } // Check for air. Yes this is stupid, TODO fix this too - try { + try + { if (getBlockInstance(x - 1, y, z).getModel() == null || getBlockInstance(x + 1, y, z).getModel() == null || getBlockInstance(x, y - 1, z).getModel() == null || getBlockInstance(x, y + 1, z).getModel() == null || getBlockInstance(x, y, z - 1).getModel() == null || getBlockInstance(x, y, z + 1).getModel() == null) { - renderedBlocks[index(x, y, z)] = block; + tempList.add(block); } - } - catch (NullPointerException e) + } catch (NullPointerException e) { // this seems to be a hotspot for errors e.printStackTrace(); // so I can add a debug breakpoint on this line throw e; // e } } + renderedBlocks = tempList.toArray(BlockInstance[]::new); + } blockRenderer.prepareRender(); blockRenderer.render(renderedBlocks); @@ -129,7 +139,7 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable else if (z < 0) z = 0; // this.blocks[index(x, y, z)] = block; - if (this.shouldRender) this.blockEntities[index(x, y, z)] = + if (this.shouldRender) this.blockInstances[index(x, y, z)] = new BlockInstance(block, new Vector3f(this.chunkStartX + x, this.chunkStartY + y, this.chunkStartZ + z)); dirty = true; } @@ -146,7 +156,7 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable { Block block = this.blocks[index(x, y, z)]; - this.blockEntities[index(x, y, z)] = new BlockInstance(block, + this.blockInstances[index(x, y, z)] = new BlockInstance(block, new Vector3f( this.chunkStartX + x, this.chunkStartY + y, @@ -155,7 +165,7 @@ public class Chunk implements BlockAccess, WorldGenConstants, SODSerializable else if (!render && this.shouldRender) // else if it has been changed to false. // we need to check both variables because there are two cases that make // the if statement fall to here - blockEntities = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; + blockInstances = new BlockInstance[CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE]; this.shouldRender = render; dirty = true; } diff --git a/src/main/java/com/github/halotroop/litecraft/world/World.java b/src/main/java/com/github/halotroop/litecraft/world/World.java index 9d847bb..e513e3e 100644 --- a/src/main/java/com/github/halotroop/litecraft/world/World.java +++ b/src/main/java/com/github/halotroop/litecraft/world/World.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.LongConsumer; @@ -30,6 +31,7 @@ public class World implements BlockAccess, WorldGenConstants private final LitecraftSave save; private final long seed; private final int dimension; + private final ForkJoinPool threadPool; public Player player; int renderBound; int renderBoundVertical; @@ -37,6 +39,7 @@ public class World implements BlockAccess, WorldGenConstants private final BlockInstance dummy; public World(long seed, int renderSize, Dimension dim, LitecraftSave save) { + this.threadPool = new ForkJoinPool(4, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); this.updateLoadedChunks(0, 0, 0); this.dummy = new BlockInstance(Blocks.ANDESITE, new Vector3f(0, 0, 0)); this.dummy.setVisible(false); @@ -140,7 +143,7 @@ public class World implements BlockAccess, WorldGenConstants { result.set(this.save.saveChunk(chunk)); this.chunks.remove(posHash); - }); + }, threadPool); return result.get(); } @@ -195,16 +198,20 @@ public class World implements BlockAccess, WorldGenConstants public void unloadAllChunks() { LongList chunkPositions = new LongArrayList(); - CompletableFuture.runAsync(() -> + List futures = new ArrayList<>(); + if (this.chunks != null) { - if (this.chunks != null) - this.chunks.forEach((pos, chunk) -> - { // for every chunk in memory + this.chunks.forEach((pos, chunk) -> + { // for every chunk in memory + futures.add(CompletableFuture.runAsync(() -> + { chunkPositions.add((long) pos); // add pos to chunk positions list for removal later this.save.saveChunk(chunk); // save chunk - }); - chunkPositions.forEach((LongConsumer) (pos -> this.chunks.remove(pos))); // remove all chunks - }).join(); + }, threadPool)); + }); + } + futures.forEach(CompletableFuture::join); + chunkPositions.forEach((LongConsumer) (pos -> this.chunks.remove(pos))); // remove all chunks } public long getSeed() @@ -245,7 +252,7 @@ public class World implements BlockAccess, WorldGenConstants if (!alreadyRendering) chunks.put(this.posHash(chunk.chunkX, chunk.chunkY, chunk.chunkZ), chunk); }); - }); + }, threadPool); } private static final class GenerationWorld implements BlockAccess, WorldGenConstants