This commit is contained in:
cyberpwn 2022-09-06 23:26:24 -04:00
parent 4f53b058fa
commit 5d6224db08
18 changed files with 322 additions and 63 deletions

View File

@ -1,6 +1,7 @@
package com.volmit.iris.platform.bukkit;
import art.arcane.amulet.io.IO;
import art.arcane.amulet.logging.LogListener;
import com.volmit.iris.engine.EngineConfiguration;
import com.volmit.iris.platform.IrisPlatform;
import com.volmit.iris.platform.PlatformBiome;
@ -14,10 +15,13 @@ import com.volmit.iris.platform.bukkit.wrapper.BukkitWorld;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Warning;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.block.Biome;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.java.JavaPlugin;
@ -29,11 +33,16 @@ import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Stream;
public class IrisBukkit extends JavaPlugin implements IrisPlatform {
public class IrisBukkit extends JavaPlugin implements IrisPlatform, LogListener {
private static final String TAG = ChatColor.DARK_GRAY + "[" + ChatColor.GREEN + "Iris" + ChatColor.DARK_GREEN + "/";
private static final String TAG_STRIPPED = ChatColor.stripColor(TAG);
private static IrisBukkit instance;
private ConsoleCommandSender consoleSender;
public void onEnable() {
instance = this;
consoleSender = Bukkit.getConsoleSender();
LogListener.listener.set(this);
getServer().getScheduler().scheduleSyncDelayedTask(this, () -> {
World world = Bukkit.createWorld(new WorldCreator("iristests/" + UUID.randomUUID()).generator(new IrisBukkitChunkGenerator(this, EngineConfiguration.builder()
@ -84,6 +93,36 @@ public class IrisBukkit extends JavaPlugin implements IrisPlatform {
return instance;
}
private void messageConsole(String color, String key, Object o) {
try {
consoleSender.sendMessage(TAG + key + ChatColor.DARK_GRAY + "]: " + color + o.toString());
}
catch(Throwable e) {
System.out.println(TAG_STRIPPED + key + "]: " + o.toString());
}
}
@Override
public void logError(String k, Object o) {
messageConsole(ChatColor.RED.toString(), k, o);
}
@Override
public void logInfo(String k, Object o) {
messageConsole(ChatColor.WHITE.toString(), k, o);
}
@Override
public void logWarning(String k, Object o) {
messageConsole(ChatColor.YELLOW.toString(), k, o);
}
@Override
public void logDebug(String k, Object o) {
messageConsole(ChatColor.GRAY.toString(), k, o);
}
@Override
public String getPlatformName() {
return "Bukkit";
@ -155,4 +194,24 @@ public class IrisBukkit extends JavaPlugin implements IrisPlatform {
.timings(true)
.build());
}
@Override
public void i(String s, Object o) {
logInfo(s, o);
}
@Override
public void f(String s, Object o) {
logError(s, o);
}
@Override
public void w(String s, Object o) {
logWarning(s, o);
}
@Override
public void d(String s, Object o) {
logDebug(s, o);
}
}

View File

@ -10,11 +10,15 @@ import com.volmit.iris.engine.feature.FeatureTarget;
import com.volmit.iris.engine.pipeline.PipedHunkStack;
import com.volmit.iris.platform.IrisPlatform;
import com.volmit.iris.platform.block.PlatformBlock;
import com.volmit.iris.platform.bukkit.util.StaticBiomeProvider;
import com.volmit.iris.platform.bukkit.wrapper.BukkitWorld;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
import com.volmit.iris.platform.bukkit.util.ChunkDataHunkView;
import org.bukkit.generator.WorldInfo;
import java.io.Closeable;
import java.io.IOException;
@ -31,13 +35,15 @@ public class IrisBukkitChunkGenerator extends ChunkGenerator implements Closeabl
private final AtomicInteger perSecond;
private final PrecisionStopwatch p = PrecisionStopwatch.start();
private final Average a = new Average(128);
private final StaticBiomeProvider staticBiomeProvider;
public IrisBukkitChunkGenerator(IrisPlatform platform, EngineConfiguration configuration) {
this.perSecond = new AtomicInteger(0);
this.platform = platform;
this.configuration = configuration;
engine = new AtomicReference<>();
engineLock = new ReentrantLock();
this.staticBiomeProvider = new StaticBiomeProvider(Biome.PLAINS);
this.engine = new AtomicReference<>();
this.engineLock = new ReentrantLock();
}
@Override
@ -94,8 +100,48 @@ public class IrisBukkitChunkGenerator extends ChunkGenerator implements Closeabl
return false;
}
@Override
public BiomeProvider getDefaultBiomeProvider(WorldInfo worldInfo) {
return staticBiomeProvider;
}
@Override
public void close() throws IOException {
engine.get().close();
}
@Override
public boolean shouldGenerateNoise() {
return super.shouldGenerateNoise();
}
@Override
public boolean shouldGenerateSurface() {
return super.shouldGenerateSurface();
}
@Override
public boolean shouldGenerateBedrock() {
return super.shouldGenerateBedrock();
}
@Override
public boolean shouldGenerateCaves() {
return super.shouldGenerateCaves();
}
@Override
public boolean shouldGenerateDecorations() {
return super.shouldGenerateDecorations();
}
@Override
public boolean shouldGenerateMobs() {
return super.shouldGenerateMobs();
}
@Override
public boolean shouldGenerateStructures() {
return super.shouldGenerateStructures();
}
}

View File

@ -0,0 +1,27 @@
package com.volmit.iris.platform.bukkit.util;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.WorldInfo;
import java.util.Arrays;
import java.util.List;
public class StaticBiomeProvider extends BiomeProvider {
private static final List<Biome> ALL_BIOMES = Arrays.stream(Biome.values()).without((i) -> i.equals(Biome.CUSTOM)).toList().unmodifiable();
private final Biome defaultBiome;
public StaticBiomeProvider(Biome defaultBiome) {
this.defaultBiome = defaultBiome;
}
@Override
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
return defaultBiome;
}
@Override
public List<Biome> getBiomes(WorldInfo worldInfo) {
return ALL_BIOMES;
}
}

View File

@ -1,5 +1,6 @@
package com.volmit.iris.engine;
import com.volmit.iris.engine.feature.features.FeatureError;
import com.volmit.iris.engine.feature.features.FeatureTerrain;
import com.volmit.iris.engine.pipeline.EnginePipeline;
import com.volmit.iris.engine.pipeline.EnginePlumbing;
@ -51,6 +52,11 @@ public class Engine implements Closeable {
this.seedManager = getSeedManager();
this.executor = new EngineExecutor(this);
this.plumbing = EnginePlumbing.builder().engine(this)
.errorPipeline(EnginePipeline.builder()
.phase(PipelinePhase.builder()
.task(new PipelineTask<>(new FeatureError(this), PlatformBlock.class))
.build())
.build())
.pipeline(EnginePipeline.builder()
.phase(PipelinePhase.builder()
.task(new PipelineTask<>(new FeatureTerrain(this), PlatformBlock.class))
@ -59,18 +65,15 @@ public class Engine implements Closeable {
.build();
}
public PlatformBlock block(String block)
{
public PlatformBlock block(String block) {
return blockCache.get(block);
}
public PlatformNamespaceKey key(String nsk)
{
public PlatformNamespaceKey key(String nsk) {
return getPlatform().key(nsk);
}
public static Optional<Engine> context()
{
public static Optional<Engine> context() {
WeakReference<Engine> reference = engineContext.get(Thread.currentThread());
if(reference != null)

View File

@ -8,12 +8,14 @@ import lombok.Data;
public abstract class Feature<T extends PlatformNamespaced, S extends FeatureState> {
private final String name;
private final Engine engine;
private boolean optimize;
private boolean heightAgnostic;
public Feature(String name, Engine engine)
{
this.engine = engine;
this.name = name;
this.optimize = true;
this.heightAgnostic = true;
}

View File

@ -45,13 +45,13 @@ public class FeatureSizedTarget {
if(width <= 1) {
return Stream.of(this);
}
int wo2 = width/2;
return Stream.of(FeatureSizedTarget.builder()
.width(width/2).height(height).depth(depth)
.width(wo2).height(height).depth(depth)
.offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(),
FeatureSizedTarget.builder()
.width(width - (width/2)).height(height).depth(depth)
.offsetX(offsetX + (width/2)).offsetY(offsetY).offsetZ(offsetZ).build());
.width(width - wo2).height(height).depth(depth)
.offsetX(offsetX + wo2).offsetY(offsetY).offsetZ(offsetZ).build());
}
Stream<FeatureSizedTarget> splitY() {
@ -59,12 +59,13 @@ public class FeatureSizedTarget {
return Stream.of(this);
}
int ho2 = height / 2;
return Stream.of(FeatureSizedTarget.builder()
.width(width).height(height/2).depth(depth)
.width(width).height(ho2).depth(depth)
.offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(),
FeatureSizedTarget.builder()
.width(width).height(height - (height / 2)).depth(depth)
.offsetX(offsetX).offsetY(offsetY + (height/2)).offsetZ(offsetZ).build());
.width(width).height(height - ho2).depth(depth)
.offsetX(offsetX).offsetY(offsetY + ho2).offsetZ(offsetZ).build());
}
Stream<FeatureSizedTarget> splitZ() {
@ -72,12 +73,13 @@ public class FeatureSizedTarget {
return Stream.of(this);
}
int do2 = depth / 2;
return Stream.of(FeatureSizedTarget.builder()
.width(width).height(height).depth(depth/2)
.width(width).height(height).depth(do2)
.offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(),
FeatureSizedTarget.builder()
.width(width).height(height).depth(depth - (depth/2))
.offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ + (depth/2)).build());
.width(width).height(height).depth(depth - do2)
.offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ + do2).build());
}
public int getAbsoluteMaxX()

View File

@ -34,13 +34,11 @@ public class FeatureTask<T extends PlatformNamespaced, S extends FeatureState> e
FeatureTarget<T> result;
PrecisionStopwatch p = null;
if(timings != null)
{
if(timings != null) {
p = PrecisionStopwatch.start();
}
if(!heightAgnostic && size.getHeight() > verticalPrepareSize * 2) {
result = FeatureTarget.mergedTarget(size.splitY()
.map(i -> engine.getExecutor().getForks().submit((ForkJoinTask<FeatureTarget<T>>) with(i)))
.map(ForkJoinTask::join), origin, false, true, false);
@ -61,8 +59,7 @@ public class FeatureTask<T extends PlatformNamespaced, S extends FeatureState> e
result = preparedFeature.generate(origin, storage);
}
if(timings != null)
{
if(timings != null) {
timings.onCompleted(p.getMilliseconds());
}

View File

@ -0,0 +1,38 @@
package com.volmit.iris.engine.feature.features;
import com.volmit.iris.engine.Engine;
import com.volmit.iris.engine.feature.Feature;
import com.volmit.iris.engine.feature.FeatureSizedTarget;
import com.volmit.iris.engine.feature.FeatureState;
import com.volmit.iris.engine.feature.FeatureStorage;
import com.volmit.iris.engine.feature.FeatureTarget;
import com.volmit.iris.platform.block.PlatformBlock;
import lombok.AllArgsConstructor;
import lombok.Data;
public class FeatureError extends Feature<PlatformBlock, FeatureError.State> {
private static final State DEFAULT_STATE = new State();
private final PlatformBlock ERROR_BLOCK;
public FeatureError(Engine engine) {
super("error", engine);
setOptimize(false);
ERROR_BLOCK = engine.block("red_sandstone");
}
@Override
public State prepare(Engine engine, FeatureSizedTarget target, FeatureStorage storage) {
return DEFAULT_STATE;
}
@Override
public void generate(Engine engine, State state, FeatureTarget<PlatformBlock> target, FeatureStorage storage) {
target.forXZ((x, z) -> target.forYCap((y -> target.getHunk().set(x, y, z, ERROR_BLOCK)), 1));
}
@Data
@AllArgsConstructor
public static class State implements FeatureState {
}
}

View File

@ -1,40 +1,73 @@
package com.volmit.iris.engine.feature.features;
import art.arcane.source.NoisePlane;
import art.arcane.source.interpolator.CubicInterpolator;
import art.arcane.source.interpolator.Interpolator;
import art.arcane.source.interpolator.LinearInterpolator;
import art.arcane.source.interpolator.StarcastInterpolator;
import art.arcane.source.noise.provider.MirroredCacheProvider;
import art.arcane.source.util.NoisePreset;
import art.arcane.spatial.hunk.Hunk;
import art.arcane.spatial.hunk.storage.ArrayHunk;
import com.volmit.iris.engine.Engine;
import com.volmit.iris.engine.feature.*;
import com.volmit.iris.platform.block.PlatformBlock;
import com.volmit.iris.util.ShortNoiseCache;
import lombok.AllArgsConstructor;
import lombok.Data;
public class FeatureTerrain extends Feature<PlatformBlock, FeatureTerrain.State> {
private final PlatformBlock stone;
private final NoisePlane generator;
private final NoisePlane generator2;
public FeatureTerrain(Engine engine) {
super("terrain", engine);
setOptimize(true);
stone = engine.block("stone");
this.generator = NoisePreset.NATURAL.create(1234).fit(0, 64).scale(0.2);
this.generator = NoisePreset.NATURAL.create(1234);
this.generator2 = NoisePreset.NATURAL.create(6664).fit(0, 1).scale(0.1);
}
@Override
public State prepare(Engine engine, FeatureSizedTarget target, FeatureStorage storage) {
target.forXZ((x, z) -> storage.getHeight().set(x & storage.getW() - 1, z & storage.getH() - 1, (short) generator.noise(x, z)));
return new State(storage.getHeight());
Hunk<Double> snc = new ArrayHunk<>(target.getWidth(), target.getHeight(), target.getDepth());
short n;
int fdx,fdz;
for(int x : target.x()) {
fdx = Math.floorMod(x, target.getWidth());
for(int z : target.z()) {
fdz = Math.floorMod(z, target.getDepth());
n = (short) generator.noise(x, z);
for(int y = 0; y < n; y++) {
if(generator2.noise(x,y,z) > 0.5) {
snc.set(fdx, y, fdz, 1D);
}
}
}
}
return new State(snc);
}
@Override
public void generate(Engine engine, State state, FeatureTarget<PlatformBlock> target, FeatureStorage storage) {
target.forXZ((x, z) -> target.forYCap((y -> {
target.getHunk().set(x, y, z, stone);
}), state.getNoise().get(x, z)));
int y;
for(int x : target.localX()) {
for(int z : target.localZ()) {
for(int i = 0; i < target.getHeight(); i++) {
Double v = state.getNoise().get(x, i, z);
v = v == null ? 0f : v;
if(v >= 0.5) {
target.getHunk().set(x,i, z, stone);
}
}
}
}
}
@Data
@AllArgsConstructor
public static class State implements FeatureState {
private final ShortNoiseCache noise;
private final Hunk<Double> noise;
}
}

View File

@ -13,6 +13,11 @@ public class HunkSlizeConfiguration {
private final int verticalSlice;
private final int horizontalSlize;
public String toString()
{
return "H" + horizontalSlize + "" + ((verticalSlice < 8192) ? ("V" + verticalSlice) : "");
}
public static List<HunkSlizeConfiguration> generateConfigurations(IntegerRange vertical, IntegerRange horizontal)
{
List<HunkSlizeConfiguration> configurations = new ArrayList<>();

View File

@ -1,13 +1,14 @@
package com.volmit.iris.engine.optimizer;
import art.arcane.chrono.Average;
import art.arcane.spatial.hunk.storage.AtomicDoubleHunk;
import lombok.Data;
import java.util.concurrent.atomic.AtomicInteger;
@Data
public class IrisOptimizationAttempt<T> {
private final Average average;
private double value;
private final AtomicInteger runs;
private final int testRuns;
private final T parameters;
@ -16,13 +17,13 @@ public class IrisOptimizationAttempt<T> {
{
this.parameters = parameters;
this.testRuns = testRuns;
this.average = new Average(testRuns);
this.value = 0;
this.runs = new AtomicInteger(0);
}
public double getAverageTime()
{
return average.getAverage();
return value;
}
public boolean isComplete()
@ -31,7 +32,7 @@ public class IrisOptimizationAttempt<T> {
}
public void report(double ms) {
average.put(ms);
value += ms;
runs.incrementAndGet();
}
}

View File

@ -1,5 +1,7 @@
package com.volmit.iris.engine.optimizer;
import art.arcane.amulet.format.Form;
import art.arcane.chrono.Average;
import lombok.Builder;
import lombok.Data;
import lombok.Singular;
@ -12,58 +14,72 @@ import java.util.concurrent.ConcurrentHashMap;
@Data
public class IrisOptimizer<T> {
private final String optimizedFeatureName;
private final int testRuns;
private int dummyRuns;
private final List<T> options;
private final Map<T, Double> results;
private final Map<T, IrisOptimizationAttempt<T>> attempts = new ConcurrentHashMap<>();
private T defaultOption;
private final double chanceToTest;
private double bestTime;
public IrisOptimizer(int testRuns, List<T> options, T defaultOption, double chanceToTest)
{
public IrisOptimizer(int testRuns, List<T> options, T defaultOption, double chanceToTest, String optimizedFeatureName) {
this.bestTime = Double.MAX_VALUE;
this.dummyRuns = 1024;
this.testRuns = testRuns;
this.results = new HashMap<>();
this.options = options;
this.optimizedFeatureName = optimizedFeatureName;
this.defaultOption = defaultOption;
this.chanceToTest = chanceToTest;
for(T i : options)
{
for(T i : options) {
attempts.put(i, new IrisOptimizationAttempt<>(i, testRuns));
}
}
public String toString()
{
public String toString() {
return "optimizer";
}
public synchronized void report(T parameters, double ms)
{
if(dummyRuns-- > 0)
{
return;
}
IrisOptimizationAttempt<T> attempt = attempts.get(parameters);
if(attempt != null)
{
if(attempt != null) {
attempt.report(ms);
if(attempt.isComplete())
{
if(attempt.isComplete()) {
results.put(parameters, attempt.getAverageTime());
attempts.remove(parameters);
double result = attempt.getAverageTime();
if(result < bestTime)
{
if(result < bestTime) {
bestTime = result;
defaultOption = attempt.getParameters();
}
d("Attempted " + optimizedFeatureName + " with " + defaultOption.toString() + " " + Form.duration(attempt.getAverageTime(), 2));
if(attempts.isEmpty()) {
d("Fully Optimized " + optimizedFeatureName + " with " + defaultOption.toString());
for(T i : results.keySet()) {
d(i.toString() + ": " + Form.duration(results.get(i), 2));
}
}
}
}
}
public T nextParameters()
{
if(!attempts.isEmpty() && Math.r(chanceToTest))
{
public T nextParameters() {
if(!attempts.isEmpty() && Math.r(chanceToTest)) {
return attempts.k().popRandom();
}

View File

@ -16,12 +16,18 @@ public class EnginePlumbing {
private final Engine engine;
@Singular
private final List<EnginePipeline> pipelines;
private final EnginePipeline errorPipeline;
public void generate(Engine engine, FeatureSizedTarget target, PipedHunkStack stack)
{
for(EnginePipeline i : pipelines)
{
public void generate(Engine engine, FeatureSizedTarget target, PipedHunkStack stack) {
for(EnginePipeline i : pipelines) {
i.generate(engine, target, stack);
}
try {
}
catch(Throwable e) {
e.printStackTrace();
getErrorPipeline().generate(engine, target, stack);
}
}
}

View File

@ -26,7 +26,7 @@ public class PipelinePhase
.collect(Collectors.toList())).stream().map(i -> {
try {
return i.get();
} catch(InterruptedException | ExecutionException e) {
} catch(Throwable e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());

View File

@ -30,16 +30,15 @@ public class PipelineTask<T extends PlatformNamespaced>
this.horizontalEnvelope = horizontalEnvelope;
List<HunkSlizeConfiguration> configurations = feature.isHeightAgnostic() ? HunkSlizeConfiguration.generateConfigurations(Integer.MAX_VALUE, horizontalEnvelope)
: HunkSlizeConfiguration.generateConfigurations(verticalEnvelope, horizontalEnvelope);
this.optimizer = new IrisOptimizer<>(256, configurations, configurations[0], 0.75);
this.optimizer = new IrisOptimizer<>(128, configurations, configurations[0], 1, feature.getName());
}
public PipelineTask(Feature<T, ?> feature, Class<T> target)
{
public PipelineTask(Feature<T, ?> feature, Class<T> target) {
this(feature, target, 1 to 16, 1 to 16);
}
public FeatureTask<T, ?> task(FeatureSizedTarget target, FeatureTarget<T> origin, FeatureStorage storage){
HunkSlizeConfiguration configuration = optimizer.nextParameters();
HunkSlizeConfiguration configuration = getFeature().isOptimize() ? optimizer.nextParameters() : optimizer.getDefaultOption();
return feature.task(target, origin, storage, configuration.getVerticalSlice(), configuration.getHorizontalSlize(), (ms) -> optimizer.report(configuration, ms));
}
}

View File

@ -6,6 +6,14 @@ import java.io.File;
import java.util.stream.Stream;
public interface IrisPlatform {
void logError(String k, Object o);
void logInfo(String k, Object o);
void logWarning(String k, Object o);
void logDebug(String k, Object o);
String getPlatformName();
Stream<PlatformBlock> getBlocks();

View File

@ -0,0 +1,17 @@
package com.volmit.iris.util;
import art.arcane.source.NoisePlane;
import art.arcane.spatial.hunk.Hunk;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class HunkedNoisePlane implements NoisePlane {
private final Hunk<Double> noise;
@Override
public double noise(double x, double y, double z) {
Double d = noise.get(Math.floorMod((int)x, noise.getWidth()), Math.floorMod((int)y, noise.getHeight()), Math.floorMod((int)z, noise.getDepth()));
return d != null ? d : 0;
}
}

View File

@ -13,10 +13,10 @@ public class ShortNoiseCache {
}
public void set(int x, int y, short v) {
this.cache[y % this.height * this.width + x % this.width] = v;
this.cache[(Math.floorMod(y,this.height) * this.width) + Math.floorMod(x, this.width)] = v;
}
public short get(int x, int y) {
return this.cache[y % this.height * this.width + x % this.width];
return this.cache[(Math.floorMod(y,this.height) * this.width) + Math.floorMod(x,this.width)];
}
}