- ignore 3rd party ChunkPrimer candidates for the fast chunk wrapper

- some tweaks to terrains stuff
This commit is contained in:
dags- 2020-03-20 23:32:17 +00:00
parent f0d8cf3a76
commit 5142264bcf
13 changed files with 149 additions and 100 deletions

View File

@ -91,7 +91,7 @@ public class Main extends Applet {
if (cell.tag == getCache().getTerrain().volcano) { if (cell.tag == getCache().getTerrain().volcano) {
return 0F; return 0F;
} }
return 20 + (cell.tag.getId() / (float) Terrain.MAX_ID.get()) * 80; return 20 + (cell.tag.getHue() * 80);
case ELEVATION: case ELEVATION:
float value = (cell.value - 0.245F) / 0.65F; float value = (cell.value - 0.245F) / 0.65F;
return (1 - value) * 30; return (1 - value) * 30;

View File

@ -26,6 +26,7 @@
package com.terraforged.core.settings; package com.terraforged.core.settings;
import com.terraforged.core.util.serialization.annotation.Comment; 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.Range;
import com.terraforged.core.util.serialization.annotation.Serializable; import com.terraforged.core.util.serialization.annotation.Serializable;
import me.dags.noise.Module; import me.dags.noise.Module;
@ -77,6 +78,7 @@ public class GeneratorSettings {
public int continentScale = 4000; public int continentScale = 4000;
@Range(min = 250, max = 5000) @Range(min = 250, max = 5000)
@Name("Mountain Range Scale")
@Comment("Controls the size of mountain ranges") @Comment("Controls the size of mountain ranges")
public int mountainScale = 950; public int mountainScale = 950;

View File

@ -30,34 +30,30 @@ import com.terraforged.core.util.concurrent.batcher.Batcher;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future; import java.util.concurrent.ForkJoinTask;
public class ThreadPool implements Executor { 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) { private ThreadPool(String name, int size) {
this.service = ThreadPool.createPool(size, name); this.service = ThreadPool.createPool(size, name);
} }
public void shutdown() {
service.shutdown();
}
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {
service.execute(command); service.submit(command);
} }
public <T> Future<?> submit(Runnable runnable) { public <T> ForkJoinTask<?> submit(Runnable runnable) {
return service.submit(runnable); return service.submit(runnable);
} }
public <T> Future<T> submit(Callable<T> callable) { public <T> ForkJoinTask<T> submit(Callable<T> callable) {
return service.submit(callable); return service.submit(callable);
} }
@ -73,16 +69,13 @@ public class ThreadPool implements Executor {
return new ThreadPool("Pool", Math.max(1, size)); return new ThreadPool("Pool", Math.max(1, size));
} }
public static void shutdownCurrent() {
instance.shutdown();
}
private static int defaultPoolSize() { private static int defaultPoolSize() {
int threads = Runtime.getRuntime().availableProcessors(); int threads = Runtime.getRuntime().availableProcessors();
return Math.max(1, (int) ((threads / 3F) * 2F)); return Math.max(1, (int) ((threads / 3F) * 2F));
} }
public static ForkJoinPool createPool(int size, String name) { public static ForkJoinPool createPool(int size, String name) {
return new ForkJoinPool(size, new WorkerFactory.ForkJoin(name), null, true); return new ForkJoinPool(size, new WorkerFactory.ForkJoin(name), null, true);
} }
} }

View File

@ -10,6 +10,7 @@ public class Cache<V extends ExpiringEntry> implements Runnable {
private final long expireMS; private final long expireMS;
private final long intervalMS; private final long intervalMS;
private final SynchronizedLongMap<V> map; private final SynchronizedLongMap<V> map;
private final ThreadPool threadPool = ThreadPool.getPool();
private volatile long timestamp = 0L; private volatile long timestamp = 0L;
@ -33,7 +34,7 @@ public class Cache<V extends ExpiringEntry> implements Runnable {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - timestamp > intervalMS) { if (now - timestamp > intervalMS) {
timestamp = now; timestamp = now;
ThreadPool.getPool().execute(this); threadPool.submit(this);
} }
} }

View File

@ -1,17 +1,17 @@
package com.terraforged.core.util.concurrent.cache; package com.terraforged.core.util.concurrent.cache;
import java.util.concurrent.Executor; import com.terraforged.core.util.concurrent.ThreadPool;
import java.util.function.Supplier;
public class CacheEntry<T> implements Runnable, ExpiringEntry { import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinTask;
public class CacheEntry<T> implements ExpiringEntry {
private volatile T result;
private volatile long timestamp; private volatile long timestamp;
private final ForkJoinTask<T> task;
private final Supplier<T> supplier; public CacheEntry(ForkJoinTask<T> task) {
this.task = task;
private CacheEntry(Supplier<T> supplier) {
this.supplier = supplier;
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
} }
@ -20,33 +20,15 @@ public class CacheEntry<T> implements Runnable, ExpiringEntry {
return timestamp; return timestamp;
} }
@Override
public void run() {
this.result = supplier.get();
this.timestamp = System.currentTimeMillis();
}
public boolean isDone() { public boolean isDone() {
return result != null; return task.isDone();
} }
public T get() { public T get() {
T value = result; return task.join();
if (value == null) {
value = getNow();
}
return value;
} }
private T getNow() { public static <T> CacheEntry<T> supplyAsync(Callable<T> callable, ThreadPool executor) {
T result = supplier.get(); return new CacheEntry<>(executor.submit(callable));
this.result = result;
return result;
}
public static <T> CacheEntry<T> supplyAsync(Supplier<T> supplier, Executor executor) {
CacheEntry<T> entry = new CacheEntry<>(supplier);
executor.execute(entry);
return entry;
} }
} }

View File

@ -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();
}

View File

@ -26,6 +26,7 @@
package com.terraforged.core.util.serialization.serializer; package com.terraforged.core.util.serialization.serializer;
import com.terraforged.core.util.serialization.annotation.Comment; 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.Range;
import com.terraforged.core.util.serialization.annotation.Serializable; import com.terraforged.core.util.serialization.annotation.Serializable;
@ -163,7 +164,9 @@ public class Serializer {
} }
private static String getName(Field field) { 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); StringBuilder sb = new StringBuilder(name.length() * 2);
for (int i = 0; i < name.length(); i++) { for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i); char c = name.charAt(i);

View File

@ -48,6 +48,7 @@ public class RiverMap {
private final GeneratorContext context; private final GeneratorContext context;
private final RiverMapConfig riverMapConfig; private final RiverMapConfig riverMapConfig;
private final Cache<CacheEntry<RiverRegion>> cache; private final Cache<CacheEntry<RiverRegion>> cache;
private final ThreadPool threadPool = ThreadPool.getPool();
public RiverMap(Heightmap heightmap, GeneratorContext context) { public RiverMap(Heightmap heightmap, GeneratorContext context) {
RiverConfig primary = RiverConfig.builder(context.levels) RiverConfig primary = RiverConfig.builder(context.levels)
@ -151,6 +152,6 @@ public class RiverMap {
} }
private CacheEntry<RiverRegion> generateRegion(int rx, int rz) { private CacheEntry<RiverRegion> 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);
} }
} }

View File

@ -29,17 +29,25 @@ import com.terraforged.core.cell.Tag;
import com.terraforged.core.settings.Settings; import com.terraforged.core.settings.Settings;
import com.terraforged.core.world.heightmap.Levels; 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; import java.util.concurrent.atomic.AtomicInteger;
public class Terrain implements Tag { public class Terrain implements Tag {
public static final int ID_START = 9; private static final AtomicInteger MAX_ID = new AtomicInteger(0);
public static final AtomicInteger MAX_ID = new AtomicInteger(0); private static final Map<String, Terrain> register = Collections.synchronizedMap(new HashMap<>());
public static final int ID_START = 10;
public static final Terrain NONE = new Terrain("none", -1); public static final Terrain NONE = new Terrain("none", -1);
private final String name; private final String name;
private final int id; private final int id;
private float weight; private final float weight;
public Terrain(String name, int id) { public Terrain(String name, int id) {
this(name, id, 1F); this(name, id, 1F);
@ -49,6 +57,7 @@ public class Terrain implements Tag {
this.name = name; this.name = name;
this.id = id; this.id = id;
this.weight = (float) weight; this.weight = (float) weight;
register.put(name, this);
MAX_ID.set(Math.max(MAX_ID.get(), id)); MAX_ID.set(Math.max(MAX_ID.get(), id));
} }
@ -69,6 +78,10 @@ public class Terrain implements Tag {
return weight; return weight;
} }
public float getHue() {
return id / (float) MAX_ID.get();
}
@Override @Override
public String toString() { public String toString() {
return getName(); return getName();
@ -177,4 +190,12 @@ public class Terrain implements Tag {
public static Terrain volcanoPipe(Settings settings) { public static Terrain volcanoPipe(Settings settings) {
return new Terrain("volcano_pipe", 15, settings.terrain.volcano.weight); return new Terrain("volcano_pipe", 15, settings.terrain.volcano.weight);
} }
public static Optional<Terrain> get(String name) {
return Optional.ofNullable(register.get(name));
}
public static List<Terrain> getRegistered() {
return new ArrayList<>(register.values());
}
} }

View File

@ -26,20 +26,17 @@
package com.terraforged.mod; package com.terraforged.mod;
import com.terraforged.api.material.WGTags; import com.terraforged.api.material.WGTags;
import com.terraforged.core.util.concurrent.ThreadPool;
import com.terraforged.feature.FeatureManager; import com.terraforged.feature.FeatureManager;
import com.terraforged.mod.data.DataGen; import com.terraforged.mod.data.DataGen;
import com.terraforged.mod.feature.feature.DiskFeature; import com.terraforged.mod.feature.feature.DiskFeature;
import com.terraforged.mod.feature.tree.SaplingManager; import com.terraforged.mod.feature.tree.SaplingManager;
import com.terraforged.mod.util.Environment; import com.terraforged.mod.util.Environment;
import net.minecraft.world.gen.feature.Feature; import net.minecraft.world.gen.feature.Feature;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
/** /**
* Author <dags@dags.me> * Author <dags@dags.me>
@ -51,7 +48,6 @@ public class TerraForgedMod {
@SubscribeEvent @SubscribeEvent
public static void setup(FMLCommonSetupEvent event) { public static void setup(FMLCommonSetupEvent event) {
Log.info("Common setup"); Log.info("Common setup");
MinecraftForge.EVENT_BUS.addListener(TerraForgedMod::onShutdown);
WGTags.init(); WGTags.init();
TerraWorld.init(); TerraWorld.init();
SaplingManager.init(); SaplingManager.init();
@ -69,8 +65,4 @@ public class TerraForgedMod {
FeatureManager.registerTemplates(event); FeatureManager.registerTemplates(event);
event.getRegistry().register(DiskFeature.INSTANCE); event.getRegistry().register(DiskFeature.INSTANCE);
} }
private static void onShutdown(FMLServerStoppingEvent event) {
ThreadPool.shutdownCurrent();
}
} }

View File

@ -9,12 +9,17 @@ import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk; import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap; 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 * A ChunkPrimer wrapper that handles setting BlockStates within the chunk & updating heightmaps accordingly
*/ */
public class FastChunk implements ChunkDelegate { 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 blockX;
private final int blockZ; private final int blockZ;
private final ChunkPrimer primer; private final ChunkPrimer primer;
@ -66,9 +71,56 @@ public class FastChunk implements ChunkDelegate {
if (chunk instanceof FastChunk) { if (chunk instanceof FastChunk) {
return chunk; return chunk;
} }
if (chunk instanceof ChunkPrimer) { if (chunk.getClass() == ChunkPrimer.class) {
return new FastChunk((ChunkPrimer) chunk); return new FastChunk((ChunkPrimer) chunk);
} }
return 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);
}
} }

View File

@ -187,6 +187,8 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
processor.decorate(ctx.buffer, ctx, px, py, pz); processor.decorate(ctx.buffer, ctx, px, py, pz);
} }
}); });
FastChunk.updateWGHeightmaps(chunk, context.pos);
} }
@Override @Override

View File

@ -32,20 +32,17 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.terraforged.core.settings.Settings;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
import com.terraforged.core.world.terrain.Terrains;
import net.minecraft.command.ISuggestionProvider; import net.minecraft.command.ISuggestionProvider;
import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.StringTextComponent;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class TerrainArgType implements ArgumentType<Terrain> { public class TerrainArgType implements ArgumentType<Terrain> {
private final List<Terrain> terrains = getTerrains(); private final List<Terrain> terrains = Terrain.getRegistered();
@Override @Override
public Terrain parse(StringReader reader) throws CommandSyntaxException { public Terrain parse(StringReader reader) throws CommandSyntaxException {
@ -79,39 +76,4 @@ public class TerrainArgType implements ArgumentType<Terrain> {
new StringTextComponent(String.format(message, args)) new StringTextComponent(String.format(message, args))
); );
} }
private static List<Terrain> getTerrains() {
Terrains terrains = Terrains.create(new Settings());
List<Terrain> 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;
}
} }