diff --git a/common/src/main/java/com/dfsek/terra/carving/CarverCache.java b/common/src/main/java/com/dfsek/terra/carving/CarverCache.java index 809dd6936..c6ce44956 100644 --- a/common/src/main/java/com/dfsek/terra/carving/CarverCache.java +++ b/common/src/main/java/com/dfsek/terra/carving/CarverCache.java @@ -12,7 +12,8 @@ import com.dfsek.terra.api.world.generation.GenerationPhase; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.grid.master.TerraBiomeGrid; -import java.util.HashMap; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; @@ -20,35 +21,42 @@ import java.util.Random; public class CarverCache { private final World w; - private final Map> carvers = new HashMap<>(); + private final Map> carvers; private final TerraPlugin main; public CarverCache(World w, TerraPlugin main) { this.w = w; this.main = main; + carvers = Collections.synchronizedMap(new LinkedHashMap>() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return this.size() > main.getTerraConfig().getCarverCacheSize(); + } + }); } public List getPoints(int chunkX, int chunkZ, UserDefinedCarver carver) { - if(carvers.size() > main.getTerraConfig().getCarverCacheSize()) carvers.clear(); - return carvers.computeIfAbsent((((long) chunkX) << 32) | (chunkZ & 0xffffffffL), key -> { - TerraBiomeGrid grid = main.getWorld(w).getGrid(); - if(carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + carver.hashCode())))) { - long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed()); - carver.getSeedVar().setValue(seed); - Random r = new FastRandom(seed); - Worm carving = carver.getWorm(seed, new Vector3((chunkX << 4) + r.nextInt(16), carver.getConfig().getHeight().get(r), (chunkZ << 4) + r.nextInt(16))); - List points = new GlueList<>(); - for(int i = 0; i < carving.getLength(); i++) { - carving.step(); - Biome biome = grid.getBiome(carving.getRunning().toLocation(w), GenerationPhase.POPULATE); - if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(carver)) { // Stop if we enter a biome this carver is not present in - return new GlueList<>(); + synchronized(carvers) { + return carvers.computeIfAbsent((((long) chunkX) << 32) | (chunkZ & 0xffffffffL), key -> { + TerraBiomeGrid grid = main.getWorld(w).getGrid(); + if(carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + carver.hashCode())))) { + long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed()); + carver.getSeedVar().setValue(seed); + Random r = new FastRandom(seed); + Worm carving = carver.getWorm(seed, new Vector3((chunkX << 4) + r.nextInt(16), carver.getConfig().getHeight().get(r), (chunkZ << 4) + r.nextInt(16))); + List points = new GlueList<>(); + for(int i = 0; i < carving.getLength(); i++) { + carving.step(); + Biome biome = grid.getBiome(carving.getRunning().toLocation(w), GenerationPhase.POPULATE); + if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(carver)) { // Stop if we enter a biome this carver is not present in + return new GlueList<>(); + } + points.add(carving.getPoint()); } - points.add(carving.getPoint()); + return points; } - return points; - } - return new GlueList<>(); - }); + return new GlueList<>(); + }); + } } } diff --git a/platforms/region/src/main/java/com/dfsek/terra/RegionGenerator.java b/platforms/region/src/main/java/com/dfsek/terra/RegionGenerator.java index 52ceb47f6..59a48910b 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/RegionGenerator.java +++ b/platforms/region/src/main/java/com/dfsek/terra/RegionGenerator.java @@ -6,12 +6,13 @@ import java.io.IOException; import java.util.concurrent.ThreadLocalRandom; public class RegionGenerator { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { long seed; if(args.length == 1) seed = Long.parseLong(args[0]); else seed = ThreadLocalRandom.current().nextLong(); - Generator generator = new Generator(seed); + StandalonePlugin plugin = new StandalonePlugin(); + Generator generator = new Generator(seed, plugin); generator.generate(); } diff --git a/platforms/region/src/main/java/com/dfsek/terra/async/Counter.java b/platforms/region/src/main/java/com/dfsek/terra/async/Counter.java new file mode 100644 index 000000000..57566f20d --- /dev/null +++ b/platforms/region/src/main/java/com/dfsek/terra/async/Counter.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.async; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +public class Counter { + private final AtomicInteger integer = new AtomicInteger(); + private final Consumer update; + + public Counter(Consumer update) { + this.update = update; + } + + public void add() { + update.accept(integer.addAndGet(1)); + } +} diff --git a/platforms/region/src/main/java/com/dfsek/terra/async/GenerationWorker.java b/platforms/region/src/main/java/com/dfsek/terra/async/GenerationWorker.java new file mode 100644 index 000000000..0561d1f9f --- /dev/null +++ b/platforms/region/src/main/java/com/dfsek/terra/async/GenerationWorker.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.async; + +import com.dfsek.terra.DirectUtils; +import com.dfsek.terra.api.math.MathUtil; +import com.dfsek.terra.api.util.FastRandom; +import com.dfsek.terra.platform.DirectChunkData; +import com.dfsek.terra.platform.DirectWorld; +import com.dfsek.terra.region.Generator; +import net.querz.mca.MCAUtil; + +import java.io.File; +import java.util.concurrent.RecursiveAction; + +public class GenerationWorker extends RecursiveAction { + private final int x; + private final int z; + private final DirectWorld world; + private final Generator generator; + private final Counter counter; + + public GenerationWorker(int x, int z, DirectWorld world, Generator generator, Counter counter) { + this.x = x; + this.z = z; + this.world = world; + this.generator = generator; + this.counter = counter; + } + + + @Override + protected void compute() { + for(int cx = x * 32; cx < x * 32 + 32; cx++) { + for(int cz = z * 32; cz <= z * 32 + 32; cz++) { + DirectChunkData chunkData = (DirectChunkData) world.getChunkAt(cx, cz); + generator.getGenerator().generateChunkData(world, null, cx, cz, chunkData); + + generator.getCavePopulator().populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, world.getSeed())), chunkData); + generator.getStructurePopulator().populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, world.getSeed())), chunkData); + generator.getOrePopulator().populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, world.getSeed())), chunkData); + generator.getFloraPopulator().populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, world.getSeed())), chunkData); + generator.getTreePopulator().populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, world.getSeed())), chunkData); + + counter.add(); + } + } + long regionID = DirectUtils.regionID(x, z); + try { + File file = new File("region", MCAUtil.createNameFromRegionLocation(x, z)); + file.getParentFile().mkdirs(); + MCAUtil.write(world.getFiles().get(regionID), file); + } catch(Exception e) { + e.printStackTrace(); + } + world.getFiles().remove(regionID); + System.out.println("Saved region [" + x + "," + z + "]. " + world.getFiles().size() + " regions remain loaded."); + } +} diff --git a/platforms/region/src/main/java/com/dfsek/terra/platform/Data.java b/platforms/region/src/main/java/com/dfsek/terra/platform/Data.java index 033866ede..0ba30f150 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/platform/Data.java +++ b/platforms/region/src/main/java/com/dfsek/terra/platform/Data.java @@ -33,8 +33,7 @@ public class Data implements BlockData, MaterialData { } else { this.data = tag; } - String id = data.getString("Name"); - noProp = id; + noProp = data.getString("Name"); } diff --git a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectBlock.java b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectBlock.java index 5dfa4194f..55372978b 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectBlock.java +++ b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectBlock.java @@ -20,7 +20,9 @@ public class DirectBlock implements Block { @Override public void setBlockData(BlockData data, boolean physics) { - world.compute(FastMath.floorDiv(pos.getBlockX(), 16), FastMath.floorDiv(pos.getBlockZ(), 16)).setBlockStateAt(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), ((Data) data).getHandle(), false); + synchronized(world) { + world.compute(FastMath.floorDiv(pos.getBlockX(), 16), FastMath.floorDiv(pos.getBlockZ(), 16)).setBlockStateAt(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ(), ((Data) data).getHandle(), false); + } } @Override diff --git a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java index dcf6e79f5..b3c3b344a 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java +++ b/platforms/region/src/main/java/com/dfsek/terra/platform/DirectWorld.java @@ -16,6 +16,8 @@ import net.querz.mca.MCAUtil; import net.querz.nbt.tag.CompoundTag; import java.io.File; +import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -23,7 +25,7 @@ import java.util.UUID; public class DirectWorld implements World { private final long seed; private final GenWrapper generator; - private final Map files = new HashMap<>(); + private final Map files = Collections.synchronizedMap(new HashMap<>()); public DirectWorld(long seed, GenWrapper generator) { this.seed = seed; @@ -102,7 +104,20 @@ public class DirectWorld implements World { } public MCAFile compute(int x, int z) { - return files.computeIfAbsent(DirectUtils.regionID(x, z), k -> new MCAFile(MCAUtil.chunkToRegion(x), MCAUtil.chunkToRegion(z))); + synchronized(files) { + return files.computeIfAbsent(DirectUtils.regionID(x, z), k -> { + File test = new File("region", MCAUtil.createNameFromChunkLocation(x, z)); + if(test.exists()) { + try { + System.out.println("Re-loading " + MCAUtil.createNameFromChunkLocation(x, z)); + return MCAUtil.read(test); + } catch(IOException e) { + e.printStackTrace(); + } + } + return new MCAFile(MCAUtil.chunkToRegion(x), MCAUtil.chunkToRegion(z)); + }); + } } public CompoundTag getData(int x, int y, int z) { diff --git a/platforms/region/src/main/java/com/dfsek/terra/region/Generator.java b/platforms/region/src/main/java/com/dfsek/terra/region/Generator.java index 730c3be95..43fcd3352 100644 --- a/platforms/region/src/main/java/com/dfsek/terra/region/Generator.java +++ b/platforms/region/src/main/java/com/dfsek/terra/region/Generator.java @@ -1,12 +1,11 @@ package com.dfsek.terra.region; import com.dfsek.terra.StandalonePlugin; -import com.dfsek.terra.api.math.MathUtil; -import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.util.GlueList; +import com.dfsek.terra.async.Counter; +import com.dfsek.terra.async.GenerationWorker; import com.dfsek.terra.generation.MasterChunkGenerator; import com.dfsek.terra.generation.math.SamplerCache; -import com.dfsek.terra.platform.DirectChunkData; import com.dfsek.terra.platform.DirectWorld; import com.dfsek.terra.platform.GenWrapper; import com.dfsek.terra.population.CavePopulator; @@ -21,67 +20,69 @@ import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.atomic.AtomicLong; public class Generator { private final long seed; + FloraPopulator floraPopulator; + StructurePopulator structurePopulator; + TreePopulator treePopulator; + OrePopulator orePopulator; + CavePopulator cavePopulator; + MasterChunkGenerator generator; - public Generator(long seed) { + public Generator(long seed, StandalonePlugin plugin) { + plugin.load(); + floraPopulator = new FloraPopulator(plugin); + structurePopulator = new StructurePopulator(plugin); + treePopulator = new TreePopulator(plugin); + orePopulator = new OrePopulator(plugin); + cavePopulator = new CavePopulator(plugin); + generator = new MasterChunkGenerator(plugin.getRegistry().get("DEFAULT"), plugin, new SamplerCache(plugin)); this.seed = seed; } public void generate() throws IOException { - int rad = 25; - - StandalonePlugin plugin = new StandalonePlugin(); - plugin.load(); + int rad = 64; + System.out.println("Total mem: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024 + "GB"); - MasterChunkGenerator generator = new MasterChunkGenerator(plugin.getRegistry().get("DEFAULT"), plugin, new SamplerCache(plugin)); GenWrapper wrapper = new GenWrapper(generator); - - FloraPopulator floraPopulator = new FloraPopulator(plugin); - StructurePopulator structurePopulator = new StructurePopulator(plugin); - TreePopulator treePopulator = new TreePopulator(plugin); - OrePopulator orePopulator = new OrePopulator(plugin); - CavePopulator cavePopulator = new CavePopulator(plugin); - - int count = 0; - - List times = new GlueList<>(); - DirectWorld world = new DirectWorld(seed, wrapper); - for(int cx = -rad; cx <= rad; cx++) { - for(int cz = -rad; cz <= rad; cz++) { - long start = System.nanoTime(); + AtomicLong l = new AtomicLong(System.nanoTime()); - DirectChunkData chunkData = (DirectChunkData) world.getChunkAt(cx, cz); - generator.generateChunkData(world, null, cx, cz, chunkData); + Counter counter = new Counter((id) -> { + if(id % 200 == 0) { + long c = System.nanoTime(); - cavePopulator.populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, seed)), chunkData); - structurePopulator.populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, seed)), chunkData); - orePopulator.populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, seed)), chunkData); - floraPopulator.populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, seed)), chunkData); - treePopulator.populate(world, new FastRandom(MathUtil.getCarverChunkSeed(cx, cz, seed)), chunkData); + long diff = c - l.get(); + + double ms = (double) diff / 1000000; + + System.out.println("[" + Thread.currentThread().getName() + "] Generated " + id + " chunks. " + (200d / ms) * 1000 + " cps. " + world.getFiles().size() + " Regions loaded."); + l.set(System.nanoTime()); + } + }); - long end = System.nanoTime() - start; - count++; - times.add((double) end / 1000000); + List workers = new GlueList<>(); - if(count % 200 == 0) { - double total = 0; - for(double d : times) total += d; - double avg = total / count; - double pct = ((double) count / (rad * rad * 2 * 2)) * 100D; - - plugin.getLogger().info("Generated " + count + " chunks. " + (1 / avg) * 1000 + "cps; " + pct + "%"); - } + for(int x = -rad / 32; x <= rad / 32; x++) { + for(int z = -rad / 32; z <= rad / 32; z++) { + workers.add(new GenerationWorker(x, z, world, this, counter)); } } + + ForkJoinTask.invokeAll(workers); + + + System.out.println("Saving..."); + for(Map.Entry entry : world.getFiles().entrySet()) { if(entry.getValue() == null) continue; entry.getValue().cleanupPalettesAndBlockStates(); @@ -91,5 +92,35 @@ public class Generator { file.getParentFile().mkdirs(); MCAUtil.write(entry.getValue(), file); } + + System.out.println("Done in " + (System.nanoTime() - l.get()) / 1000000000 + "s"); + } + + public StructurePopulator getStructurePopulator() { + return structurePopulator; + } + + public OrePopulator getOrePopulator() { + return orePopulator; + } + + public CavePopulator getCavePopulator() { + return cavePopulator; + } + + public TreePopulator getTreePopulator() { + return treePopulator; + } + + public FloraPopulator getFloraPopulator() { + return floraPopulator; + } + + public long getSeed() { + return seed; + } + + public MasterChunkGenerator getGenerator() { + return generator; } }