From 5142264bcf9a24a6711b2266cfc15482b1a7a114 Mon Sep 17 00:00:00 2001 From: dags- Date: Fri, 20 Mar 2020 23:32:17 +0000 Subject: [PATCH] - ignore 3rd party ChunkPrimer candidates for the fast chunk wrapper - some tweaks to terrains stuff --- .../main/java/com/terraforged/app/Main.java | 2 +- .../core/settings/GeneratorSettings.java | 2 + .../core/util/concurrent/ThreadPool.java | 23 +++----- .../core/util/concurrent/cache/Cache.java | 3 +- .../util/concurrent/cache/CacheEntry.java | 42 +++++---------- .../util/serialization/annotation/Name.java | 38 +++++++++++++ .../serialization/serializer/Serializer.java | 5 +- .../core/world/rivermap/RiverMap.java | 3 +- .../core/world/terrain/Terrain.java | 27 ++++++++-- .../com/terraforged/mod/TerraForgedMod.java | 8 --- .../com/terraforged/mod/chunk/FastChunk.java | 54 ++++++++++++++++++- .../mod/chunk/TerraChunkGenerator.java | 2 + .../mod/command/arg/TerrainArgType.java | 40 +------------- 13 files changed, 149 insertions(+), 100 deletions(-) create mode 100644 TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/annotation/Name.java diff --git a/TerraForgedApp/src/main/java/com/terraforged/app/Main.java b/TerraForgedApp/src/main/java/com/terraforged/app/Main.java index 08fe3a6..b1627ff 100644 --- a/TerraForgedApp/src/main/java/com/terraforged/app/Main.java +++ b/TerraForgedApp/src/main/java/com/terraforged/app/Main.java @@ -91,7 +91,7 @@ public class Main extends Applet { if (cell.tag == getCache().getTerrain().volcano) { return 0F; } - return 20 + (cell.tag.getId() / (float) Terrain.MAX_ID.get()) * 80; + return 20 + (cell.tag.getHue() * 80); case ELEVATION: float value = (cell.value - 0.245F) / 0.65F; return (1 - value) * 30; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/settings/GeneratorSettings.java b/TerraForgedCore/src/main/java/com/terraforged/core/settings/GeneratorSettings.java index e6068f7..9d6e660 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/settings/GeneratorSettings.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/settings/GeneratorSettings.java @@ -26,6 +26,7 @@ package com.terraforged.core.settings; import com.terraforged.core.util.serialization.annotation.Comment; +import com.terraforged.core.util.serialization.annotation.Name; import com.terraforged.core.util.serialization.annotation.Range; import com.terraforged.core.util.serialization.annotation.Serializable; import me.dags.noise.Module; @@ -77,6 +78,7 @@ public class GeneratorSettings { public int continentScale = 4000; @Range(min = 250, max = 5000) + @Name("Mountain Range Scale") @Comment("Controls the size of mountain ranges") public int mountainScale = 950; diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java index afc6f2b..29856f6 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/concurrent/ThreadPool.java @@ -30,34 +30,30 @@ import com.terraforged.core.util.concurrent.batcher.Batcher; import java.util.concurrent.Callable; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Future; +import java.util.concurrent.ForkJoinTask; public class ThreadPool implements Executor { - private static final ThreadPool instance = new ThreadPool("TerraForged", defaultPoolSize()); + private static final ThreadPool instance = new ThreadPool("TF", defaultPoolSize()); + private static final Object lock = new Object(); - private final ExecutorService service; + private final ForkJoinPool service; private ThreadPool(String name, int size) { this.service = ThreadPool.createPool(size, name); } - public void shutdown() { - service.shutdown(); - } - @Override public void execute(Runnable command) { - service.execute(command); + service.submit(command); } - public Future submit(Runnable runnable) { + public ForkJoinTask submit(Runnable runnable) { return service.submit(runnable); } - public Future submit(Callable callable) { + public ForkJoinTask submit(Callable callable) { return service.submit(callable); } @@ -73,16 +69,13 @@ public class ThreadPool implements Executor { return new ThreadPool("Pool", Math.max(1, size)); } - public static void shutdownCurrent() { - instance.shutdown(); - } - private static int defaultPoolSize() { int threads = Runtime.getRuntime().availableProcessors(); return Math.max(1, (int) ((threads / 3F) * 2F)); } public static ForkJoinPool createPool(int size, String name) { + return new ForkJoinPool(size, new WorkerFactory.ForkJoin(name), null, true); } } 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 c6a5ba1..1b2d32c 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 @@ -10,6 +10,7 @@ public class Cache implements Runnable { private final long expireMS; private final long intervalMS; private final SynchronizedLongMap map; + private final ThreadPool threadPool = ThreadPool.getPool(); private volatile long timestamp = 0L; @@ -33,7 +34,7 @@ public class Cache implements Runnable { long now = System.currentTimeMillis(); if (now - timestamp > intervalMS) { timestamp = now; - ThreadPool.getPool().execute(this); + threadPool.submit(this); } } 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 82b4a3d..73e5ba1 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,17 +1,17 @@ package com.terraforged.core.util.concurrent.cache; -import java.util.concurrent.Executor; -import java.util.function.Supplier; +import com.terraforged.core.util.concurrent.ThreadPool; -public class CacheEntry implements Runnable, ExpiringEntry { +import java.util.concurrent.Callable; +import java.util.concurrent.ForkJoinTask; + +public class CacheEntry implements ExpiringEntry { - private volatile T result; private volatile long timestamp; + private final ForkJoinTask task; - private final Supplier supplier; - - private CacheEntry(Supplier supplier) { - this.supplier = supplier; + public CacheEntry(ForkJoinTask task) { + this.task = task; this.timestamp = System.currentTimeMillis(); } @@ -20,33 +20,15 @@ public class CacheEntry implements Runnable, ExpiringEntry { return timestamp; } - @Override - public void run() { - this.result = supplier.get(); - this.timestamp = System.currentTimeMillis(); - } - public boolean isDone() { - return result != null; + return task.isDone(); } public T get() { - T value = result; - if (value == null) { - value = getNow(); - } - return value; + return task.join(); } - 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; + public static CacheEntry supplyAsync(Callable callable, ThreadPool executor) { + return new CacheEntry<>(executor.submit(callable)); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/annotation/Name.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/annotation/Name.java new file mode 100644 index 0000000..ef32732 --- /dev/null +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/annotation/Name.java @@ -0,0 +1,38 @@ +/* + * + * MIT License + * + * Copyright (c) 2020 TerraForged + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.terraforged.core.util.serialization.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Name { + + String value(); +} diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/serializer/Serializer.java b/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/serializer/Serializer.java index c0432f8..9891b89 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/serializer/Serializer.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/util/serialization/serializer/Serializer.java @@ -26,6 +26,7 @@ package com.terraforged.core.util.serialization.serializer; import com.terraforged.core.util.serialization.annotation.Comment; +import com.terraforged.core.util.serialization.annotation.Name; import com.terraforged.core.util.serialization.annotation.Range; import com.terraforged.core.util.serialization.annotation.Serializable; @@ -163,7 +164,9 @@ public class Serializer { } private static String getName(Field field) { - String name = field.getName(); + Name nameMeta = field.getAnnotation(Name.class); + + String name = nameMeta == null ? field.getName() : nameMeta.value(); StringBuilder sb = new StringBuilder(name.length() * 2); for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java index 80f90f1..45bb80a 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/rivermap/RiverMap.java @@ -48,6 +48,7 @@ public class RiverMap { private final GeneratorContext context; private final RiverMapConfig riverMapConfig; private final Cache> cache; + private final ThreadPool threadPool = ThreadPool.getPool(); public RiverMap(Heightmap heightmap, GeneratorContext context) { RiverConfig primary = RiverConfig.builder(context.levels) @@ -151,6 +152,6 @@ public class RiverMap { } private CacheEntry generateRegion(int rx, int rz) { - return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), ThreadPool.getPool()); + return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), threadPool); } } diff --git a/TerraForgedCore/src/main/java/com/terraforged/core/world/terrain/Terrain.java b/TerraForgedCore/src/main/java/com/terraforged/core/world/terrain/Terrain.java index d153061..fb1d3e5 100644 --- a/TerraForgedCore/src/main/java/com/terraforged/core/world/terrain/Terrain.java +++ b/TerraForgedCore/src/main/java/com/terraforged/core/world/terrain/Terrain.java @@ -29,17 +29,25 @@ import com.terraforged.core.cell.Tag; import com.terraforged.core.settings.Settings; import com.terraforged.core.world.heightmap.Levels; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; public class Terrain implements Tag { - public static final int ID_START = 9; - public static final AtomicInteger MAX_ID = new AtomicInteger(0); + private static final AtomicInteger MAX_ID = new AtomicInteger(0); + private static final Map register = Collections.synchronizedMap(new HashMap<>()); + + public static final int ID_START = 10; public static final Terrain NONE = new Terrain("none", -1); private final String name; private final int id; - private float weight; + private final float weight; public Terrain(String name, int id) { this(name, id, 1F); @@ -49,6 +57,7 @@ public class Terrain implements Tag { this.name = name; this.id = id; this.weight = (float) weight; + register.put(name, this); MAX_ID.set(Math.max(MAX_ID.get(), id)); } @@ -69,6 +78,10 @@ public class Terrain implements Tag { return weight; } + public float getHue() { + return id / (float) MAX_ID.get(); + } + @Override public String toString() { return getName(); @@ -177,4 +190,12 @@ public class Terrain implements Tag { public static Terrain volcanoPipe(Settings settings) { return new Terrain("volcano_pipe", 15, settings.terrain.volcano.weight); } + + public static Optional get(String name) { + return Optional.ofNullable(register.get(name)); + } + + public static List getRegistered() { + return new ArrayList<>(register.values()); + } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java index 414131b..4a85b32 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/TerraForgedMod.java @@ -26,20 +26,17 @@ package com.terraforged.mod; import com.terraforged.api.material.WGTags; -import com.terraforged.core.util.concurrent.ThreadPool; import com.terraforged.feature.FeatureManager; import com.terraforged.mod.data.DataGen; import com.terraforged.mod.feature.feature.DiskFeature; import com.terraforged.mod.feature.tree.SaplingManager; import com.terraforged.mod.util.Environment; import net.minecraft.world.gen.feature.Feature; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; -import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; /** * Author @@ -51,7 +48,6 @@ public class TerraForgedMod { @SubscribeEvent public static void setup(FMLCommonSetupEvent event) { Log.info("Common setup"); - MinecraftForge.EVENT_BUS.addListener(TerraForgedMod::onShutdown); WGTags.init(); TerraWorld.init(); SaplingManager.init(); @@ -69,8 +65,4 @@ public class TerraForgedMod { FeatureManager.registerTemplates(event); event.getRegistry().register(DiskFeature.INSTANCE); } - - private static void onShutdown(FMLServerStoppingEvent event) { - ThreadPool.shutdownCurrent(); - } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java index 64a688b..c166475 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/FastChunk.java @@ -9,12 +9,17 @@ import net.minecraft.world.chunk.ChunkPrimer; import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.IChunk; import net.minecraft.world.gen.Heightmap; +import org.apache.commons.lang3.Validate; /** * A ChunkPrimer wrapper that handles setting BlockStates within the chunk & updating heightmaps accordingly */ public class FastChunk implements ChunkDelegate { + private static final int arraySize = 256; + private static final int bitsPerEntry = 9; + private static final long maxEntryValue = (1L << bitsPerEntry) - 1L; + private final int blockX; private final int blockZ; private final ChunkPrimer primer; @@ -66,9 +71,56 @@ public class FastChunk implements ChunkDelegate { if (chunk instanceof FastChunk) { return chunk; } - if (chunk instanceof ChunkPrimer) { + if (chunk.getClass() == ChunkPrimer.class) { return new FastChunk((ChunkPrimer) chunk); } return chunk; } + + public static void updateWGHeightmaps(IChunk chunk, BlockPos.Mutable pos) { + int topY = chunk.getTopFilledSegment() + 15; + long[] ocean = chunk.getHeightmap(Heightmap.Type.OCEAN_FLOOR_WG).getDataArray(); + long[] surface = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE_WG).getDataArray(); + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + boolean flag = false; + for (int y = topY; y >= 0; y--) { + BlockState state = chunk.getBlockState(pos.setPos(x, y, z)); + if (state.getBlock() != Blocks.AIR) { + if (!flag) { + // WORLD_SURFACE_WG predicate is when block != air .: can set the height at this y value + setAt(surface, index(x, z), y + 1); + // flag = true means subsequent y values are ignored + flag = true; + } + if (Heightmap.Type.OCEAN_FLOOR_WG.getHeightLimitPredicate().test(state)) { + setAt(ocean, index(x, z), y + 1); + // no more heightmaps to update at this x/z so can exit the y loop here + break; + } + } + } + } + } + } + + // from BitArray + private static void setAt(long[] longArray, int index, int value) { + Validate.inclusiveBetween(0L, (arraySize - 1), index); + Validate.inclusiveBetween(0L, maxEntryValue, value); + int i = index * bitsPerEntry; + int j = i >> 6; + int k = (index + 1) * bitsPerEntry - 1 >> 6; + int l = i ^ j << 6; + longArray[j] = longArray[j] & ~(maxEntryValue << l) | ((long) value & maxEntryValue) << l; + if (j != k) { + int i1 = 64 - l; + int j1 = bitsPerEntry - i1; + longArray[k] = longArray[k] >>> j1 << j1 | ((long) value & maxEntryValue) >> i1; + } + } + + private static int index(int x, int z) { + return x + (z << 4); + } } diff --git a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java index 6f1efad..e198017 100644 --- a/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java +++ b/TerraForgedMod/src/main/java/com/terraforged/mod/chunk/TerraChunkGenerator.java @@ -187,6 +187,8 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator { - private final List terrains = getTerrains(); + private final List terrains = Terrain.getRegistered(); @Override public Terrain parse(StringReader reader) throws CommandSyntaxException { @@ -79,39 +76,4 @@ public class TerrainArgType implements ArgumentType { new StringTextComponent(String.format(message, args)) ); } - - private static List getTerrains() { - Terrains terrains = Terrains.create(new Settings()); - List result = new ArrayList<>(); - - for (int i = 0; i < terrains.index.size(); i++) { - Terrain terrain = terrains.index.get(i); - result.add(terrain); - if (dontMix(terrain, terrains)) { - continue; - } - for (int j = i + 1; j < terrains.index.size(); j++) { - Terrain other = terrains.index.get(j); - if (dontMix(other, terrains)) { - continue; - } - Terrain mix = new Terrain(terrain.getName() + "-" + other.getName(), -1); - result.add(mix); - } - } - - return result; - } - - private static boolean dontMix(Terrain terrain, Terrains terrains) { - return terrain == terrains.ocean - || terrain == terrains.deepOcean - || terrain == terrains.river - || terrain == terrains.riverBanks - || terrain == terrains.beach - || terrain == terrains.coast - || terrain == terrains.volcano - || terrain == terrains.volcanoPipe - || terrain == terrains.lake; - } }