Compare commits

...

27 Commits

Author SHA1 Message Date
Zoe Gidiere 7ca61f82b2 Some API clean up 2024-10-12 00:22:18 -06:00
Zoe Gidiere 6851999926 Some refactoring 2024-10-11 19:45:12 -06:00
Zoe Gidiere 305255511d More cache improvements 2024-10-11 18:27:37 -06:00
Zoe Gidiere 819be16d83 Merge branch 'dev/cli' into dev/caching 2024-10-11 16:40:26 -06:00
Zoe Gidiere 8a028b193a Add no save option 2024-10-11 16:35:26 -06:00
Zoe Gidiere d31679e6be remove stuff from example 2024-10-11 16:06:40 -06:00
Zoe Gidiere 9d328b12b3 More cache improvements 2024-10-11 16:06:23 -06:00
Zoe Gidiere a743e9c015 Merge branch 'dev/cli' into dev/caching 2024-10-11 15:16:20 -06:00
Zoe Gidiere 43d52e4bc1 CLI Improvements 2024-10-11 15:16:04 -06:00
Zoe Gidiere accc07fa07 Bump Version 2024-10-11 15:15:38 -06:00
Zoe Gidiere c1d3155ddd Cache Improvements 2024-10-11 15:15:10 -06:00
Zoe Gidiere 9a01a6c6a0 more WIP on cache 2024-10-11 00:20:46 -06:00
Zoe Gidiere 1ae0d1f867 WIP Caching Hints and Caching Sampler 2024-10-09 23:36:12 -06:00
Zoe Gidiere 370b2e0122 Fix import 2024-10-09 13:01:54 -06:00
Zoe Gidiere 331075e54d Refractor name 2024-10-09 12:59:19 -06:00
Zoë Gidiere 973590f5fd Add parameter to turn off salting cellular lookup
another updated astrash commit
2024-10-09 12:42:24 -06:00
Astrash 1a5ab6b505 Add meta annotations 2024-10-09 12:42:13 -06:00
Astrash 14732328cd Add default 'from' values for linear map template 2024-10-09 12:41:55 -06:00
Zoë Gidiere 6c7974c302 Implement linear map normalizer
Updated astrash commit
2024-10-09 12:41:40 -06:00
Zoe Gidiere 1204b7a8c1 ArithmeticSampler Derivatives 2024-10-09 12:37:50 -06:00
Astrash fbe1c76e26 Forgot to include 4th derivative array index for 3D FBM 2024-10-09 21:50:08 +11:00
Astrash add7803e65 Simplify isDifferentiable static method impl 2024-10-09 20:56:37 +11:00
Astrash 4393a16b2f Use proper static isDifferentiable method 2024-10-09 20:56:11 +11:00
Astrash b4fa635455 Ensure throw with incorrect use of SimplexStyle derivatives
This should ensure that in the case an implementation fails to correctly
check isDifferentiable then performs a subsequent derivative based sample,
an error is thrown, rather than potentially missing a logic bug due to
noise of 0 and derivative of 0 being returned which could otherwise be
hard to narrow down.
2024-10-09 20:45:25 +11:00
Astrash 9f425c6159 Add support for derivatives in FBM 2024-10-09 20:39:45 +11:00
Astrash 158ffba2a5 Make FractalNoiseFunction impls support derivatives 2024-10-09 20:39:13 +11:00
Zoë Gidiere b7326c0ff6 Merge pull request #469 from PolyhedralDev/dev/fix-bukkit-spawner
fix spawners on bukkit
2024-09-24 23:27:44 -06:00
42 changed files with 699 additions and 124 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.5.0"))
versionProjects(":common:implementation", version("6.5.0"))
versionProjects(":platforms", version("6.5.0"))
versionProjects(":common:api", version("6.6.0"))
versionProjects(":common:implementation", version("6.6.0"))
versionProjects(":platforms", version("6.6.0"))
allprojects {
+1
View File
@@ -74,5 +74,6 @@ object Versions {
object CLI {
const val nbt = "6.1"
const val logback = "1.5.8"
const val picocli = "4.7.6"
}
}
@@ -1,5 +1,7 @@
package com.dfsek.terra.addons.biome.pipeline.v2;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
@@ -11,7 +13,6 @@ import java.util.stream.StreamSupport;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -23,7 +24,7 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class PipelineBiomeProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache;
private final LoadingCache<SeededVector2Key, BiomeChunk> biomeChunkCache;
private final int chunkSize;
private final int resolution;
private final NoiseSampler mutator;
@@ -90,7 +91,7 @@ public class PipelineBiomeProvider implements BiomeProvider {
int xInChunk = x - chunkWorldX;
int zInChunk = z - chunkWorldZ;
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome();
return biomeChunkCache.get(new SeededVector2Key(chunkWorldX, chunkWorldZ, seed)).get(xInChunk, zInChunk).getBiome();
}
@Override
@@ -1,10 +1,12 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import java.util.List;
public interface Pipeline {
BiomeChunk generateChunk(SeededVector worldCoordinates);
BiomeChunk generateChunk(SeededVector2Key worldCoordinates);
int getChunkSize();
@@ -1,19 +0,0 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
public record SeededVector(long seed, int x, int z) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
@@ -4,20 +4,20 @@ import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
public class BiomeChunkImpl implements BiomeChunk {
private final SeededVector worldOrigin;
private final SeededVector2Key worldOrigin;
private final int chunkOriginArrayIndex;
private final int worldCoordinateScale;
private final int size;
private PipelineBiome[] biomes;
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) {
public BiomeChunkImpl(SeededVector2Key worldOrigin, PipelineImpl pipeline) {
this.worldOrigin = worldOrigin;
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex();
@@ -44,7 +44,7 @@ public class BiomeChunkImpl implements BiomeChunk {
for(int gridZ = 0; gridZ < gridSize; gridZ++) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[(xIndex * size) + zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex),
biomes[(xIndex * size) + zIndex] = pipeline.getSource().get(worldOrigin.seed, xIndexToWorldCoordinate(xIndex),
zIndexToWorldCoordinate(zIndex));
}
}
@@ -139,14 +139,14 @@ public class BiomeChunkImpl implements BiomeChunk {
}
private int xIndexToWorldCoordinate(int xIndex) {
return (worldOrigin.x() + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
return (worldOrigin.x + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private int zIndexToWorldCoordinate(int zIndex) {
return (worldOrigin.z() + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
return (worldOrigin.z + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private SeededVector getOrigin() {
private SeededVector2Key getOrigin() {
return worldOrigin;
}
@@ -216,7 +216,7 @@ public class BiomeChunkImpl implements BiomeChunk {
}
public long worldSeed() {
return chunk.getOrigin().seed();
return chunk.getOrigin().seed;
}
}
}
@@ -1,5 +1,7 @@
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -8,7 +10,6 @@ import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
@@ -55,7 +56,7 @@ public class PipelineImpl implements Pipeline {
}
@Override
public BiomeChunk generateChunk(SeededVector worldCoordinates) {
public BiomeChunk generateChunk(SeededVector2Key worldCoordinates) {
return new BiomeChunkImpl(worldCoordinates, this);
}
@@ -17,6 +17,7 @@ import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
import com.dfsek.terra.addons.noise.config.templates.CacheSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.DerivativeNoiseSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
@@ -37,6 +38,7 @@ import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractal
import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.CubicSplineNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ExpressionNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearMapNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
@@ -101,6 +103,7 @@ public class NoiseAddon implements AddonInitializer {
.applyLoader(DerivativeNoiseSampler.class, DerivativeNoiseSamplerTemplate::new);
noiseRegistry.register(addon.key("LINEAR"), LinearNormalizerTemplate::new);
noiseRegistry.register(addon.key("LINEAR_MAP"), LinearMapNormalizerTemplate::new);
noiseRegistry.register(addon.key("NORMAL"), NormalNormalizerTemplate::new);
noiseRegistry.register(addon.key("CLAMP"), ClampNormalizerTemplate::new);
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
@@ -148,6 +151,8 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new));
noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new));
noiseRegistry.register(addon.key("CACHE"), CacheSamplerTemplate::new);
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
@@ -0,0 +1,24 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.CacheSampler;
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
public class CacheSamplerTemplate extends SamplerTemplate<CacheSampler> {
@Value("sampler")
@Default
private NoiseSampler sampler;
public CacheSamplerTemplate() {
}
@Override
public NoiseSampler get() {
return new CacheSampler(sampler, getDimensions());
}
}
@@ -34,7 +34,11 @@ public class CellularNoiseTemplate extends NoiseTemplate<CellularSampler> {
@Value("lookup")
@Default
private @Meta NoiseSampler lookup = new OpenSimplex2Sampler();
@Value("salt-lookup")
@Default
private @Meta boolean saltLookup = true;
@Override
public NoiseSampler get() {
CellularSampler sampler = new CellularSampler();
@@ -44,6 +48,7 @@ public class CellularNoiseTemplate extends NoiseTemplate<CellularSampler> {
sampler.setReturnType(cellularReturnType);
sampler.setDistanceFunction(cellularDistanceFunction);
sampler.setSalt(salt);
sampler.setSaltLookup(saltLookup);
return sampler;
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.normalizer.LinearMapNormalizer;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearMapNormalizerTemplate extends NormalizerTemplate<LinearMapNormalizer> {
@Value("from.a")
@Default
private @Meta double aFrom = -1;
@Value("from.b")
@Default
private @Meta double bFrom = 1;
@Value("to.a")
private @Meta double aTo;
@Value("to.b")
private @Meta double bTo;
@Override
public NoiseSampler get() {
return new LinearMapNormalizer(function, aFrom, aTo, bFrom, bTo);
}
}
@@ -0,0 +1,28 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearMapNormalizer extends Normalizer {
private final double aFrom;
private final double aTo;
private final double bFrom;
private final double bTo;
public LinearMapNormalizer(NoiseSampler sampler, double aFrom, double aTo, double bFrom, double bTo) {
super(sampler);
this.aFrom = aFrom;
this.aTo = aTo;
this.bFrom = bFrom;
this.bTo = bTo;
}
@Override
public double normalize(double in) {
return (in - aFrom) * (aTo - bTo) / (aFrom - bFrom) + aTo;
}
}
@@ -0,0 +1,74 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.cache.DoubleSeededVector2Key;
import com.dfsek.terra.api.util.cache.DoubleSeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
public class CacheSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final ThreadLocal<Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>>> cache2D;
private final ThreadLocal<Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>>> cache3D;
public CacheSampler(NoiseSampler sampler, int dimensions) {
this.sampler = sampler;
if (dimensions == 2) {
LoadingCache<DoubleSeededVector2Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleNoise);
this.cache2D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector2Key(0, 0, 0), cache).mutable());
this.cache3D = null;
} else {
LoadingCache<DoubleSeededVector3Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleNoise);
this.cache3D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector3Key(0, 0, 0, 0), cache).mutable());
this.cache2D = null;
}
}
private Double sampleNoise(DoubleSeededVector2Key vec) {
this.cache2D.get().setLeft(new DoubleSeededVector2Key(0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
}
private Double sampleNoise(DoubleSeededVector3Key vec) {
this.cache3D.get().setLeft(new DoubleSeededVector3Key(0, 0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.y, vec.z);
}
@Override
public double noise(long seed, double x, double y) {
Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>> cachePair = cache2D.get();
DoubleSeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public double noise(long seed, double x, double y, double z) {
Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>> cachePair = cache3D.get();
DoubleSeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
}
@@ -12,4 +12,14 @@ public class AdditionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left + right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
for(int i = 0; i < dimensions; i++) {
out[i] = left[i] + right[i];
}
return out;
}
}
@@ -1,9 +1,10 @@
package com.dfsek.terra.addons.noise.samplers.arithmetic;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
public abstract class BinaryArithmeticSampler implements NoiseSampler {
public abstract class BinaryArithmeticSampler implements DerivativeNoiseSampler {
private final NoiseSampler left;
private final NoiseSampler right;
@@ -12,6 +13,11 @@ public abstract class BinaryArithmeticSampler implements NoiseSampler {
this.right = right;
}
@Override
public boolean isDifferentiable() {
return DerivativeNoiseSampler.isDifferentiable(left) && DerivativeNoiseSampler.isDifferentiable(right);
}
@Override
public double noise(long seed, double x, double y) {
return operate(left.noise(seed, x, y), right.noise(seed, x, y));
@@ -22,5 +28,17 @@ public abstract class BinaryArithmeticSampler implements NoiseSampler {
return operate(left.noise(seed, x, y, z), right.noise(seed, x, y, z));
}
@Override
public double[] noised(long seed, double x, double y) {
return operateDerivative(((DerivativeNoiseSampler)left).noised(seed, x, y), ((DerivativeNoiseSampler)right).noised(seed, x, y));
}
@Override
public double[] noised(long seed, double x, double y, double z) {
return operateDerivative(((DerivativeNoiseSampler)left).noised(seed, x, y, z), ((DerivativeNoiseSampler)right).noised(seed, x, y, z));
}
public abstract double operate(double left, double right);
public abstract double[] operateDerivative(double[] left, double[] right);
}
@@ -12,4 +12,15 @@ public class DivisionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left / right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
out[0] = left[0] / right[0];
for(int i = 1; i < dimensions; i++) {
out[i] = (left[i] * right[0] - left[0] * right[i]) / (right[0] * right[0]);
}
return out;
}
}
@@ -12,4 +12,11 @@ public class MaxSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return Math.max(left, right);
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
double leftValue = left[0];
double rightValue = right[0];
return leftValue > rightValue ? left : right;
}
}
@@ -12,4 +12,11 @@ public class MinSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return Math.min(left, right);
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
double leftValue = left[0];
double rightValue = right[0];
return leftValue < rightValue ? left : right;
}
}
@@ -12,4 +12,15 @@ public class MultiplicationSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left * right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
out[0] = left[0] * right[0];
for(int i = 1; i < dimensions; i++) {
out[i] = left[i] * right[0] + left[0] * right[i];
}
return out;
}
}
@@ -12,4 +12,14 @@ public class SubtractionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left - right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
for(int i = 0; i < dimensions; i++) {
out[i] = left[i] - right[i];
}
return out;
}
}
@@ -197,7 +197,9 @@ public class CellularSampler extends NoiseFunction {
private double jitterModifier = 1.0;
private NoiseSampler noiseLookup;
private boolean saltLookup;
public CellularSampler() {
noiseLookup = new OpenSimplex2Sampler();
}
@@ -217,7 +219,11 @@ public class CellularSampler extends NoiseFunction {
public void setReturnType(ReturnType returnType) {
this.returnType = returnType;
}
public void setSaltLookup(boolean saltLookup) {
this.saltLookup = saltLookup;
}
@Override
public double getNoiseRaw(long sl, double x, double y) {
int seed = (int) sl;
@@ -286,8 +292,8 @@ public class CellularSampler extends NoiseFunction {
case Distance2Sub -> distance1 - distance0 - 1;
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY);
case NoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), x / frequency - centerX, y / frequency - centerY);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -377,8 +383,8 @@ public class CellularSampler extends NoiseFunction {
case Distance2Sub -> distance1 - distance0 - 1;
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case NoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -7,6 +7,7 @@
package com.dfsek.terra.addons.noise.samplers.noise.fractal;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
@@ -52,4 +53,60 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
return sum;
}
@Override
public boolean isDifferentiable() {
return DerivativeNoiseSampler.isDifferentiable(input);
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
double[] sum = {0, 0, 0};
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
// This should only be called after `input` is verified as a `DerivativeNoiseSampler`
// so this should be a safe cast
double[] noise = ((DerivativeNoiseSampler) input).noised(seed++, x, y);
sum[0] += noise[0] * amp;
// Directional derivative of each octave can be subject to the same addition and product
// as per derivative sum and product rules in order to produce the correct final derivative
sum[1] += noise[1] * amp;
sum[2] += noise[2] * amp;
amp *= MathUtil.lerp(weightedStrength, 1.0, Math.min(noise[0] + 1, 2) * 0.5);
x *= lacunarity;
y *= lacunarity;
amp *= gain;
}
return sum;
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
double[] sum = {0, 0, 0, 0};
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double[] noise = ((DerivativeNoiseSampler) input).noised(seed++, x, y, z);
sum[0] += noise[0] * amp;
// See comment in 2D version
sum[1] += noise[1] * amp;
sum[2] += noise[2] * amp;
sum[3] += noise[3] * amp;
amp *= MathUtil.lerp(weightedStrength, 1.0, (noise[0] + 1) * 0.5);
x *= lacunarity;
y *= lacunarity;
z *= lacunarity;
amp *= gain;
}
return sum;
}
}
@@ -7,11 +7,11 @@
package com.dfsek.terra.addons.noise.samplers.noise.fractal;
import com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction;
import com.dfsek.terra.addons.noise.samplers.noise.DerivativeNoiseFunction;
import com.dfsek.terra.api.noise.NoiseSampler;
public abstract class FractalNoiseFunction extends NoiseFunction {
public abstract class FractalNoiseFunction extends DerivativeNoiseFunction {
protected final NoiseSampler input;
protected double fractalBounding = 1 / 1.75;
protected int octaves = 3;
@@ -52,4 +52,19 @@ public abstract class FractalNoiseFunction extends NoiseFunction {
public void setWeightedStrength(double weightedStrength) {
this.weightedStrength = weightedStrength;
}
@Override
public boolean isDifferentiable() {
return false;
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
}
@@ -116,12 +116,12 @@ public abstract class SimplexStyleSampler extends DerivativeNoiseFunction {
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
return new double[]{ 0, 0, 0 };
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
return new double[]{ 0, 0, 0, 0 };
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
@@ -17,6 +17,10 @@ import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.generic.Lazy;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/*
* Cache prevents configs from loading the same image multiple times into memory
@@ -26,8 +30,9 @@ record ImageCache(LoadingCache<String, Image> cache) implements Properties {
ImageLibraryPackConfigTemplate config = pack.getContext().get(ImageLibraryPackConfigTemplate.class);
ImageCache images;
if(!pack.getContext().has(ImageCache.class)) {
var cacheBuilder = Caffeine.newBuilder();
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS);
var cacheBuilder = Caffeine.newBuilder().executor(CACHE_EXECUTOR).scheduler(Scheduler.systemScheduler());
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler());
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, files)));
pack.getContext().put(images);
} else images = pack.getContext().get(ImageCache.class);
@@ -81,4 +81,7 @@ public interface Platform extends LoaderRegistrar {
@NotNull
@Contract(pure = true)
Profiler getProfiler();
@Contract(pure = true)
int getGenerationThreads();
}
@@ -6,10 +6,7 @@ package com.dfsek.terra.api.noise;
public interface DerivativeNoiseSampler extends NoiseSampler {
static boolean isDifferentiable(NoiseSampler sampler) {
if(sampler instanceof DerivativeNoiseSampler dSampler) {
return dSampler.isDifferentiable();
}
return false;
return sampler instanceof DerivativeNoiseSampler dSampler && dSampler.isDifferentiable();
}
/**
@@ -0,0 +1,9 @@
package com.dfsek.terra.api.util.cache;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CacheUtils {
public static final Executor CACHE_EXECUTOR = Executors.newSingleThreadExecutor();
}
@@ -0,0 +1,35 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector2Key {
public double x;
public double z;
public long seed;
public DoubleSeededVector2Key(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector3Key {
public double x;
public double y;
public double z;
public long seed;
public DoubleSeededVector3Key(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(y);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector2Key {
public int x;
public int z;
public long seed;
public SeededVector2Key(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,40 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector3Key {
public int x;
public int y;
public int z;
public long seed;
public SeededVector3Key(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
@@ -7,6 +7,8 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.Platform;
import org.jetbrains.annotations.Contract;
import java.util.Optional;
@@ -91,7 +93,7 @@ public interface BiomeProvider {
return StreamSupport.stream(getBiomes().spliterator(), false);
}
default CachingBiomeProvider caching() {
default CachingBiomeProvider caching(Platform platform) {
if(this instanceof CachingBiomeProvider cachingBiomeProvider) {
return cachingBiomeProvider;
}
@@ -1,5 +1,11 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import com.dfsek.terra.api.util.cache.SeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
@@ -9,6 +15,8 @@ import java.util.Optional;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.world.biome.Biome;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/**
* A biome provider implementation that lazily evaluates biomes, and caches them.
@@ -18,24 +26,43 @@ import com.dfsek.terra.api.world.biome.Biome;
public class CachingBiomeProvider implements BiomeProvider, Handle {
protected final BiomeProvider delegate;
private final int res;
private final LoadingCache<SeededVector3, Biome> cache;
private final LoadingCache<SeededVector2, Optional<Biome>> baseCache;
private final ThreadLocal<Pair.Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>>> cache;
private final ThreadLocal<Pair.Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>>> baseCache;
protected CachingBiomeProvider(BiomeProvider delegate) {
this.delegate = delegate;
this.res = delegate.resolution();
this.cache = Caffeine
.newBuilder()
.scheduler(Scheduler.disabledScheduler())
.initialCapacity(98304)
.maximumSize(98304) // 1 full chunk (high res)
.build(vec -> delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed));
this.baseCache = Caffeine
LoadingCache<SeededVector2Key, Optional<Biome>> cache = Caffeine
.newBuilder()
.maximumSize(256) // 1 full chunk (high res)
.build(vec -> delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed));
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleBiome);
this.baseCache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector2Key(0, 0, 0), cache).mutable());
LoadingCache<SeededVector3Key, Biome> cache3D = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleBiome);
this.cache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector3Key(0, 0, 0, 0), cache3D).mutable());
}
private Optional<Biome> sampleBiome(SeededVector2Key vec) {
this.baseCache.get().setLeft(new SeededVector2Key(0, 0, 0));
return this.delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed);
}
private Biome sampleBiome(SeededVector3Key vec) {
this.cache.get().setLeft(new SeededVector3Key(0, 0, 0, 0));
return this.delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed);
}
@Override
@@ -45,12 +72,18 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return cache.get(new SeededVector3(x / res, y / res, z / res, seed));
Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>> cachePair = cache.get();
SeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return baseCache.get(new SeededVector2(x / res, z / res, seed));
Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>> cachePair = baseCache.get();
SeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, z, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
@@ -62,40 +95,4 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
public int resolution() {
return delegate.resolution();
}
private record SeededVector3(int x, int y, int z, long seed) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3 that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
private record SeededVector2(int x, int z, long seed) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2 that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
}
@@ -310,6 +310,24 @@ public abstract class AbstractPlatform implements Platform {
}
}
public static int getGenerationThreadsWithReflection(String className, String fieldName, String project) {
try {
Class aClass = Class.forName(className);
int threads = aClass.getField(fieldName).getInt(null);
logger.info("{} found, setting {} generation threads.", project, threads);
return threads;
} catch(ClassNotFoundException e) {
logger.info("{} not found.", project);
} catch(NoSuchFieldException e) {
logger.warn("{} found, but {} field not found this probably means {0} has changed its code and " +
"Terra has not updated to reflect that.", project, fieldName);
} catch(IllegalAccessException e) {
logger.error("Failed to access {} field in {}, assuming 1 generation thread.", fieldName, project, e);
}
return 0;
}
@Override
public void register(TypeRegistry registry) {
loaders.register(registry);
@@ -339,4 +357,9 @@ public abstract class AbstractPlatform implements Platform {
public @NotNull Profiler getProfiler() {
return profiler;
}
@Override
public int getGenerationThreads() {
return 1;
}
}
@@ -232,7 +232,7 @@ public class ConfigPackImpl implements ConfigPack {
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
selfLoader.load(packPostTemplate, packManifest);
seededBiomeProvider =
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching() : packPostTemplate.getProviderBuilder();
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching(platform) : packPostTemplate.getProviderBuilder();
checkDeadEntries();
}
@@ -51,7 +51,13 @@ public class PlatformImpl extends AbstractPlatform {
private final TerraBukkitPlugin plugin;
private int generationThreads;
public PlatformImpl(TerraBukkitPlugin plugin) {
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
if (generationThreads == 0) {
generationThreads = 1;
}
this.plugin = plugin;
load();
}
@@ -108,6 +114,11 @@ public class PlatformImpl extends AbstractPlatform {
return itemHandle;
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
@Override
public void register(TypeRegistry registry) {
super.register(registry);
+9
View File
@@ -10,6 +10,9 @@ dependencies {
shadedApi("commons-io", "commons-io", Versions.Libraries.Internal.apacheIO)
shadedApi("com.github.Querz", "NBT", Versions.CLI.nbt)
shadedImplementation("info.picocli", "picocli", Versions.CLI.picocli)
annotationProcessor("info.picocli", "picocli-codegen", Versions.CLI.picocli)
shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
shadedImplementation("ch.qos.logback", "logback-classic", Versions.CLI.logback)
@@ -26,6 +29,12 @@ tasks.withType<Jar> {
}
}
tasks.withType<JavaCompile> {
doFirst {
options.compilerArgs.add("-Aproject=${project.group}/${project.name}")
}
}
application {
mainClass.set(javaMainClass)
}
@@ -22,6 +22,8 @@ public class CLIPlatform extends AbstractPlatform {
private final CLIWorldHandle worldHandle = new CLIWorldHandle();
private final CLIItemHandle itemHandle = new CLIItemHandle();
private final int generationThreads = Runtime.getRuntime().availableProcessors() - 1;
public CLIPlatform() {
LOGGER.info("Root directory: {}", getDataFolder().getAbsoluteFile());
load();
@@ -58,4 +60,9 @@ public class CLIPlatform extends AbstractPlatform {
super.register(registry);
registry.registerLoader(PlatformBiome.class, (TypeLoader<PlatformBiome>) (annotatedType, o, configLoader, depthTracker) -> () -> o);
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
}
@@ -5,41 +5,74 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.Callable;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.api.util.vector.Vector2Int;
import com.dfsek.terra.cli.world.CLIWorld;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
public final class TerraCLI {
private static final Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
public static void main(String... args) {
//TODO auto pull in version
@Command(name = "TerraCLI", mixinStandardHelpOptions = true, version = "6.6.0",
description = "Generates a Terra World and saves it in minecraft region format.")
public final class TerraCLI implements Callable<Integer> {
@Option(names = { "-s", "--size"}, description = "Number of regions to generate.")
private int size = 2;
@Option(names = { "-p", "--pack"}, description = "Config pack to use.")
private String pack = "OVERWORLD";
@Option(names = { "--seed"}, description = "Seed for world generation.")
private long seed = 0;
@Option(names = { "--max-height"}, description = "Maximum height of the world.")
private int maxHeight = 320;
@Option(names = { "--min-height"}, description = "Minimum height of the world.")
private int minHeight = -64;
@Option(names = { "--no-save"}, description = "Don't save the world to disk.")
private boolean noSave = false;
@Override
public Integer call() {
Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
LOGGER.info("Starting Terra CLI...");
CLIPlatform platform = new CLIPlatform();
platform.getEventManager().callEvent(new PlatformInitializationEvent());
ConfigPack generate = platform.getConfigRegistry().getByID("OVERWORLD").orElseThrow(); // TODO: make this a cli argument
ConfigPack generate = platform.getConfigRegistry().getByID(pack).orElseThrow();
CLIWorld world = new CLIWorld(2, 2, 384, -64, generate);
CLIWorld world = new CLIWorld(size, seed, maxHeight, minHeight, generate, noSave);
world.generate();
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
if(!noSave) {
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
}
LOGGER.info("Done.");
System.exit(0);
return 0;
}
public static void main(String... args) {
int exitCode = new CommandLine(new TerraCLI()).execute(args);
System.exit(exitCode);
}
}
@@ -43,6 +43,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
private final ChunkGenerator chunkGenerator;
private final BiomeProvider biomeProvider;
private final ConfigPack pack;
private final boolean noSave;
private final AtomicInteger amount = new AtomicInteger(0);
private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 1);
@@ -51,7 +52,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
long seed,
int maxHeight,
int minHeight,
ConfigPack pack) {
ConfigPack pack, boolean noSave) {
this.size = size;
this.maxHeight = maxHeight;
this.minHeight = minHeight;
@@ -59,6 +60,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
this.chunkGenerator = pack.getGeneratorProvider().newInstance(pack);
this.biomeProvider = pack.getBiomeProvider();
this.pack = pack;
this.noSave = noSave;
size += 1;
@@ -73,6 +75,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
}
public void generate() {
ArrayList<Double> CPSHistory = new ArrayList<>();
int sizeChunks = size * 32;
List<Future<?>> futures = new ArrayList<>();
final AtomicLong start = new AtomicLong(System.nanoTime());
@@ -83,7 +86,13 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
futures.add(executor.submit(() -> {
try {
int num = amount.getAndIncrement();
CLIChunk chunk = getChunkAt(finalX, finalZ);
CLIChunk chunk;
if (!noSave) {
chunk = getChunkAt(finalX, finalZ);
} else {
chunk = new CLIChunk(Math.floorMod(finalX, 32), Math.floorMod(finalZ, 32), this);
}
BiomeProvider cachingBiomeProvider = pack.getBiomeProvider();
chunkGenerator.generateChunkData(chunk, this, cachingBiomeProvider, finalX, finalZ);
CLIProtoWorld protoWorld = new CLIProtoWorld(this, cachingBiomeProvider, finalX, finalZ);
@@ -91,6 +100,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
if(num % 240 == 239) {
long time = System.nanoTime();
double cps = num / ((double) (time - start.get()) / 1000000000);
CPSHistory.add(cps);
LOGGER.info("Generating chunk at ({}, {}), generated {} chunks at {}cps", finalX, finalZ, num, cps);
amount.set(0);
start.set(System.nanoTime());
@@ -109,6 +119,8 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
e.printStackTrace();
}
}
LOGGER.info("Average CPS: {}", CPSHistory.stream().mapToDouble(d -> d).average().orElse(0));
}
@Override
@@ -3,6 +3,9 @@ package com.dfsek.terra.lifecycle;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import net.minecraft.MinecraftVersion;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.registry.Registry;
@@ -37,8 +40,15 @@ public abstract class LifecyclePlatform extends ModPlatform {
private static final AtomicReference<Registry<MultiNoiseBiomeSourceParameterList>> NOISE = new AtomicReference<>();
private static final AtomicReference<Registry<Enchantment>> ENCHANTMENT = new AtomicReference<>();
private static MinecraftServer server;
private int generationThreads;
public LifecyclePlatform() {
generationThreads = getGenerationThreadsWithReflection("com.ishland.c2me.base.common.GlobalExecutors", "GLOBAL_EXECUTOR_PARALLELISM", "C2ME");
if (generationThreads == 0) {
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
} if (generationThreads == 0) {
generationThreads = 1;
}
CommonPlatform.initialize(this);
load();
}
@@ -55,6 +65,8 @@ public abstract class LifecyclePlatform extends ModPlatform {
ENCHANTMENT.set(enchantmentRegistry);
}
@Override
public MinecraftServer getServer() {
return server;