diff --git a/src/main/java/ru/windcorp/progressia/common/io/ChunkCodec.java b/src/main/java/ru/windcorp/progressia/common/io/ChunkCodec.java index 03efab4..251eb43 100644 --- a/src/main/java/ru/windcorp/progressia/common/io/ChunkCodec.java +++ b/src/main/java/ru/windcorp/progressia/common/io/ChunkCodec.java @@ -1,10 +1,11 @@ package ru.windcorp.progressia.common.io; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.DecodingException; @@ -27,10 +28,10 @@ public abstract class ChunkCodec extends Namespaced { return signature; } - public abstract ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException; + public abstract ChunkData decode(WorldData world, Vec3i position, DataInputStream input, IOContext context) throws DecodingException, IOException; - public abstract boolean shouldEncode(ChunkData chunk); + public abstract boolean shouldEncode(ChunkData chunk, IOContext context); - public abstract void encode(ChunkData chunk, OutputStream output) throws IOException; + public abstract void encode(ChunkData chunk, DataOutputStream output, IOContext context) throws IOException; } diff --git a/src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java b/src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java index 5b91762..bf03c12 100644 --- a/src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java +++ b/src/main/java/ru/windcorp/progressia/common/io/ChunkIO.java @@ -1,9 +1,9 @@ package ru.windcorp.progressia.common.io; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -11,6 +11,7 @@ import java.util.List; import glm.vec._3.i.Vec3i; import gnu.trove.map.TByteObjectMap; import gnu.trove.map.hash.TByteObjectHashMap; +import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.DecodingException; @@ -21,7 +22,7 @@ public class ChunkIO { private static final TByteObjectMap CODECS_BY_ID = new TByteObjectHashMap<>(); private static final List CODECS_BY_PRIORITY = new ArrayList<>(); - public static ChunkData load(WorldData world, Vec3i position, InputStream data) + public static ChunkData load(WorldData world, Vec3i position, DataInputStream data, IOContext context) throws DecodingException, IOException { if (CODECS_BY_ID.isEmpty()) throw new IllegalStateException("No codecs registered"); @@ -35,7 +36,7 @@ public class ChunkIO { } try { - return codec.decode(world, position, data); + return codec.decode(world, position, data, context); } catch (IOException | DecodingException e) { throw e; } catch (Throwable t) { @@ -47,14 +48,14 @@ public class ChunkIO { } } - public static void save(ChunkData chunk, OutputStream output) + public static void save(ChunkData chunk, DataOutputStream output, IOContext context) throws IOException { - ChunkCodec codec = getCodec(chunk); + ChunkCodec codec = getCodec(chunk, context); try { output.write(codec.getSignature()); - codec.encode(chunk, output); + codec.encode(chunk, output, context); } catch (IOException e) { throw e; } catch (Throwable t) { @@ -70,9 +71,9 @@ public class ChunkIO { return CODECS_BY_ID.get(signature); } - public static ChunkCodec getCodec(ChunkData chunk) { + public static ChunkCodec getCodec(ChunkData chunk, IOContext context) { for (ChunkCodec codec : CODECS_BY_PRIORITY) { - if (codec.shouldEncode(chunk)) { + if (codec.shouldEncode(chunk, context)) { return codec; } } @@ -82,7 +83,7 @@ public class ChunkIO { } /** - * Sorted is order of decreasing priority + * Sorted in order of decreasing priority * @return */ public static List getCodecs() { diff --git a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java index ab5dbc8..89f4527 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java @@ -38,8 +38,8 @@ public class DataBuffer { } }; - private final DataInput reader = new DataInputStream(inputStream); - private final DataOutput writer = new DataOutputStream(outputStream); + private final DataInputStream reader = new DataInputStream(inputStream); + private final DataOutputStream writer = new DataOutputStream(outputStream); public DataBuffer(int capacity) { this.buffer = new TByteArrayList(capacity); @@ -53,25 +53,23 @@ public class DataBuffer { this.buffer = new TByteArrayList(copyFrom.buffer); } - public InputStream getInputStream() { - position = 0; - return inputStream; - } - - public OutputStream getOutputStream() { - buffer.resetQuick(); - return outputStream; - } - - public DataInput getReader() { + public DataInputStream getReader() { position = 0; return reader; } - public DataOutput getWriter() { + public InputStream getInputStream() { + return getReader(); + } + + public DataOutputStream getWriter() { buffer.resetQuick(); return writer; } + + public OutputStream getOutputStream() { + return getWriter(); + } public int getSize() { return buffer.size(); diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index af6c9c7..8590a65 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -59,6 +59,8 @@ implements GenericChunk< BLOCK_FACE_COUNT ]; + private Object generationHint = null; + private final Collection listeners = Collections.synchronizedCollection(new ArrayList<>()); @@ -238,6 +240,14 @@ implements GenericChunk< getListeners().forEach(l -> l.beforeChunkUnloaded(this)); } + public Object getGenerationHint() { + return generationHint; + } + + public void setGenerationHint(Object generationHint) { + this.generationHint = generationHint; + } + /** * Implementation of {@link TileDataStack} used internally by {@link ChunkData} to * actually store the tiles. This is basically an array wrapper with reporting diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java index a0fe418..7eccad2 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java @@ -6,6 +6,7 @@ import java.io.IOException; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.io.ChunkIO; +import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.crash.CrashReports; @@ -26,7 +27,7 @@ public class PacketSendChunk extends PacketChunkChange { this.position.set(chunk.getX(), chunk.getY(), chunk.getZ()); try { - ChunkIO.save(chunk, this.data.getOutputStream()); + ChunkIO.save(chunk, this.data.getWriter(), IOContext.COMMS); } catch (IOException e) { // Impossible } @@ -50,7 +51,7 @@ public class PacketSendChunk extends PacketChunkChange { @Override public void apply(WorldData world) { try { - world.addChunk(ChunkIO.load(world, position, data.getInputStream())); + world.addChunk(ChunkIO.load(world, position, data.getReader(), IOContext.COMMS)); } catch (DecodingException | IOException e) { CrashReports.report(e, "Could not load chunk"); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java index 79746ed..bdfdfaa 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java @@ -101,8 +101,8 @@ public interface GenericWorld< return stack.get(layer); } - default boolean isChunkLoaded(Vec3i pos) { - return getChunk(pos) != null; + default boolean isChunkLoaded(Vec3i chunkPos) { + return getChunk(chunkPos) != null; } default boolean isBlockLoaded(Vec3i blockInWorld) { diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java index e7f4f00..6b50e25 100644 --- a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -33,9 +33,12 @@ public class ChunkManager { public void updateQueues(Player player) { toSend.clear(); - toSend.addAll(requested); - toSend.removeAll(visible); - toSend.retainAll(loaded); + + requested.forEachIn(server.getWorld(), chunk -> { + if (!chunk.isReady()) return; + if (visible.contains(chunk)) return; + toSend.add(chunk); + }); toRevoke.clear(); toRevoke.addAll(visible); @@ -120,13 +123,13 @@ public class ChunkManager { WorldData world = getServer().getWorld().getData(); - ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world); - if (chunk == null) { - chunk = getServer().getWorld().generate(chunkPos); + ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world, getServer()); + if (chunk != null) { + world.addChunk(chunk); + } else { + getServer().getWorld().generate(chunkPos); } - world.addChunk(chunk); - } public void unloadChunk(Vec3i chunkPos) { @@ -143,7 +146,7 @@ public class ChunkManager { world.removeChunk(chunk); - TestWorldDiskIO.saveChunk(chunk); + TestWorldDiskIO.saveChunk(chunk, getServer()); } diff --git a/src/main/java/ru/windcorp/progressia/server/ServerThread.java b/src/main/java/ru/windcorp/progressia/server/ServerThread.java index ca9498b..4f2cdc7 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerThread.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerThread.java @@ -56,8 +56,8 @@ public class ServerThread implements Runnable { try { server.tick(); ticker.runOneTick(); - } catch (Exception e) { - CrashReports.report(e, "Got an exception in the server thread"); + } catch (Throwable e) { + CrashReports.report(e, "Got a throwable in the server thread"); } } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index c82a582..9d84304 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -4,8 +4,6 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.logging.log4j.LogManager; - import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.map.TIntObjectMap; @@ -65,7 +63,6 @@ public class ClientManager { getServer().getPlayerManager().getPlayers().add(player); PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); - LogManager.getLogger().info("Sending local player ID {}", EntityData.formatEntityId(entity.getEntityId())); packet.set(entity.getEntityId()); client.sendPacket(packet); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java index 622ca00..d26b308 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java @@ -86,6 +86,10 @@ public class ChunkLogic implements GenericChunk< return data; } + public boolean isReady() { + return getWorld().getGenerator().isChunkReady(getData().getGenerationHint()); + } + public boolean hasTickingBlocks() { return !tickingBlocks.isEmpty(); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/AbstractWorldGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/AbstractWorldGenerator.java new file mode 100644 index 0000000..1cae29d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/AbstractWorldGenerator.java @@ -0,0 +1,48 @@ +package ru.windcorp.progressia.server.world.generation; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Objects; + +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.DecodingException; + +public abstract class AbstractWorldGenerator extends WorldGenerator { + + private final Class hintClass; + + public AbstractWorldGenerator(String id, Class hintClass) { + super(id); + this.hintClass = Objects.requireNonNull(hintClass, "hintClass"); + } + + @Override + public final Object readGenerationHint(DataInputStream input) throws IOException, DecodingException { + return doReadGenerationHint(input); + } + + @Override + public final void writeGenerationHint(DataOutputStream output, Object hint) throws IOException { + doWriteGenerationHint(output, hintClass.cast(hint)); + } + + protected abstract H doReadGenerationHint(DataInputStream input) throws IOException, DecodingException; + protected abstract void doWriteGenerationHint(DataOutputStream output, H hint) throws IOException; + + @Override + public final boolean isChunkReady(Object hint) { + return checkIsChunkReady(hintClass.cast(hint)); + } + + protected abstract boolean checkIsChunkReady(H hint); + + protected H getHint(ChunkData chunk) { + return hintClass.cast(chunk.getGenerationHint()); + } + + protected void setHint(ChunkData chunk, H hint) { + chunk.setGenerationHint(hint); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/generation/WorldGenerator.java b/src/main/java/ru/windcorp/progressia/server/world/generation/WorldGenerator.java index 45d0a12..25b48df 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/generation/WorldGenerator.java +++ b/src/main/java/ru/windcorp/progressia/server/world/generation/WorldGenerator.java @@ -1,16 +1,25 @@ package ru.windcorp.progressia.server.world.generation; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.WorldData; public abstract class WorldGenerator extends Namespaced { - - public WorldGenerator(String id) { + + WorldGenerator(String id) { super(id); + // package-private constructor; extend AbstractWorldGeneration } public abstract ChunkData generate(Vec3i chunkPos, WorldData world); + public abstract Object readGenerationHint(DataInputStream input) throws IOException, DecodingException; + public abstract void writeGenerationHint(DataOutputStream output, Object hint) throws IOException; + public abstract boolean isChunkReady(Object hint); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java index e12e873..d9164dd 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java @@ -15,5 +15,11 @@ public abstract class CachedChunkChange

extends Cac public void getRelevantChunk(Vec3i output) { getPacket().getAffectedChunk(output); } + + @Override + protected Vec3i getAffectedChunk(Vec3i output) { + getPacket().getAffectedChunk(output); + return output; + } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java index 8e0a190..de93a5b 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java @@ -5,8 +5,6 @@ import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; @@ -17,6 +15,7 @@ import gnu.trove.map.TObjectIntMap; import gnu.trove.map.hash.TObjectIntHashMap; import ru.windcorp.jputil.functions.ThrowingConsumer; import ru.windcorp.progressia.common.io.ChunkCodec; +import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.WorldData; @@ -57,7 +56,7 @@ public class TestChunkCodec extends ChunkCodec { } @Override - public boolean shouldEncode(ChunkData chunk) { + public boolean shouldEncode(ChunkData chunk, IOContext context) { return true; } @@ -66,9 +65,7 @@ public class TestChunkCodec extends ChunkCodec { */ @Override - public ChunkData decode(WorldData world, Vec3i position, InputStream inputStream) throws DecodingException, IOException { - DataInput input = new DataInputStream(inputStream); - + public ChunkData decode(WorldData world, Vec3i position, DataInputStream input, IOContext context) throws DecodingException, IOException { BlockData[] blockPalette = readBlockPalette(input); TileData[] tilePalette = readTilePalette(input); @@ -136,10 +133,7 @@ public class TestChunkCodec extends ChunkCodec { */ @Override - public void encode(ChunkData chunk, OutputStream outputStream) throws IOException { - - DataOutput output = new DataOutputStream(outputStream); - + public void encode(ChunkData chunk, DataOutputStream output, IOContext context) throws IOException { Palette blockPalette = createBlockPalette(chunk); Palette tilePalette = createTilePalette(chunk); diff --git a/src/main/java/ru/windcorp/progressia/test/gen/TestWorldGenerator.java b/src/main/java/ru/windcorp/progressia/test/gen/TestWorldGenerator.java index e680fa2..2304813 100644 --- a/src/main/java/ru/windcorp/progressia/test/gen/TestWorldGenerator.java +++ b/src/main/java/ru/windcorp/progressia/test/gen/TestWorldGenerator.java @@ -1,8 +1,16 @@ package ru.windcorp.progressia.test.gen; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Random; + import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.VectorUtil; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockDataRegistry; @@ -10,28 +18,46 @@ import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataRegistry; import ru.windcorp.progressia.server.world.WorldLogic; -import ru.windcorp.progressia.server.world.generation.WorldGenerator; - -public class TestWorldGenerator extends WorldGenerator { +import ru.windcorp.progressia.server.world.generation.AbstractWorldGenerator; +public class TestWorldGenerator extends AbstractWorldGenerator { + public TestWorldGenerator(WorldLogic world) { - super("Test:WorldGenerator"); + super("Test:WorldGenerator", Boolean.class); + } + + @Override + protected Boolean doReadGenerationHint(DataInputStream input) throws IOException, DecodingException { + return input.readBoolean(); + } + + @Override + protected void doWriteGenerationHint(DataOutputStream output, Boolean hint) throws IOException { + output.writeBoolean(hint); + } + + @Override + protected boolean checkIsChunkReady(Boolean hint) { + return hint; } @Override public ChunkData generate(Vec3i chunkPos, WorldData world) { + ChunkData chunk = generateUnpopulated(chunkPos, world); + world.addChunk(chunk); + findAndPopulate(chunkPos, world); + return chunk; + } + + private ChunkData generateUnpopulated(Vec3i chunkPos, WorldData world) { ChunkData chunk = new ChunkData(chunkPos, world); + chunk.setGenerationHint(false); final int bpc = ChunkData.BLOCKS_PER_CHUNK; BlockData dirt = BlockDataRegistry.getInstance().get("Test:Dirt"); BlockData stone = BlockDataRegistry.getInstance().get("Test:Stone"); BlockData air = BlockDataRegistry.getInstance().get("Test:Air"); - - TileData grass = TileDataRegistry.getInstance().get("Test:Grass"); - TileData stones = TileDataRegistry.getInstance().get("Test:Stones"); - TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); - TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); final float maxHeight = 32; final float rho = 2000; @@ -48,70 +74,134 @@ public class TestWorldGenerator extends WorldGenerator { } } - Vec3i pos = new Vec3i(); - - for (pos.x = 0; pos.x < bpc; ++pos.x) { - for (pos.y = 0; pos.y < bpc; ++pos.y) { - for (pos.z = 0; pos.z < bpc; ++pos.z) { - - int layer = pos.z - heightMap[pos.x][pos.y]; - - if (layer < -4) { - chunk.setBlock(pos, stone, false); - } else if (layer < 0) { - chunk.setBlock(pos, dirt, false); - } else { - chunk.setBlock(pos, air, false); - } - - } + VectorUtil.iterateCuboid(0, 0, 0, bpc, bpc, bpc, pos -> { + int layer = pos.z - heightMap[pos.x][pos.y]; + + if (layer < -4) { + chunk.setBlock(pos, stone, false); + } else if (layer < 0) { + chunk.setBlock(pos, dirt, false); + } else { + chunk.setBlock(pos, air, false); } - } - - for (int x = 0; x < bpc; ++x) { - for (int y = 0; y < bpc; ++y) { - -// int z = heightMap[x][y]; - - for (int z = 0; z < bpc; ++z) { - - pos.set(x, y, z); - int layer = pos.z - heightMap[x][y]; - - if (layer == -1) { - chunk.getTiles(pos, BlockFace.TOP).add(grass); - for (BlockFace face : BlockFace.getFaces()) { - if (face.getVector().z != 0) continue; - pos.add(face.getVector()); - - if (!ChunkData.isInBounds(pos) || (chunk.getBlock(pos) == air)) { - pos.sub(face.getVector()); - chunk.getTiles(pos, face).add(grass); - } else { - pos.sub(face.getVector()); - } - } - - int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147; - if (hash % 5 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(sand); - } - - hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129; - if (hash % 5 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(stones); - } - - hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131; - if (hash % 9 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(flowers); - } - } - } - } - } + }); return chunk; } + private void findAndPopulate(Vec3i changePos, WorldData world) { + VectorUtil.iterateCuboidAround(changePos, 3, candidatePos -> { + if (canBePopulated(candidatePos, world)) { + populate(candidatePos, world); + } + }); + } + + private boolean canBePopulated(Vec3i candidatePos, WorldData world) { + Vec3i cursor = Vectors.grab3i(); + + ChunkData candidate = world.getChunk(candidatePos); + if (candidate == null || isChunkReady(candidate.getGenerationHint())) return false; + + for (int dx = -1; dx <= 1; ++dx) { + cursor.x = candidatePos.x + dx; + for (int dy = -1; dy <= 1; ++dy) { + cursor.y = candidatePos.y + dy; + for (int dz = -1; dz <= 1; ++dz) { + + if ((dx | dy | dz) == 0) continue; + + cursor.z = candidatePos.z + dz; + + ChunkData chunk = world.getChunk(cursor); + if (chunk == null) { + return false; + } + + } + } + } + + Vectors.release(cursor); + return true; + } + + private void populate(Vec3i chunkPos, WorldData world) { + Random random = new Random(chunkPos.x + chunkPos.y + chunkPos.z); + + ChunkData chunk = world.getChunk(chunkPos); + assert chunk != null : "Something went wrong when populating chunk at (" + chunkPos.x + "; " + chunkPos.y + "; " + chunkPos.z + ")"; + + BlockData air = BlockDataRegistry.getInstance().get("Test:Air"); + + Vec3i biw = new Vec3i(); + + int minX = Coordinates.getInWorld(chunkPos.x, 0); + int maxX = Coordinates.getInWorld(chunkPos.x + 1, 0); + int minY = Coordinates.getInWorld(chunkPos.y, 0); + int maxY = Coordinates.getInWorld(chunkPos.y + 1, 0); + int minZ = Coordinates.getInWorld(chunkPos.z, 0); + int maxZ = Coordinates.getInWorld(chunkPos.z + 1, 0); + + for (biw.x = minX; biw.x < maxX; ++biw.x) { + for (biw.y = minY; biw.y < maxY; ++biw.y) { + + for (biw.z = minZ; biw.z < maxZ + 1 && world.getBlock(biw) != air; ++biw.z); + biw.z -= 1; + + if (biw.z == maxZ) continue; + if (biw.z < minZ) continue; + + addTiles(chunk, biw, world, random); + + } + } + + chunk.setGenerationHint(true); + } + + private void addTiles(ChunkData chunk, Vec3i biw, WorldData world, Random random) { + addGrass(chunk, biw, world, random); + addDecor(chunk, biw, world, random); + } + + private void addGrass(ChunkData chunk, Vec3i biw, WorldData world, Random random) { + BlockData air = BlockDataRegistry.getInstance().get("Test:Air"); + TileData grass = TileDataRegistry.getInstance().get("Test:Grass"); + + world.getTiles(biw, BlockFace.TOP).add(grass); + + for (BlockFace face : BlockFace.getFaces()) { + if (face.getVector().z != 0) continue; + biw.add(face.getVector()); + + if (world.getBlock(biw) == air) { + biw.sub(face.getVector()); + world.getTiles(biw, face).add(grass); + } else { + biw.sub(face.getVector()); + } + } + } + + private void addDecor(ChunkData chunk, Vec3i biw, WorldData world, Random random) { + if (random.nextInt(8) == 0) { + world.getTiles(biw, BlockFace.TOP).addFarthest( + TileDataRegistry.getInstance().get("Test:Sand") + ); + } + + if (random.nextInt(8) == 0) { + world.getTiles(biw, BlockFace.TOP).addFarthest( + TileDataRegistry.getInstance().get("Test:Stones") + ); + } + + if (random.nextInt(8) == 0) { + world.getTiles(biw, BlockFace.TOP).addFarthest( + TileDataRegistry.getInstance().get("Test:YellowFlowers") + ); + } + } + }