From de1f845b71ea58c9a9baa7a68b86b9c07a3ac6d8 Mon Sep 17 00:00:00 2001 From: dags- Date: Fri, 20 Mar 2020 09:36:13 +0000 Subject: [PATCH] - use fast-util long2obj maps - reorganised river stuff - improvements to TerrainHelper - fixes buildings spawning with terrain inside --- TerraForgedApp/build.gradle | 2 +- TerraForgedCore/build.gradle | 5 ++ .../com/terraforged/core/region/Region.java | 46 +++-------- .../terraforged/core/region/RegionCache.java | 2 +- .../core/region/RegionGenerator.java | 14 +--- .../core/util/concurrent/cache/Cache.java | 23 +++--- .../util/concurrent/cache/CacheEntry.java | 43 +++++++---- .../concurrent/cache/SynchronizedLongMap.java | 54 +++++++++++++ .../core/world/heightmap/Heightmap.java | 6 +- .../core/world/heightmap/WorldHeightmap.java | 14 ++-- .../{river => rivermap}/PosGenerator.java | 3 +- .../RiverMap.java} | 21 ++--- .../RiverMapConfig.java} | 9 ++- .../{river => rivermap}/RiverRegionList.java | 8 +- .../world/{river => rivermap/lake}/Lake.java | 3 +- .../{river => rivermap/lake}/LakeConfig.java | 2 +- .../world/{ => rivermap}/river/River.java | 2 +- .../{ => rivermap}/river/RiverBounds.java | 2 +- .../{ => rivermap}/river/RiverConfig.java | 2 +- .../world/{ => rivermap}/river/RiverNode.java | 2 +- .../{ => rivermap}/river/RiverRegion.java | 36 +++++---- TerraForgedMod/build.gradle | 13 +--- .../mod/feature/TerrainHelper.java | 74 +++++++++++------- .../BetterAtSurfaceWithChanceMultiple.java | 32 -------- .../mod/feature/placement/PosGenerator.java | 8 -- .../mod/feature/placement/PosStream.java | 76 ------------------- 26 files changed, 224 insertions(+), 278 deletions(-) create mode 100644 TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/SynchronizedLongMap.java rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river => rivermap}/PosGenerator.java (98%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river/RiverManager.java => rivermap/RiverMap.java} (89%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river/RiverContext.java => rivermap/RiverMapConfig.java} (52%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river => rivermap}/RiverRegionList.java (77%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river => rivermap/lake}/Lake.java (97%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{river => rivermap/lake}/LakeConfig.java (98%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{ => rivermap}/river/River.java (99%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{ => rivermap}/river/RiverBounds.java (98%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{ => rivermap}/river/RiverConfig.java (98%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{ => rivermap}/river/RiverNode.java (97%) rename TerraForgedCore/src/main/java/com/terraforged/core/world/{ => rivermap}/river/RiverRegion.java (89%) delete mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/BetterAtSurfaceWithChanceMultiple.java delete mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosGenerator.java delete mode 100644 TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosStream.java diff --git a/TerraForgedApp/build.gradle b/TerraForgedApp/build.gradle index 6af11c0..f96bdfd 100644 --- a/TerraForgedApp/build.gradle +++ b/TerraForgedApp/build.gradle @@ -8,12 +8,12 @@ repositories { dependencies { compile "org.processing:core:3.3.7" + compile "it.unimi.dsi:fastutil:8.2.1" compile project(":TerraForgedCore") } jar { manifest { attributes "Main-Class": "com.terraforged.app.Main" } - from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } } diff --git a/TerraForgedCore/build.gradle b/TerraForgedCore/build.gradle index 8560f00..f68ce7d 100644 --- a/TerraForgedCore/build.gradle +++ b/TerraForgedCore/build.gradle @@ -1,7 +1,12 @@ apply plugin: "maven-publish" +repositories { + mavenCentral() +} + dependencies { compile project(":Noise2D") + compile "it.unimi.dsi:fastutil:8.2.1" } publishing { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java index 6a071bf..af2595d 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/Region.java @@ -28,14 +28,11 @@ package com.terraforged.core.region; import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Extent; import com.terraforged.core.filter.Filterable; -import com.terraforged.core.region.chunk.ChunkGenTask; import com.terraforged.core.region.chunk.ChunkReader; import com.terraforged.core.region.chunk.ChunkWriter; -import com.terraforged.core.region.chunk.ChunkZoomTask; -import com.terraforged.core.util.concurrent.batcher.Batcher; import com.terraforged.core.world.decorator.Decorator; import com.terraforged.core.world.heightmap.Heightmap; -import com.terraforged.core.world.river.RiverRegionList; +import com.terraforged.core.world.rivermap.RiverRegionList; import com.terraforged.core.world.terrain.Terrain; import java.util.Collection; @@ -173,45 +170,20 @@ public class Region implements Extent { } } - public void generate(Heightmap heightmap, Batcher batcher) { - for (int cz = 0; cz < chunkSize.total; cz++) { - for (int cx = 0; cx < chunkSize.total; cx++) { - int index = chunkSize.indexOf(cx, cz); - GenChunk chunk = computeChunk(index, cx, cz); - Runnable task = new ChunkGenTask(chunk, heightmap); - batcher.submit(task); - } - } - } - - public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom, Batcher batcher) { + public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom) { float translateX = offsetX - ((blockSize.size * zoom) / 2F); float translateZ = offsetZ - ((blockSize.size * zoom) / 2F); for (int cz = 0; cz < chunkSize.total; cz++) { for (int cx = 0; cx < chunkSize.total; cx++) { int index = chunkSize.indexOf(cx, cz); GenChunk chunk = computeChunk(index, cx, cz); - Runnable task = new ChunkZoomTask(chunk, heightmap, translateX, translateZ, zoom); - batcher.submit(task); - } - } - } - - public void check() { - for (int dz = 0; dz < chunkSize.total; dz++) { - for (int dx = 0; dx < chunkSize.total; dx++) { - int index = chunkSize.indexOf(dx, dz); - if (chunks[index] == null) { - throw new NullPointerException("Null chunk " + dx + ":" + dz); - } - } - } - - for (int dz = 0; dz < blockSize.total; dz++) { - for (int dx = 0; dx < blockSize.total; dx++) { - int index = blockSize.indexOf(dx, dz); - if (blocks[index] == null) { - throw new NullPointerException("Null block " + dx + ":" + dz); + for (int dz = 0; dz < 16; dz++) { + for (int dx = 0; dx < 16; dx++) { + float x = ((chunk.getBlockX() + dx) * zoom) + translateX; + float z = ((chunk.getBlockZ() + dz) * zoom) + translateZ; + Cell cell = chunk.genCell(dx, dz); + heightmap.apply(cell, x, z); + } } } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java index 5aadeb2..e309965 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionCache.java @@ -38,7 +38,7 @@ public class RegionCache implements RegionExtent { private final boolean queuing; private final RegionGenerator renderer; - private final Cache> cache; + private final Cache> cache; public RegionCache(boolean queueNeighbours, RegionGenerator renderer) { this.renderer = renderer; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java index 5cf914c..7de02f6 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/region/RegionGenerator.java @@ -31,7 +31,7 @@ import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.world.WorldGenerator; import com.terraforged.core.world.WorldGeneratorFactory; import com.terraforged.core.world.heightmap.RegionExtent; -import com.terraforged.core.world.river.RiverRegionList; +import com.terraforged.core.world.rivermap.RiverRegionList; import java.util.concurrent.CompletableFuture; @@ -89,7 +89,7 @@ public class RegionGenerator implements RegionExtent { public Region generateRegion(int regionX, int regionZ) { WorldGenerator generator = genPool.get(); Region region = regions.create(regionX, regionZ, factor, border); - RiverRegionList rivers = generator.getHeightmap().getRiverManager().getRivers(region); + RiverRegionList rivers = generator.getHeightmap().getRiverMap().getRivers(region); region.generateBase(generator.getHeightmap()); region.generateRivers(generator.getHeightmap(), rivers); postProcess(region, generator); @@ -104,15 +104,7 @@ public class RegionGenerator implements RegionExtent { public Region generateRegion(float centerX, float centerZ, float zoom, boolean filter) { WorldGenerator generator = genPool.get(); Region region = regions.create(0, 0, factor, border); - float translateX = centerX - ((region.getBlockSize().size * zoom) / 2F); - float translateZ = centerZ - ((region.getBlockSize().size * zoom) / 2F); - region.generate(chunk -> { - chunk.generate((cell, dx, dz) -> { - float x = ((chunk.getBlockX() + dx) * zoom) + translateX; - float z = ((chunk.getBlockZ() + dz) * zoom) + translateZ; - generator.getHeightmap().apply(cell, x, z); - }); - }); + region.generateZoom(generator.getHeightmap(), centerX, centerZ, zoom); postProcess(region, generator, centerX, centerZ, zoom, filter); return region; } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java index 11dd9e2..07b9180 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/Cache.java @@ -2,31 +2,30 @@ package com.terraforged.core.util.concurrent.cache; import com.terraforged.core.util.concurrent.ThreadPool; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.function.Function; +import java.util.function.LongFunction; import java.util.function.Supplier; -public class Cache implements Runnable { +public class Cache implements Runnable { private final long expireMS; private final long intervalMS; - private final Map map = new ConcurrentHashMap<>(); + private final SynchronizedLongMap map; private volatile long timestamp = 0L; public Cache(long expireTime, long interval, TimeUnit unit) { this.expireMS = unit.toMillis(expireTime); this.intervalMS = unit.toMillis(interval); + this.map = new SynchronizedLongMap<>(100); } - public V computeIfAbsent(K k, Supplier supplier) { - return computeIfAbsent(k, o -> supplier.get()); + public V computeIfAbsent(long key, Supplier supplier) { + return computeIfAbsent(key, o -> supplier.get()); } - public V computeIfAbsent(K k, Function func) { - V v = map.computeIfAbsent(k, func); + public V computeIfAbsent(long key, LongFunction func) { + V v = map.computeIfAbsent(key, func); queueUpdate(); return v; } @@ -42,10 +41,6 @@ public class Cache implements Runnable { @Override public void run() { final long now = timestamp; - map.forEach((key, val) -> { - if (now - val.getTimestamp() > expireMS) { - map.remove(key); - } - }); + map.removeIf(val -> now - val.getTimestamp() > expireMS); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java index 9e806c6..82b4a3d 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/CacheEntry.java @@ -1,16 +1,17 @@ package com.terraforged.core.util.concurrent.cache; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; -import java.util.concurrent.FutureTask; +import java.util.function.Supplier; -public class CacheEntry extends FutureTask implements ExpiringEntry { +public class CacheEntry implements Runnable, ExpiringEntry { + private volatile T result; private volatile long timestamp; - private CacheEntry(Callable supplier) { - super(supplier); + private final Supplier supplier; + + private CacheEntry(Supplier supplier) { + this.supplier = supplier; this.timestamp = System.currentTimeMillis(); } @@ -20,16 +21,30 @@ public class CacheEntry extends FutureTask implements ExpiringEntry { } @Override - public T get() { - try { - this.timestamp = System.currentTimeMillis(); - return super.get(); - } catch (Throwable t) { - throw new CompletionException(t); - } + public void run() { + this.result = supplier.get(); + this.timestamp = System.currentTimeMillis(); } - public static CacheEntry supplyAsync(Callable supplier, Executor executor) { + public boolean isDone() { + return result != null; + } + + public T get() { + T value = result; + if (value == null) { + value = getNow(); + } + return value; + } + + private T getNow() { + T result = supplier.get(); + this.result = result; + return result; + } + + public static CacheEntry supplyAsync(Supplier supplier, Executor executor) { CacheEntry entry = new CacheEntry<>(supplier); executor.execute(entry); return entry; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/SynchronizedLongMap.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/SynchronizedLongMap.java new file mode 100644 index 0000000..5f92ad9 --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/cache/SynchronizedLongMap.java @@ -0,0 +1,54 @@ +package com.terraforged.core.util.concurrent.cache; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; + +import java.util.function.LongFunction; +import java.util.function.Predicate; + +public class SynchronizedLongMap { + + private final Object lock; + private final Long2ObjectOpenHashMap map; + + public SynchronizedLongMap(int size) { + this.map = new Long2ObjectOpenHashMap<>(size); + this.lock = this; + } + + public void remove(long key) { + synchronized (lock) { + map.remove(key); + } + } + + public void put(long key, V v) { + synchronized (lock) { + map.put(key, v); + } + } + + public V get(long key) { + synchronized (lock) { + return map.get(key); + } + } + + public V computeIfAbsent(long key, LongFunction func) { + synchronized (lock) { + return map.computeIfAbsent(key, func); + } + } + + public void removeIf(Predicate predicate) { + synchronized (lock) { + ObjectIterator> iterator = map.long2ObjectEntrySet().fastIterator(); + while (iterator.hasNext()) { + if (predicate.test(iterator.next().getValue())) { + iterator.remove(); + } + } + } + } +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/Heightmap.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/Heightmap.java index d244a5e..a9c176f 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/Heightmap.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/Heightmap.java @@ -31,15 +31,15 @@ import com.terraforged.core.cell.Populator; import com.terraforged.core.region.Size; import com.terraforged.core.util.concurrent.ObjectPool; import com.terraforged.core.world.climate.Climate; -import com.terraforged.core.world.river.RiverManager; -import com.terraforged.core.world.river.RiverRegionList; +import com.terraforged.core.world.rivermap.RiverMap; +import com.terraforged.core.world.rivermap.RiverRegionList; import com.terraforged.core.world.terrain.Terrain; public interface Heightmap extends Populator, Extent { Climate getClimate(); - RiverManager getRiverManager(); + RiverMap getRiverMap(); void visit(Cell cell, float x, float z); diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/WorldHeightmap.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/WorldHeightmap.java index f054790..2ecdb4c 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/WorldHeightmap.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/heightmap/WorldHeightmap.java @@ -36,8 +36,8 @@ import com.terraforged.core.world.climate.Climate; import com.terraforged.core.world.continent.ContinentLerper2; import com.terraforged.core.world.continent.ContinentLerper3; import com.terraforged.core.world.continent.ContinentModule; -import com.terraforged.core.world.river.RiverManager; -import com.terraforged.core.world.river.RiverRegionList; +import com.terraforged.core.world.rivermap.RiverMap; +import com.terraforged.core.world.rivermap.RiverRegionList; import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.TerrainPopulator; import com.terraforged.core.world.terrain.Terrains; @@ -67,7 +67,7 @@ public class WorldHeightmap implements Heightmap { private final Climate climate; private final Populator root; - private final RiverManager riverManager; + private final RiverMap riverMap; private final TerrainProvider terrainProvider; public WorldHeightmap(GeneratorContext context) { @@ -154,7 +154,7 @@ public class WorldHeightmap implements Heightmap { COAST_VALUE - 0.05F ); - riverManager = new RiverManager(this, context); + riverMap = new RiverMap(this, context); } @Override @@ -167,7 +167,7 @@ public class WorldHeightmap implements Heightmap { @Override public void apply(Cell cell, float x, float z) { applyBase(cell, x, z); - applyRivers(cell, x, z, riverManager.getRivers((int) x, (int) z)); + applyRivers(cell, x, z, riverMap.getRivers((int) x, (int) z)); applyClimate(cell, x, z); } @@ -219,8 +219,8 @@ public class WorldHeightmap implements Heightmap { } @Override - public RiverManager getRiverManager() { - return riverManager; + public RiverMap getRiverMap() { + return riverMap; } public Populator getPopulator(Terrain terrain) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/PosGenerator.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/PosGenerator.java similarity index 98% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/PosGenerator.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/PosGenerator.java index 72071bb..fba5b62 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/PosGenerator.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/PosGenerator.java @@ -23,10 +23,11 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap; import com.terraforged.core.cell.Cell; import com.terraforged.core.world.heightmap.Heightmap; +import com.terraforged.core.world.rivermap.river.RiverNode; import com.terraforged.core.world.terrain.Terrain; import me.dags.noise.domain.Domain; import me.dags.noise.util.Vec2i; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverManager.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java similarity index 89% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverManager.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java index 1dc5e1f..6238623 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverManager.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap; import com.terraforged.core.cell.Cell; import com.terraforged.core.region.Region; @@ -32,21 +32,24 @@ import com.terraforged.core.util.concurrent.cache.Cache; import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.world.GeneratorContext; import com.terraforged.core.world.heightmap.Heightmap; +import com.terraforged.core.world.rivermap.lake.LakeConfig; +import com.terraforged.core.world.rivermap.river.RiverConfig; +import com.terraforged.core.world.rivermap.river.RiverRegion; import com.terraforged.core.world.terrain.Terrain; import me.dags.noise.util.NoiseUtil; import java.util.concurrent.TimeUnit; -public class RiverManager { +public class RiverMap { private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2; private final Heightmap heightmap; private final GeneratorContext context; - private final RiverContext riverContext; - private final Cache> cache = new Cache<>(120, 60, TimeUnit.SECONDS); + private final RiverMapConfig riverMapConfig; + private final Cache> cache; - public RiverManager(Heightmap heightmap, GeneratorContext context) { + public RiverMap(Heightmap heightmap, GeneratorContext context) { RiverConfig primary = RiverConfig.builder(context.levels) .bankHeight(context.settings.rivers.primaryRivers.minBankHeight, context.settings.rivers.primaryRivers.maxBankHeight) .bankWidth(context.settings.rivers.primaryRivers.bankWidth) @@ -76,7 +79,8 @@ public class RiverManager { this.heightmap = heightmap; this.context = context; - this.riverContext = new RiverContext(context.settings.rivers.riverFrequency, primary, secondary, tertiary, lakes); + this.riverMapConfig = new RiverMapConfig(context.settings.rivers.riverFrequency, primary, secondary, tertiary, lakes); + this.cache = new Cache<>(120, 60, TimeUnit.SECONDS); } public RiverRegionList getRivers(Region region) { @@ -100,8 +104,7 @@ public class RiverManager { RiverRegionList list = new RiverRegionList(); for (int dz = minZ; dz <= maxZ; dz++) { for (int dx = minX; dx <= maxX; dx++) { - CacheEntry entry = getRegion(rx + dx, rz + dz); - list.add(entry); + list.add(getRegion(rx + dx, rz + dz)); } } @@ -148,6 +151,6 @@ public class RiverManager { } private CacheEntry generateRegion(int rx, int rz) { - return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverContext), ThreadPool.getDefaultPool()); + return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), ThreadPool.getDefaultPool()); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverContext.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMapConfig.java similarity index 52% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverContext.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMapConfig.java index 721787f..957fedc 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverContext.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMapConfig.java @@ -1,6 +1,9 @@ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap; -public class RiverContext { +import com.terraforged.core.world.rivermap.lake.LakeConfig; +import com.terraforged.core.world.rivermap.river.RiverConfig; + +public class RiverMapConfig { public final float frequency; public final RiverConfig primary; @@ -8,7 +11,7 @@ public class RiverContext { public final RiverConfig tertiary; public final LakeConfig lakes; - public RiverContext(float frequency, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lakes) { + public RiverMapConfig(float frequency, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lakes) { this.frequency = frequency; this.primary = primary; this.secondary = secondary; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegionList.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverRegionList.java similarity index 77% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegionList.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverRegionList.java index 3a5092c..ce6bb17 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegionList.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverRegionList.java @@ -1,7 +1,8 @@ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap; import com.terraforged.core.cell.Cell; import com.terraforged.core.util.concurrent.cache.CacheEntry; +import com.terraforged.core.world.rivermap.river.RiverRegion; import com.terraforged.core.world.terrain.Terrain; public class RiverRegionList { @@ -10,7 +11,10 @@ public class RiverRegionList { private final CacheEntry[] regions = new CacheEntry[4]; protected void add(CacheEntry entry) { - regions[index++] = entry; + if (index < regions.length) { + regions[index] = entry; + index++; + } } public void apply(Cell cell, float x, float z) { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/Lake.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/Lake.java similarity index 97% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/Lake.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/Lake.java index ea52dbf..c41b639 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/Lake.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/Lake.java @@ -23,9 +23,10 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.lake; import com.terraforged.core.cell.Cell; +import com.terraforged.core.world.rivermap.river.River; import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.TerrainPopulator; import com.terraforged.core.world.terrain.Terrains; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/LakeConfig.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/LakeConfig.java similarity index 98% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/LakeConfig.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/LakeConfig.java index 2a1ae29..49c7f82 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/LakeConfig.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/lake/LakeConfig.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.lake; import com.terraforged.core.settings.RiverSettings; import com.terraforged.core.world.heightmap.Levels; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/River.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java similarity index 99% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/River.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java index 3632e75..f3d4262 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/River.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/River.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.river; import com.terraforged.core.cell.Cell; import com.terraforged.core.world.terrain.Terrain; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverBounds.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverBounds.java similarity index 98% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverBounds.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverBounds.java index 1d60608..9b78bc7 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverBounds.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverBounds.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.river; import me.dags.noise.util.Vec2f; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverConfig.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverConfig.java similarity index 98% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverConfig.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverConfig.java index 0f7a2ed..3f9b70e 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverConfig.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverConfig.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.river; import com.terraforged.core.world.heightmap.Levels; import me.dags.noise.util.NoiseUtil; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverNode.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverNode.java similarity index 97% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverNode.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverNode.java index b953abb..52b3952 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverNode.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverNode.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.river; public class RiverNode { diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegion.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverRegion.java similarity index 89% rename from TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegion.java rename to TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverRegion.java index 5e10e09..585a705 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/river/RiverRegion.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/river/RiverRegion.java @@ -23,13 +23,17 @@ * SOFTWARE. */ -package com.terraforged.core.world.river; +package com.terraforged.core.world.rivermap.river; import com.terraforged.core.cell.Cell; import com.terraforged.core.util.concurrent.ObjectPool; import com.terraforged.core.util.concurrent.cache.ExpiringEntry; import com.terraforged.core.world.GeneratorContext; import com.terraforged.core.world.heightmap.Heightmap; +import com.terraforged.core.world.rivermap.PosGenerator; +import com.terraforged.core.world.rivermap.RiverMapConfig; +import com.terraforged.core.world.rivermap.lake.Lake; +import com.terraforged.core.world.rivermap.lake.LakeConfig; import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrains; import me.dags.noise.domain.Domain; @@ -73,27 +77,27 @@ public class RiverRegion implements ExpiringEntry { private final long timestamp = System.currentTimeMillis() + EXPIRE_TIME; - public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverContext riverContext) { + public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverMapConfig riverMapConfig) { int seed = new Random(NoiseUtil.seed(regionX, regionZ)).nextInt(); - this.lake = riverContext.lakes; - this.primary = riverContext.primary; - this.secondary = riverContext.secondary; - this.tertiary = riverContext.tertiary; + this.lake = riverMapConfig.lakes; + this.primary = riverMapConfig.primary; + this.secondary = riverMapConfig.secondary; + this.tertiary = riverMapConfig.tertiary; this.terrains = context.terrain; this.domain = Domain.warp(seed, 400, 1, 400) .add(Domain.warp(seed + 1, 80, 1, 35)); - this.primaryLimit = NoiseUtil.round(10 * riverContext.frequency); - this.secondaryLimit = NoiseUtil.round(20 * riverContext.frequency); - this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverContext.frequency); - this.forkLimit = NoiseUtil.round(40 * riverContext.frequency); - this.tertiaryLimit = NoiseUtil.round(50 * riverContext.frequency); + this.primaryLimit = NoiseUtil.round(10 * riverMapConfig.frequency); + this.secondaryLimit = NoiseUtil.round(20 * riverMapConfig.frequency); + this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverMapConfig.frequency); + this.forkLimit = NoiseUtil.round(40 * riverMapConfig.frequency); + this.tertiaryLimit = NoiseUtil.round(50 * riverMapConfig.frequency); - this.primaryAttempts = NoiseUtil.round(100 * riverContext.frequency); - this.secondaryAttempts = NoiseUtil.round(100 * riverContext.frequency); - this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverContext.frequency); - this.forkAttempts = NoiseUtil.round(75 * riverContext.frequency); - this.tertiaryAttempts = NoiseUtil.round(50 * riverContext.frequency); + this.primaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency); + this.secondaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency); + this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverMapConfig.frequency); + this.forkAttempts = NoiseUtil.round(75 * riverMapConfig.frequency); + this.tertiaryAttempts = NoiseUtil.round(50 * riverMapConfig.frequency); try (ObjectPool.Item> cell = Cell.pooled()) { PosGenerator pos = new PosGenerator(heightmap, domain, cell.getValue(),1 << SCALE, River.VALLEY_WIDTH); diff --git a/TerraForgedMod/build.gradle b/TerraForgedMod/build.gradle index 793201e..928ead1 100644 --- a/TerraForgedMod/build.gradle +++ b/TerraForgedMod/build.gradle @@ -24,15 +24,9 @@ repositories { dependencies { minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" compile project(":Noise2D") - compile (project(":TerraForgedCore")) { - transitive false - } - compile (project(":FeatureManager")) { - transitive false - } - compile (project(":TerraForgedAPI")) { - transitive false - } + compile (project(":TerraForgedCore")) { transitive false } + compile (project(":FeatureManager")) { transitive false } + compile (project(":TerraForgedAPI")) { transitive false } } minecraft { @@ -81,7 +75,6 @@ task collectResources(type: Copy) { processResources { dependsOn(collectResources) - filesMatching("**/mods.toml") { expand( "version": "${mod_version}${getClassifier()}", diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java index 184b466..54a6e02 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/TerrainHelper.java @@ -25,6 +25,7 @@ package com.terraforged.mod.feature; +import com.terraforged.api.material.state.States; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; @@ -89,7 +90,7 @@ public class TerrainHelper { } } - // try to fill in type air beneath village pieces with the biomes default filler block + // lowers or raises the terrain matcher the base height of each structure piece private void buildBases(IChunk chunk, ObjectList pieces, int chunkStartX, int chunkStartZ) { BlockPos.Mutable pos = new BlockPos.Mutable(); MutableBoundingBox chunkBounds = new MutableBoundingBox(chunkStartX, chunkStartZ, chunkStartX + 15, chunkStartZ + 15); @@ -110,6 +111,9 @@ public class TerrainHelper { int endX = Math.min(chunkStartX + 15, expanded.maxX); int endZ = Math.min(chunkStartZ + 15, expanded.maxZ); + // y position of the structure piece + int level = pieceBounds.minY + piece.getGroundLevelDelta(); + // iterate the intersecting area for (int z = startZ; z <= endZ; z++) { for (int x = startX; x <= endX; x++) { @@ -117,41 +121,57 @@ public class TerrainHelper { int dx = x & 15; int dz = z & 15; - // the paste position of the village piece - BlockPos position = piece.getPos(); - - int offset = piece.getGroundLevelDelta(); - int level = position.getY() + (offset - 1); - int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz) - 1; - int height = level - surface; - if (height <= 0) { + int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz); + if (surface == level) { continue; } - float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(x, z)); - float alpha = getAlpha(pieceBounds, radius2, x, z); - if (alpha == 0F) { - continue; - } - - if (alpha < 1F) { - alpha = alpha * alpha; - height = NoiseUtil.round(alpha * height); - } - - BlockState state = Blocks.STONE.getDefaultState(); - for (int dy = surface + height; dy >= surface; dy--) { - pos.setPos(dx, dy, dz); - if (chunk.getBlockState(pos).isSolid()) { - break; - } - chunk.setBlockState(pos.setPos(dx, dy, dz), state, false); + if (surface > level) { + // world-surface is higher than the piece's base .: flatten terrain + flatten(chunk, pieceBounds, pos.setPos(x, surface, z), dx, dz, level, surface, borderRadius); + } else { + // piece is higher than world-surface .: raise ground to form a base + raise(chunk, pieceBounds, pos.setPos(x, surface, z), dx, dz, level, surface, borderRadius); } } } } } + private void flatten(IChunk chunk, MutableBoundingBox bounds, BlockPos.Mutable pos, int dx, int dz, int level, int surface, int borderRadius) { + // only flatten terrain within the footprint of the structure piece + if (pos.getX() >= bounds.minX && pos.getX() <= bounds.maxX && pos.getZ() >= bounds.minZ && pos.getZ() <= bounds.maxZ) { + for (int dy = level + 1; dy <= surface; dy++) { + chunk.setBlockState(pos.setPos(dx, dy, dz), Blocks.AIR.getDefaultState(), false); + } + } + } + + private void raise(IChunk chunk, MutableBoundingBox bounds, BlockPos.Mutable pos, int dx, int dz, int level, int surface, int borderRadius) { + float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(pos.getX(), pos.getZ())); + float alpha = getAlpha(bounds, radius2, pos.getX(), pos.getZ()); + if (alpha == 0F) { + // outside of the raise-able radius + return; + } + + int heightDelta = level - surface - 1; + if (alpha < 1F) { + // sharper fall-off + alpha = alpha * alpha; + heightDelta = NoiseUtil.round(alpha * heightDelta); + } + + BlockState state = States.STONE.getDefaultState(); + for (int dy = surface + heightDelta; dy >= surface; dy--) { + pos.setPos(dx, dy, dz); + if (chunk.getBlockState(pos).isSolid()) { + return; + } + chunk.setBlockState(pos.setPos(dx, dy, dz), state, false); + } + } + private static MutableBoundingBox expand(MutableBoundingBox box, int radius) { return new MutableBoundingBox( box.minX - radius, diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/BetterAtSurfaceWithChanceMultiple.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/BetterAtSurfaceWithChanceMultiple.java deleted file mode 100644 index 6c079d6..0000000 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/BetterAtSurfaceWithChanceMultiple.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.terraforged.mod.feature.placement; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IWorld; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.Heightmap; -import net.minecraft.world.gen.placement.HeightWithChanceConfig; -import net.minecraft.world.gen.placement.Placement; - -import java.util.Random; -import java.util.stream.Stream; - -public class BetterAtSurfaceWithChanceMultiple extends Placement { - - public BetterAtSurfaceWithChanceMultiple() { - super(HeightWithChanceConfig::deserialize); - } - - @Override - public Stream getPositions(IWorld world, ChunkGenerator generator, Random random, HeightWithChanceConfig config, BlockPos pos) { - return PosStream.of(config.count, next -> { - if (random.nextFloat() < config.chance) { - int x = random.nextInt(16) + pos.getX(); - int z = random.nextInt(16) + pos.getZ(); - int y = world.getHeight(Heightmap.Type.MOTION_BLOCKING, x, z); - next.setPos(x, y, z); - return true; - } - return false; - }); - } -} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosGenerator.java deleted file mode 100644 index bf0f077..0000000 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosGenerator.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.terraforged.mod.feature.placement; - -import net.minecraft.util.math.BlockPos; - -public interface PosGenerator { - - boolean generate(BlockPos.Mutable next); -} diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosStream.java b/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosStream.java deleted file mode 100644 index 28ab6fa..0000000 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/feature/placement/PosStream.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.terraforged.mod.feature.placement; - -import net.minecraft.util.math.BlockPos; - -import java.util.Spliterator; -import java.util.function.Consumer; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -public final class PosStream { - - private static final ThreadLocal MUTABLE_THREAD_LOCAL = ThreadLocal.withInitial(BlockPos.Mutable::new); - - private PosStream() { - - } - - public static Stream of(int count, Populator populator) { - return StreamSupport.stream(new PosSpliterator(count, populator), false); - } - - @FunctionalInterface - public interface Populator { - - /** - * Populates the BlockPos.Mutable with the next x,y,z coordinates - * - * @param next the BlockPos.Mutable to populate - * @return true if the populated BlockPos.Mutable should be used next in the stream - */ - boolean populate(BlockPos.Mutable next); - } - - private static class PosSpliterator implements Spliterator { - - private final int attempts; - private final Populator populator; - private final BlockPos.Mutable pos = MUTABLE_THREAD_LOCAL.get(); - - private int counter = -1; - - private PosSpliterator(int attempts, Populator populator) { - this.attempts = attempts; - this.populator = populator; - } - - @Override - public boolean tryAdvance(Consumer action) { - if (counter < attempts) { - counter++; - if (populator.populate(pos)) { - action.accept(pos); - } - return true; - } - return false; - } - - @Override - public Spliterator trySplit() { - // cannot split - return null; - } - - @Override - public long estimateSize() { - return attempts; - } - - @Override - public int characteristics() { - // assume that positions are generated from a seeded random .: ordered - return Spliterator.ORDERED; - } - } -}