Compare commits

...

42 Commits

Author SHA1 Message Date
Zoë Gidiere b10fd84e00 Merge remote-tracking branch 'origin/ver/6.5.0' into dev/metapacks 2024-01-05 09:11:42 -07:00
Astrash 271e7f3c69 Fix empty slant holders attempting to calculate slant
This occurs when the NoiseChunkGenerator3D#getSlant method is invoked at
a position where the biome has an empty slant holder, currently possible
when using the slant locator addon.

This is fixed by making slant calculation independent of SlantHolders.
Some minor refactoring has also been done as a result of this change.
2024-01-05 18:42:35 +11:00
Zoë 3e04bae828 Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-24 22:39:15 -06:00
Zoë 6681bdfbf2 Merge remote-tracking branch 'origin/master' into ver/6.5.0 2023-12-24 22:39:06 -06:00
dfsek ab60f14ff1 Merge pull request #444 from PolyhedralDev/dev/fix-lerp
6.4.3 - Fix lerp (urgent)
2023-12-24 02:15:54 -07:00
dfsek d8ba9e1016 bump version 2023-12-24 02:06:11 -07:00
dfsek 3622003a39 fix other lerp issues 2023-12-24 02:02:46 -07:00
dfsek 84cd96ecf7 fix BrownianMotionSampler lerp usage 2023-12-24 01:53:05 -07:00
Zoë 82334cfe9e Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-22 15:03:04 -06:00
Zoë 8afda7424c Merge branch 'master' into ver/6.5.0 2023-12-22 15:02:53 -06:00
Zoë Gidiere 50ba1c6eab push changes 2023-12-21 08:47:52 -07:00
Zoë da4ab8b71c Merge pull request #441 from PolyhedralDev/ver/6.4.2
Ver/6.4.2
2023-12-18 13:01:36 -07:00
Zoë Gidiere 27a967f3a6 fix build 2023-12-12 20:17:15 -07:00
Zoë Gidiere 4c71355535 Reformat 2023-12-12 19:07:57 -07:00
Zoë Gidiere e83b70b5ae generation settings 2023-12-12 18:52:31 -07:00
Zoë Gidiere 56b428d501 refactor 2023-12-12 17:19:32 -07:00
Zoë Gidiere 7ca24faad3 fix loading 2023-12-12 17:15:54 -07:00
Zoë Gidiere 9d200565d7 more fixes 2023-12-12 16:54:27 -07:00
Zoë Gidiere f6c2795eaf remove debug loging 2023-12-12 16:12:36 -07:00
Zoë Gidiere 6f03746e41 another fix 2023-12-12 16:09:08 -07:00
Zoë Gidiere 47c8cb3168 Fix up random changes 2023-12-12 16:00:56 -07:00
Zoë Gidiere a9f973cae9 WIP Random Changes 2023-12-12 15:57:04 -07:00
Zoë Gidiere 033181d7c8 more wip changes 2023-12-12 15:48:31 -07:00
Zoë Gidiere e11a235386 WIP Dim opts 2023-12-12 13:55:03 -07:00
Zoë Gidiere db1e924246 more metapack work 2023-12-12 13:35:52 -07:00
Zoë Gidiere c86faa44ec Remove netherfossiloptimization because it's no longer applicable
also it's not needed mc seems to have done this themselves
2023-12-12 13:35:24 -07:00
Zoë Gidiere de91a6facb metapack linking instead of shading 2023-12-11 23:36:05 -07:00
Zoë Gidiere d4a328eb38 who let the datadrive (the dimensions) 2023-12-11 21:14:43 -07:00
Zoë Gidiere b039629b2d WIP meta pack system 2023-12-11 16:25:13 -07:00
Zoë Gidiere d48fa96ec7 opt import 2023-12-11 14:56:07 -07:00
Zoë Gidiere 8dd1f49b88 Merge remote-tracking branch 'origin/dev/remove-loader' into dev/metapacks 2023-12-11 14:54:59 -07:00
Zoë Gidiere 81528915a8 Merge branch 'ver/6.5.0' into dev/metapacks 2023-12-11 14:54:44 -07:00
Zoë Gidiere 5979254808 wip 2023-12-11 14:54:27 -07:00
Zoë Gidiere 86f2356cc8 Merge branch 'ver/6.4.2' into ver/6.5.0 2023-11-27 17:55:40 -07:00
Astrash c0aaf6c6e8 Add messages to exceptions 2023-11-28 10:36:30 +11:00
Astrash 1ab3233cba Reformat code 2023-11-25 15:14:16 +11:00
Astrash 59ea5a69d8 Refactor pack loading
- Combine initial load and reload logic together between each platform implementation
- Should prevent pack load errors from blocking other packs from loading.
2023-11-25 15:10:43 +11:00
Astrash 4ba71e9c27 packDirectory -> rootPath 2023-11-25 15:07:45 +11:00
Astrash 5c7441241c Replace Loader with java.nio.files 2023-11-25 13:31:42 +11:00
Astrash ffb1198da2 Merge branch 'master' into ver/6.5.0 2023-11-25 12:33:51 +11:00
Astrash 2c211f0aa6 Merge branch 'master' into ver/6.5.0 2023-11-25 12:29:54 +11:00
Zoë Gidiere 866d527d35 bump version 2023-11-17 13:11:28 -07:00
98 changed files with 1531 additions and 797 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.4.2"))
versionProjects(":common:implementation", version("6.4.2"))
versionProjects(":platforms", version("6.4.2"))
versionProjects(":common:api", version("6.5.0"))
versionProjects(":common:implementation", version("6.5.0"))
versionProjects(":platforms", version("6.5.0"))
allprojects {
@@ -27,7 +27,8 @@ fun Project.configureDistribution() {
group = "terra"
doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively()
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
val defaultPackUrl =
URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
downloadPack(defaultPackUrl, project)
}
}
@@ -1,9 +1,9 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
import java.util.Set;
public final class DelegatedPipelineBiome implements PipelineBiome {
private final Biome biome;
@@ -7,17 +7,17 @@
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class ReplaceListStage implements Stage {
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
@@ -13,6 +13,7 @@ import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.config.palette.BiomePaletteTemplate;
import com.dfsek.terra.addons.chunkgenerator.config.palette.slant.SlantLayerTemplate;
import com.dfsek.terra.addons.chunkgenerator.generation.NoiseChunkGenerator3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
@@ -45,8 +46,8 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
.priority(1000)
.then(event -> {
event.getPack().applyLoader(SlantHolder.CalculationMethod.class,
(type, o, loader, depthTracker) -> SlantHolder.CalculationMethod.valueOf((String) o));
event.getPack().applyLoader(SlantCalculationMethod.class,
(type, o, loader, depthTracker) -> SlantCalculationMethod.valueOf((String) o));
NoiseChunkGeneratorPackConfigTemplate config = event.loadTemplate(new NoiseChunkGeneratorPackConfigTemplate());
event.getPack().getContext().put(config);
@@ -57,7 +58,7 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
pack -> new NoiseChunkGenerator3D(pack, platform, config.getElevationBlend(),
config.getHorizontalRes(),
config.getVerticalRes(), noisePropertiesPropertyKey,
paletteInfoPropertyKey));
paletteInfoPropertyKey, config.getSlantCalculationMethod()));
event.getPack()
.applyLoader(SlantHolder.Layer.class, SlantLayerTemplate::new);
})
@@ -4,7 +4,7 @@ import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.properties.Properties;
@@ -24,7 +24,7 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate, Pr
@Value("slant.calculation-method")
@Default
private SlantHolder.@Meta CalculationMethod slantCalculationMethod = SlantHolder.CalculationMethod.Derivative;
private @Meta SlantCalculationMethod slantCalculationMethod = SlantCalculationMethod.Derivative;
public int getElevationBlend() {
return elevationBlend;
@@ -38,7 +38,7 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate, Pr
return verticalRes;
}
public SlantHolder.CalculationMethod getSlantCalculationMethod() {
public SlantCalculationMethod getSlantCalculationMethod() {
return slantCalculationMethod;
}
}
@@ -16,6 +16,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
@@ -27,7 +28,7 @@ import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class BiomePaletteTemplate implements ObjectTemplate<BiomePaletteInfo> {
private final Platform platform;
private final SlantHolder.CalculationMethod slantCalculationMethod;
private final SlantCalculationMethod slantCalculationMethod;
@Value("slant")
@Default
@Description("The slant palettes to use in this biome.")
@@ -56,7 +57,7 @@ public class BiomePaletteTemplate implements ObjectTemplate<BiomePaletteInfo> {
@Default
private @Meta boolean updatePalette = false;
public BiomePaletteTemplate(Platform platform, SlantHolder.CalculationMethod slantCalculationMethod) {
public BiomePaletteTemplate(Platform platform, SlantCalculationMethod slantCalculationMethod) {
this.platform = platform;
this.slantCalculationMethod = slantCalculationMethod;
}
@@ -8,10 +8,13 @@
package com.dfsek.terra.addons.chunkgenerator.generation;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.generation.math.PaletteUtil;
import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.LazilyEvaluatedInterpolator;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.SamplerProvider;
@@ -42,16 +45,19 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
private final PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey;
private final PropertyKey<BiomeNoiseProperties> noisePropertiesKey;
private final SlantCalculationMethod slantCalculationMethod;
public NoiseChunkGenerator3D(ConfigPack pack, Platform platform, int elevationBlend, int carverHorizontalResolution,
int carverVerticalResolution,
PropertyKey<BiomeNoiseProperties> noisePropertiesKey,
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey) {
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey, SlantCalculationMethod slantCalculationMethod) {
this.platform = platform;
this.air = platform.getWorldHandle().air();
this.carverHorizontalResolution = carverHorizontalResolution;
this.carverVerticalResolution = carverVerticalResolution;
this.paletteInfoPropertyKey = paletteInfoPropertyKey;
this.noisePropertiesKey = noisePropertiesKey;
this.slantCalculationMethod = slantCalculationMethod;
int maxBlend = pack
.getBiomeProvider()
.stream()
@@ -63,6 +69,17 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
this.samplerCache = new SamplerProvider(platform, elevationBlend, noisePropertiesKey, maxBlend);
}
private Palette paletteAt(int x, int y, int z, Sampler3D sampler, BiomePaletteInfo paletteInfo, int depth) {
SlantHolder slantHolder = paletteInfo.slantHolder();
if(slantHolder.isAboveDepth(depth)) {
double slant = slantCalculationMethod.slant(sampler, x, y, z);
if(slantHolder.isInSlantThreshold(slant)) {
return slantHolder.getPalette(slant).getPalette(y);
}
}
return paletteInfo.paletteHolder().getPalette(y);
}
@Override
@SuppressWarnings("try")
public void generateChunkData(@NotNull ProtoChunk chunk, @NotNull WorldProperties world,
@@ -103,8 +120,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
if(sampler.sample(x, y, z) > 0) {
if(carver.sample(x, y, z) <= 0) {
data = PaletteUtil
.getPalette(x, y, z, sampler, paletteInfo, paletteLevel)
data = paletteAt(x, y, z, sampler, paletteInfo, paletteLevel)
.get(paletteLevel, cx, y, cz, seed);
chunk.setBlock(x, y, z, data);
paletteLevel++;
@@ -135,7 +151,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
int fdX = Math.floorMod(x, 16);
int fdZ = Math.floorMod(z, 16);
Palette palette = PaletteUtil.getPalette(fdX, y, fdZ, sampler, paletteInfo, 0);
Palette palette = paletteAt(fdX, y, fdZ, sampler, paletteInfo, 0);
double noise = sampler.sample(fdX, y, fdZ);
if(noise > 0) {
int level = 0;
@@ -157,11 +173,8 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
public double getSlant(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) {
int fdX = Math.floorMod(x, 16);
int fdZ = Math.floorMod(z, 16);
return biomeProvider.getBiome(x, y, z, world.getSeed())
.getContext()
.get(paletteInfoPropertyKey)
.slantHolder()
.calculateSlant(samplerCache.get(x, z, world, biomeProvider), fdX, y, fdZ);
Sampler3D sampler = samplerCache.get(x, z, world, biomeProvider);
return slantCalculationMethod.slant(sampler, fdX, y, fdZ);
}
public SamplerProvider samplerProvider() {
@@ -1,29 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.generation.math;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public final class PaletteUtil {
public static Palette getPalette(int x, int y, int z, Sampler3D sampler, BiomePaletteInfo paletteInfo, int depth) {
SlantHolder slantHolder = paletteInfo.slantHolder();
if(slantHolder.isAboveDepth(depth)) {
double slant = slantHolder.calculateSlant(sampler, x, y, z);
if(slantHolder.isInSlantThreshold(slant)) {
return slantHolder.getPalette(slant).getPalette(y);
}
}
return paletteInfo.paletteHolder().getPalette(y);
}
}
@@ -0,0 +1,69 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.api.util.vector.Vector3;
public enum SlantCalculationMethod {
DotProduct {
private static final Vector3 DOT_PRODUCT_DIRECTION = Vector3.of(0, 1, 0);
private static final Vector3[] DOT_PRODUCT_SAMPLE_POINTS = {
Vector3.of(0, 0, -DERIVATIVE_DIST),
Vector3.of(0, 0, DERIVATIVE_DIST),
Vector3.of(0, -DERIVATIVE_DIST, 0),
Vector3.of(0, DERIVATIVE_DIST, 0),
Vector3.of(-DERIVATIVE_DIST, 0, 0),
Vector3.of(DERIVATIVE_DIST, 0, 0)
};
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
Vector3.Mutable normalApproximation = Vector3.Mutable.of(0, 0, 0);
for(Vector3 point : DOT_PRODUCT_SAMPLE_POINTS) {
var scalar = -sampler.sample(x + point.getX(), y + point.getY(), z + point.getZ());
normalApproximation.add(point.mutable().multiply(scalar));
}
return DOT_PRODUCT_DIRECTION.dot(normalApproximation.normalize());
}
@Override
public boolean floorToThreshold() {
return false;
}
},
Derivative {
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(
((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
@Override
public boolean floorToThreshold() {
return true;
}
};
private static final double DERIVATIVE_DIST = 0.55;
public abstract double slant(Sampler3D sampler, double x, double y, double z);
/*
* Controls whether palettes should be applied before or after their respective thresholds.
*
* If true, slant values will map to the palette of the next floor threshold, otherwise they
* will map to the ceiling.
*/
public abstract boolean floorToThreshold();
}
@@ -14,6 +14,7 @@ import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
@@ -21,7 +22,7 @@ public class MultipleSlantHolder extends SlantHolderImpl {
private final NavigableMap<Double, PaletteHolder> layers;
private final double slantThreshold;
MultipleSlantHolder(List<SlantHolder.Layer> slant, int slantDepth, CalculationMethod calculationMethod) {
MultipleSlantHolder(List<SlantHolder.Layer> slant, int slantDepth, SlantCalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
NavigableMap<Double, PaletteHolder> layers = new TreeMap<>(
slant.stream().collect(Collectors.toMap(SlantHolder.Layer::threshold, SlantHolder.Layer::palette)));
@@ -1,5 +1,6 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
@@ -7,7 +8,7 @@ final class SingleSlantHolder extends SlantHolderImpl {
private final SlantHolder.Layer layer;
public SingleSlantHolder(SlantHolder.Layer layer, int slantDepth, CalculationMethod calculationMethod) {
public SingleSlantHolder(SlantHolder.Layer layer, int slantDepth, SlantCalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
this.layer = layer;
}
@@ -2,19 +2,13 @@ package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import java.util.List;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.api.util.vector.Vector3;
public interface SlantHolder {
SlantHolder EMPTY = new SlantHolder() {
@Override
public double calculateSlant(Sampler3D sampler, double x, double y, double z) {
throw new UnsupportedOperationException("Empty holder should not calculate slant");
}
@Override
public boolean isAboveDepth(int depth) {
return false;
@@ -31,7 +25,7 @@ public interface SlantHolder {
}
};
static SlantHolder of(List<SlantHolder.Layer> layers, int slantDepth, CalculationMethod calculationMethod) {
static SlantHolder of(List<SlantHolder.Layer> layers, int slantDepth, SlantCalculationMethod calculationMethod) {
if(layers.isEmpty()) {
return EMPTY;
} else if(layers.size() == 1) {
@@ -40,8 +34,6 @@ public interface SlantHolder {
return new MultipleSlantHolder(layers, slantDepth, calculationMethod);
}
double calculateSlant(Sampler3D sampler, double x, double y, double z);
boolean isAboveDepth(int depth);
boolean isInSlantThreshold(double slant);
@@ -49,71 +41,6 @@ public interface SlantHolder {
PaletteHolder getPalette(double slant);
enum CalculationMethod {
DotProduct {
private static final Vector3 DOT_PRODUCT_DIRECTION = Vector3.of(0, 1, 0);
private static final Vector3[] DOT_PRODUCT_SAMPLE_POINTS = {
Vector3.of(0, 0, -DERIVATIVE_DIST),
Vector3.of(0, 0, DERIVATIVE_DIST),
Vector3.of(0, -DERIVATIVE_DIST, 0),
Vector3.of(0, DERIVATIVE_DIST, 0),
Vector3.of(-DERIVATIVE_DIST, 0, 0),
Vector3.of(DERIVATIVE_DIST, 0, 0)
};
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
Vector3.Mutable normalApproximation = Vector3.Mutable.of(0, 0, 0);
for(Vector3 point : DOT_PRODUCT_SAMPLE_POINTS) {
var scalar = -sampler.sample(x + point.getX(), y + point.getY(), z + point.getZ());
normalApproximation.add(point.mutable().multiply(scalar));
}
return DOT_PRODUCT_DIRECTION.dot(normalApproximation.normalize());
}
@Override
public boolean floorToThreshold() {
return false;
}
},
Derivative {
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(
((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
@Override
public boolean floorToThreshold() {
return true;
}
};
private static final double DERIVATIVE_DIST = 0.55;
public abstract double slant(Sampler3D sampler, double x, double y, double z);
/*
* Controls whether palettes should be applied before or after their respective thresholds.
*
* If true, slant values will map to the palette of the next floor threshold, otherwise they
* will map to the ceiling.
*/
public abstract boolean floorToThreshold();
}
record Layer(PaletteHolder palette, double threshold) {
}
}
@@ -1,26 +1,19 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
public abstract class SlantHolderImpl implements SlantHolder {
protected final boolean floorToThreshold;
private final SlantHolder.CalculationMethod calculationMethod;
private final int slantDepth;
protected SlantHolderImpl(int slantDepth, CalculationMethod calculationMethod) {
protected SlantHolderImpl(int slantDepth, SlantCalculationMethod calculationMethod) {
this.floorToThreshold = calculationMethod.floorToThreshold();
this.calculationMethod = calculationMethod;
this.slantDepth = slantDepth;
}
protected abstract double getSlantThreshold();
@Override
public final double calculateSlant(Sampler3D sampler, double x, double y, double z) {
return calculationMethod.slant(sampler, x, y, z);
}
@Override
public final boolean isAboveDepth(int depth) {
return depth <= slantDepth;
@@ -6,7 +6,8 @@ import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.LongArgument;
import cloud.commandframework.context.CommandContext;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
@@ -56,7 +57,11 @@ public class StructureCommandAddon implements AddonInitializer {
structure.generate(
sender.position().toInt(),
sender.world(),
((Long) context.get("seed") == 0) ? new Random() : new Random(context.get("seed")),
((Long) context.get("seed") == 0)
? RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create()
: RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create(context.get("seed")),
context.get("rotation")
);
})
@@ -1,6 +1,7 @@
package com.dfsek.terra.addons.feature.distributor.distributors;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.Distributor;
import com.dfsek.terra.api.util.MathUtil;
@@ -24,7 +25,8 @@ public class PaddedGridDistributor implements Distributor {
int cellX = Math.floorDiv(x, cellWidth);
int cellZ = Math.floorDiv(z, cellWidth);
Random random = new Random((MathUtil.murmur64(MathUtil.squash(cellX, cellZ)) ^ seed) + salt);
RandomGenerator random = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(
(MathUtil.murmur64(MathUtil.squash(cellX, cellZ)) ^ seed) + salt);
int pointX = random.nextInt(width) + cellX * cellWidth;
int pointZ = random.nextInt(width) + cellZ * cellWidth;
@@ -10,7 +10,8 @@ package com.dfsek.terra.addons.flora.flora.gen;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.properties.enums.Direction;
@@ -71,7 +72,7 @@ public class TerraFlora implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
boolean doRotation = testRotation.size() > 0;
int size = layers.size();
int c = ceiling ? -1 : 1;
@@ -86,7 +87,9 @@ public class TerraFlora implements Structure {
location.getZ(), world.getSeed());
if(doRotation) {
Direction oneFace = new ArrayList<>(faces).get(
new Random(location.getX() ^ location.getZ()).nextInt(faces.size())); // Get random face.
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus")
.create(location.getX() ^ location.getZ())
.nextInt(faces.size())); // Get RandomGenerator face.
}
world.setBlockState(location.mutable().add(0, i + c, 0).immutable(), data, physics);
}
@@ -7,7 +7,8 @@
package com.dfsek.terra.addons.feature.locator.locators;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
@@ -40,7 +41,7 @@ public class GaussianRandomLocator implements Locator {
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
int size = points.get(r);
@@ -7,7 +7,8 @@
package com.dfsek.terra.addons.feature.locator.locators;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
@@ -36,7 +37,7 @@ public class RandomLocator implements Locator {
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
int size = points.get(r);
@@ -24,7 +24,7 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = input.noise(seed++, x, y);
sum += noise * amp;
amp *= MathUtil.lerp(1.0, Math.min(noise + 1, 2) * 0.5, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, Math.min(noise + 1, 2) * 0.5);
x *= lacunarity;
y *= lacunarity;
@@ -42,7 +42,7 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = input.noise(seed++, x, y, z);
sum += noise * amp;
amp *= MathUtil.lerp(1.0, (noise + 1) * 0.5, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, (noise + 1) * 0.5);
x *= lacunarity;
y *= lacunarity;
@@ -36,7 +36,7 @@ public class PingPongSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.noise(seed++, x, y) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, noise);
x *= lacunarity;
y *= lacunarity;
@@ -54,7 +54,7 @@ public class PingPongSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = pingPong((input.noise(seed++, x, y, z) + 1) * pingPongStrength);
sum += (noise - 0.5) * 2 * amp;
amp *= MathUtil.lerp(1.0, noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, noise);
x *= lacunarity;
y *= lacunarity;
@@ -25,7 +25,7 @@ public class RidgedFractalSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = Math.abs(input.noise(seed++, x, y));
sum += (noise * -2 + 1) * amp;
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, 1 - noise);
x *= lacunarity;
y *= lacunarity;
@@ -43,7 +43,7 @@ public class RidgedFractalSampler extends FractalNoiseFunction {
for(int i = 0; i < octaves; i++) {
double noise = Math.abs(input.noise(seed++, x, y, z));
sum += (noise * -2 + 1) * amp;
amp *= MathUtil.lerp(1.0, 1 - noise, weightedStrength);
amp *= MathUtil.lerp(weightedStrength, 1.0, 1 - noise);
x *= lacunarity;
y *= lacunarity;
@@ -33,10 +33,10 @@ public class PerlinSampler extends SimplexStyleSampler {
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = MathUtil.lerp(gradCoord(seed, x0, y0, xd0, yd0), gradCoord(seed, x1, y0, xd1, yd0), xs);
double xf1 = MathUtil.lerp(gradCoord(seed, x0, y1, xd0, yd1), gradCoord(seed, x1, y1, xd1, yd1), xs);
double xf0 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, xd0, yd0), gradCoord(seed, x1, y0, xd1, yd0));
double xf1 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, xd0, yd1), gradCoord(seed, x1, y1, xd1, yd1));
return MathUtil.lerp(xf0, xf1, ys) * 1.4247691104677813;
return MathUtil.lerp(ys, xf0, xf1) * 1.4247691104677813;
}
@Override
@@ -64,14 +64,14 @@ public class PerlinSampler extends SimplexStyleSampler {
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = MathUtil.lerp(gradCoord(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord(seed, x1, y0, z0, xd1, yd0, zd0), xs);
double xf10 = MathUtil.lerp(gradCoord(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord(seed, x1, y1, z0, xd1, yd1, zd0), xs);
double xf01 = MathUtil.lerp(gradCoord(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord(seed, x1, y0, z1, xd1, yd0, zd1), xs);
double xf11 = MathUtil.lerp(gradCoord(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord(seed, x1, y1, z1, xd1, yd1, zd1), xs);
double xf00 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord(seed, x1, y0, z0, xd1, yd0, zd0));
double xf10 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord(seed, x1, y1, z0, xd1, yd1, zd0));
double xf01 = MathUtil.lerp(xs, gradCoord(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord(seed, x1, y0, z1, xd1, yd0, zd1));
double xf11 = MathUtil.lerp(xs, gradCoord(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord(seed, x1, y1, z1, xd1, yd1, zd1));
double yf0 = MathUtil.lerp(xf00, xf10, ys);
double yf1 = MathUtil.lerp(xf01, xf11, ys);
double yf0 = MathUtil.lerp(ys, xf00, xf10);
double yf1 = MathUtil.lerp(ys, xf01, xf11);
return MathUtil.lerp(yf0, yf1, zs) * 0.964921414852142333984375;
return MathUtil.lerp(zs, yf0, yf1) * 0.964921414852142333984375;
}
}
@@ -25,10 +25,10 @@ public class ValueSampler extends ValueStyleNoise {
int x1 = x0 + PRIME_X;
int y1 = y0 + PRIME_Y;
double xf0 = MathUtil.lerp(valCoord(seed, x0, y0), valCoord(seed, x1, y0), xs);
double xf1 = MathUtil.lerp(valCoord(seed, x0, y1), valCoord(seed, x1, y1), xs);
double xf0 = MathUtil.lerp(xs, valCoord(seed, x0, y0), valCoord(seed, x1, y0));
double xf1 = MathUtil.lerp(xs, valCoord(seed, x0, y1), valCoord(seed, x1, y1));
return MathUtil.lerp(xf0, xf1, ys);
return MathUtil.lerp(ys, xf0, xf1);
}
@Override
@@ -49,14 +49,14 @@ public class ValueSampler extends ValueStyleNoise {
int y1 = y0 + PRIME_Y;
int z1 = z0 + PRIME_Z;
double xf00 = MathUtil.lerp(valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0), xs);
double xf10 = MathUtil.lerp(valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0), xs);
double xf01 = MathUtil.lerp(valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1), xs);
double xf11 = MathUtil.lerp(valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1), xs);
double xf00 = MathUtil.lerp(xs, valCoord(seed, x0, y0, z0), valCoord(seed, x1, y0, z0));
double xf10 = MathUtil.lerp(xs, valCoord(seed, x0, y1, z0), valCoord(seed, x1, y1, z0));
double xf01 = MathUtil.lerp(xs, valCoord(seed, x0, y0, z1), valCoord(seed, x1, y0, z1));
double xf11 = MathUtil.lerp(xs, valCoord(seed, x0, y1, z1), valCoord(seed, x1, y1, z1));
double yf0 = MathUtil.lerp(xf00, xf10, ys);
double yf1 = MathUtil.lerp(xf01, xf11, ys);
double yf0 = MathUtil.lerp(ys, xf00, xf10);
double yf1 = MathUtil.lerp(ys, xf01, xf11);
return MathUtil.lerp(yf0, yf1, zs);
return MathUtil.lerp(zs, yf0, yf1);
}
}
@@ -9,7 +9,7 @@ package com.dfsek.terra.addons.ore.ores;
import java.util.BitSet;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
@@ -44,7 +44,7 @@ public class VanillaOre implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
float randomRadian = random.nextFloat() * (float) Math.PI;
double eighthSize = size / 8.0F;
@@ -1,7 +1,7 @@
package com.dfsek.terra.addons.ore.ores;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
@@ -24,7 +24,7 @@ public class VanillaScatteredOre extends VanillaOre {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
int i = random.nextInt((int) (size + 1));
Vector3Int.Mutable mutable = Vector3Int.zero().mutable();
@@ -39,7 +39,7 @@ public class VanillaScatteredOre extends VanillaOre {
return true;
}
private void setPos(Vector3Int.Mutable mutable, Random random, Vector3Int location, int spread) {
private void setPos(Vector3Int.Mutable mutable, RandomGenerator random, Vector3Int location, int spread) {
int x = this.getSpread(random, spread);
int y = this.getSpread(random, spread);
int z = this.getSpread(random, spread);
@@ -48,7 +48,7 @@ public class VanillaScatteredOre extends VanillaOre {
mutable.setZ(location.getZ() + z);
}
private int getSpread(Random random, int spread) {
private int getSpread(RandomGenerator random, int spread) {
return Math.round((random.nextFloat() - random.nextFloat()) * (float) spread);
}
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.ore.utils;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.util.collection.MaterialSet;
@@ -8,13 +8,14 @@ import com.dfsek.terra.api.world.WritableWorld;
public class VanillaOreUtils {
private static boolean shouldExpose(Random random, double exposedChance) {
private static boolean shouldExpose(RandomGenerator random, double exposedChance) {
if(exposedChance >= 1.0F) return true;
if(exposedChance <= 0.0F) return false;
return random.nextFloat() < exposedChance;
}
public static boolean shouldPlace(MaterialSet replaceable, BlockType type, Double exposedChance, Random random, WritableWorld world,
public static boolean shouldPlace(MaterialSet replaceable, BlockType type, Double exposedChance, RandomGenerator random,
WritableWorld world,
int x,
int y, int z) {
if(!replaceable.contains(type)) return false;
@@ -12,7 +12,7 @@ import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.structure.structures.loot.functions.AmountFunction;
import com.dfsek.terra.addons.structure.structures.loot.functions.DamageFunction;
@@ -85,11 +85,11 @@ public class Entry {
/**
* Fetches a single ItemStack from the Entry, applying all functions to it.
*
* @param r The Random instance to apply functions with
* @param r The RandomGenerator instance to apply functions with
*
* @return ItemStack - The ItemStack with all functions applied.
*/
public ItemStack getItem(Random r) {
public ItemStack getItem(RandomGenerator r) {
ItemStack item = this.item.newItemStack(1);
for(LootFunction f : functions) {
item = f.apply(item, r);
@@ -14,7 +14,7 @@ import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.Inventory;
@@ -44,7 +44,7 @@ public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
}
@Override
public void fillInventory(Inventory i, Random r) {
public void fillInventory(Inventory i, RandomGenerator r) {
List<ItemStack> loot = getLoot(r);
for(ItemStack stack : loot) {
int attempts = 0;
@@ -70,7 +70,7 @@ public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
}
@Override
public List<ItemStack> getLoot(Random r) {
public List<ItemStack> getLoot(RandomGenerator r) {
List<ItemStack> itemList = new ArrayList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
@@ -12,7 +12,7 @@ import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -50,13 +50,13 @@ public class Pool {
}
/**
* Fetches a list of items from the pool using the provided Random instance.
* Fetches a list of items from the pool using the provided RandomGenerator instance.
*
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return List&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
public List<ItemStack> getItems(RandomGenerator r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new ArrayList<>();
@@ -8,7 +8,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -35,12 +35,12 @@ public class AmountFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
original.setAmount(r.nextInt(max - min + 1) + min);
return original;
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Damageable;
@@ -36,12 +36,12 @@ public class DamageFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
if(original == null) return null;
if(!original.isDamageable()) return original;
ItemMeta meta = original.getItemMeta();
@@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -41,12 +41,12 @@ public class EnchantFunction implements LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
public ItemStack apply(ItemStack original, RandomGenerator r) {
if(original.getItemMeta() == null) return original;
double enchant = (r.nextDouble() * (max - min)) + min;
@@ -8,7 +8,7 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -21,9 +21,9 @@ public interface LootFunction {
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
ItemStack apply(ItemStack original, Random r);
ItemStack apply(ItemStack original, RandomGenerator r);
}
@@ -8,7 +8,8 @@
package com.dfsek.terra.addons.generation.feature;
import java.util.Collections;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures;
import com.dfsek.terra.api.Platform;
@@ -72,7 +73,9 @@ public class FeatureGenerationStage implements GenerationStage, StringIdentifiab
.forEach(y -> feature.getStructure(world, x, y, z)
.generate(Vector3Int.of(x, y, z),
world,
new Random(coordinateSeed * 31 + y),
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of(
"Xoroshiro128PlusPlus")
.create(coordinateSeed * 31 + y),
Rotation.NONE)
);
}
@@ -11,12 +11,16 @@ import com.dfsek.tectonic.yaml.YamlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.ConfigurationDiscoveryEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.util.FileUtil;
public class YamlAddon implements AddonInitializer {
@@ -33,10 +37,21 @@ public class YamlAddon implements AddonInitializer {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigurationDiscoveryEvent.class)
.then(event -> event.getLoader().open("", ".yml").thenEntries(entries -> entries.forEach(entry -> {
LOGGER.debug("Discovered config {}", entry.getKey());
event.register(entry.getKey(), new YamlConfiguration(entry.getValue(), entry.getKey()));
})).close())
.then(event -> {
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".yml")
.forEach((key, value) -> {
LOGGER.debug("Discovered config {}", key);
try {
event.register(key, new YamlConfiguration(Files.newInputStream(value), key));
} catch(IOException e) {
throw new RuntimeException("Failed to load config " + key, e);
}
});
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.failThrough();
}
}
@@ -60,8 +60,8 @@ public class ImageLibraryAddon implements AddonInitializer {
.then(event -> {
ConfigPack pack = event.getPack();
CheckedRegistry<Supplier<ObjectTemplate<Image>>> imageRegistry = pack.getOrCreateRegistry(IMAGE_REGISTRY_KEY);
imageRegistry.register(addon.key("BITMAP"), () -> new ImageTemplate(pack.getLoader(), pack));
imageRegistry.register(addon.key("STITCHED_BITMAP"), () -> new StitchedImageTemplate(pack.getLoader(), pack));
imageRegistry.register(addon.key("BITMAP"), () -> new ImageTemplate(pack));
imageRegistry.register(addon.key("STITCHED_BITMAP"), () -> new StitchedImageTemplate(pack));
})
.then(event -> {
event.getPack()
@@ -4,8 +4,10 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import javax.imageio.ImageIO;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import com.dfsek.terra.addons.image.config.ImageLibraryPackConfigTemplate;
@@ -13,7 +15,6 @@ import com.dfsek.terra.addons.image.image.BufferedImageWrapper;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.addons.image.image.SuppliedImage;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.generic.Lazy;
@@ -22,13 +23,13 @@ import com.dfsek.terra.api.util.generic.Lazy;
* Cache prevents configs from loading the same image multiple times into memory
*/
record ImageCache(LoadingCache<String, Image> cache) implements Properties {
public static Image load(String path, ConfigPack pack, Loader files) throws IOException {
public static Image load(String path, ConfigPack pack) throws IOException {
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);
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, files)));
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, pack.getRootPath())));
pack.getContext().put(images);
} else images = pack.getContext().get(ImageCache.class);
@@ -45,17 +46,8 @@ record ImageCache(LoadingCache<String, Image> cache) implements Properties {
return images.cache.get(path);
}
private static Image loadImage(String path, Loader files) throws IOException {
try {
return new BufferedImageWrapper(ImageIO.read(files.get(path)));
} catch(IllegalArgumentException e) {
throw new IllegalArgumentException("Unable to load image (image might be too large?)", e);
} catch(IOException e) {
if(e instanceof FileNotFoundException) {
// Rethrow using nicer message
throw new IOException("Unable to load image: No such file or directory: " + path, e);
}
throw new IOException("Unable to load image", e);
}
private static Image loadImage(String path, Path directory) throws IOException {
InputStream is = Files.newInputStream(directory.resolve(path));
return new BufferedImageWrapper(ImageIO.read(is));
}
}
@@ -7,25 +7,22 @@ import java.io.IOException;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
public class ImageTemplate implements ObjectTemplate<Image> {
private final Loader files;
private final ConfigPack pack;
@Value("path")
private String path;
public ImageTemplate(Loader files, ConfigPack pack) {
this.files = files;
public ImageTemplate(ConfigPack pack) {
this.pack = pack;
}
@Override
public Image get() {
try {
return ImageCache.load(path, pack, files);
return ImageCache.load(path, pack);
} catch(IOException e) {
throw new RuntimeException(e);
}
@@ -11,12 +11,10 @@ import java.io.IOException;
import com.dfsek.terra.addons.image.image.Image;
import com.dfsek.terra.addons.image.image.StitchedImage;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedConfigTemplate {
private final Loader files;
private final ConfigPack pack;
@Value("path-format")
private String path;
@@ -28,8 +26,7 @@ public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedCo
@Default
private boolean zeroIndexed = false;
public StitchedImageTemplate(Loader files, ConfigPack pack) {
this.files = files;
public StitchedImageTemplate(ConfigPack pack) {
this.pack = pack;
}
@@ -39,7 +36,7 @@ public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedCo
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
try {
grid[i][j] = ImageCache.load(getFormattedPath(i, j), pack, files);
grid[i][j] = ImageCache.load(getFormattedPath(i, j), pack);
} catch(IOException e) {
throw new RuntimeException(e);
}
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.palette.shortcut.block;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.structure.Structure;
@@ -17,7 +17,7 @@ public class SingletonStructure implements Structure {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
world.setBlockState(location, blockState);
return true;
}
@@ -1,7 +1,5 @@
package com.dfsek.terra.addons.structure.mutator;
import java.util.Random;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.structure.Structure;
@@ -11,6 +9,8 @@ import com.dfsek.terra.api.world.WritableWorld;
import com.dfsek.terra.api.world.util.ReadInterceptor;
import com.dfsek.terra.api.world.util.WriteInterceptor;
import java.util.random.RandomGenerator;
public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
private final RegistryKey key;
@@ -32,7 +32,7 @@ public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
return base.generate(location,
world
.buffer()
@@ -16,6 +16,7 @@ import net.querz.nbt.tag.Tag;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
@@ -29,7 +30,7 @@ import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.StringUtil;
import com.dfsek.terra.api.util.FileUtil;
import com.dfsek.terra.api.util.vector.Vector3Int;
@@ -58,13 +59,21 @@ public class SpongeSchematicAddon implements AddonInitializer {
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Structure> structureRegistry = event.getPack().getOrCreateRegistry(Structure.class);
event.getPack()
.getLoader()
.open("", ".schem")
.thenEntries(entries -> entries
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".schem")
.entrySet()
.stream()
.map(entry -> convert(entry.getValue(), StringUtil.fileName(entry.getKey())))
.forEach(structureRegistry::register)).close();
.map(entry -> {
try {
return convert(Files.newInputStream(entry.getValue()), FileUtil.fileName(entry.getKey()));
} catch(IOException e) {
throw new RuntimeException("Failed to load config " + entry.getKey(), e);
}
})
.forEach(structureRegistry::register);
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.failThrough();
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.addons.sponge;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.registry.key.Keyed;
@@ -36,7 +36,7 @@ public class SpongeStructure implements Structure, Keyed<SpongeStructure> {
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
int bX = location.getX();
int bY = location.getY();
int bZ = location.getZ();
@@ -7,6 +7,9 @@
package com.dfsek.terra.addons.terrascript;
import java.io.IOException;
import java.nio.file.Files;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
@@ -19,7 +22,7 @@ import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.LootTable;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.StringUtil;
import com.dfsek.terra.api.util.FileUtil;
public class TerraScriptAddon implements AddonInitializer {
@@ -37,26 +40,28 @@ public class TerraScriptAddon implements AddonInitializer {
.then(event -> {
CheckedRegistry<Structure> structureRegistry = event.getPack().getOrCreateRegistry(Structure.class);
CheckedRegistry<LootTable> lootRegistry = event.getPack().getOrCreateRegistry(LootTable.class);
event.getPack().getLoader().open("", ".tesf").thenEntries(
entries ->
entries.stream()
.parallel()
.map(entry -> {
try {
String id = StringUtil.fileName(entry.getKey());
return new StructureScript(entry.getValue(),
addon.key(id),
platform,
structureRegistry,
lootRegistry,
event.getPack().getOrCreateRegistry(FunctionBuilder.class));
} catch(ParseException e) {
throw new RuntimeException("Failed to load script \"" + entry.getKey() + "\"", e);
}
})
.toList()
.forEach(structureRegistry::register))
.close();
try {
FileUtil.filesWithExtension(event.getPack().getRootPath(), ".tesf")
.entrySet()
.stream()
.parallel()
.map(entry -> {
try {
String id = FileUtil.fileName(entry.getKey());
return new StructureScript(Files.newInputStream(entry.getValue()),
addon.key(id),
platform,
structureRegistry,
lootRegistry,
event.getPack().getOrCreateRegistry(FunctionBuilder.class));
} catch(ParseException | IOException e) {
throw new RuntimeException("Failed to load script \"" + entry.getKey() + "\"", e);
}
})
.forEach(structureRegistry::register);
} catch(IOException e) {
throw new RuntimeException("Error occurred while reading config pack files", e);
}
})
.priority(100)
.failThrough();
@@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.Parser;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable;
@@ -131,14 +131,14 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
@Override
@SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0));
platform.getProfiler().pop(profile);
return result;
}
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) {
public boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation, int recursions) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions));
platform.getProfiler().pop(profile);
@@ -9,7 +9,7 @@ package com.dfsek.terra.addons.terrascript.script;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.api.util.Rotation;
@@ -20,14 +20,14 @@ import com.dfsek.terra.api.world.WritableWorld;
public class TerraImplementationArguments implements ImplementationArguments {
private final Rotation rotation;
private final Random random;
private final RandomGenerator random;
private final WritableWorld world;
private final Map<Vector3, String> marks = new HashMap<>();
private final int recursions;
private final Vector3Int origin;
private boolean waterlog = false;
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, Random random, WritableWorld world, int recursions) {
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, RandomGenerator random, WritableWorld world, int recursions) {
this.rotation = rotation;
this.random = random;
this.world = world;
@@ -39,7 +39,7 @@ public class TerraImplementationArguments implements ImplementationArguments {
return recursions;
}
public Random getRandom() {
public RandomGenerator getRandom() {
return random;
}
@@ -10,7 +10,8 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
@@ -84,7 +85,8 @@ public class LootFunction implements Function<Void> {
if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(),
new Random(apply.hashCode()));
RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of(
"Xoroshiro128PlusPlus").create(apply.hashCode()));
data.update(false);
} catch(Exception e) {
LOGGER.error("Could not apply loot at {}", apply, e);
@@ -14,6 +14,7 @@ import java.io.File;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.config.PluginConfig;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.handle.ItemHandle;
@@ -61,6 +62,10 @@ public interface Platform extends LoaderRegistrar {
@Contract(pure = true)
CheckedRegistry<ConfigPack> getConfigRegistry();
@NotNull
@Contract(pure = true)
CheckedRegistry<MetaPack> getMetaConfigRegistry();
@NotNull
@Contract(pure = true)
Registry<BaseAddon> getAddons();
@@ -10,6 +10,7 @@ package com.dfsek.terra.api.config;
import ca.solostudios.strata.version.Version;
import ca.solostudios.strata.version.VersionRange;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@@ -43,7 +44,7 @@ public interface ConfigPack extends LoaderRegistrar,
List<GenerationStage> getStages();
Loader getLoader();
Path getRootPath();
String getAuthor();
@@ -1,47 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the common/api directory.
*/
package com.dfsek.terra.api.config;
import com.dfsek.tectonic.api.exception.ConfigException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
public interface Loader {
Loader thenNames(Consumer<List<String>> consumer) throws ConfigException;
Loader thenEntries(Consumer<Set<Map.Entry<String, InputStream>>> consumer) throws ConfigException;
/**
* Get a single file from this Loader.
*
* @param singleFile File to get
*
* @return InputStream from file.
*/
InputStream get(String singleFile) throws IOException;
/**
* Open a subdirectory.
*
* @param directory Directory to open
* @param extension File extension
*/
Loader open(String directory, String extension);
/**
* Close all InputStreams opened.
*/
Loader close();
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.api.config;
import ca.solostudios.strata.version.Version;
import java.util.Map;
import com.dfsek.terra.api.properties.PropertyHolder;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.meta.CheckedRegistryHolder;
import com.dfsek.terra.api.registry.meta.RegistryProvider;
import com.dfsek.terra.api.tectonic.ConfigLoadingDelegate;
import com.dfsek.terra.api.tectonic.LoaderRegistrar;
public interface MetaPack extends LoaderRegistrar,
ConfigLoadingDelegate,
CheckedRegistryHolder,
RegistryProvider,
Keyed<MetaPack>,
PropertyHolder {
Map<String, ConfigPack> packs();
String getAuthor();
Version getVersion();
}
@@ -12,7 +12,6 @@ import com.dfsek.tectonic.api.config.Configuration;
import java.util.function.BiConsumer;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.event.events.FailThroughEvent;
import com.dfsek.terra.api.event.events.PackEvent;
@@ -25,13 +24,11 @@ import com.dfsek.terra.api.event.events.PackEvent;
*/
public class ConfigurationDiscoveryEvent implements PackEvent, FailThroughEvent {
private final ConfigPack pack;
private final Loader loader;
private final BiConsumer<String, Configuration> consumer;
public ConfigurationDiscoveryEvent(ConfigPack pack, Loader loader, BiConsumer<String, Configuration> consumer) {
public ConfigurationDiscoveryEvent(ConfigPack pack, BiConsumer<String, Configuration> consumer) {
this.pack = pack;
this.loader = loader;
this.consumer = consumer;
}
@@ -43,8 +40,4 @@ public class ConfigurationDiscoveryEvent implements PackEvent, FailThroughEvent
public ConfigPack getPack() {
return pack;
}
public Loader getLoader() {
return loader;
}
}
@@ -10,7 +10,7 @@ package com.dfsek.terra.api.structure;
import org.jetbrains.annotations.ApiStatus.Experimental;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.inventory.Inventory;
import com.dfsek.terra.api.inventory.ItemStack;
@@ -22,16 +22,16 @@ public interface LootTable {
* Fills an Inventory with loot.
*
* @param i The Inventory to fill.
* @param r The The Random instance to use.
* @param r The The RandomGenerator instance to use.
*/
void fillInventory(Inventory i, Random r);
void fillInventory(Inventory i, RandomGenerator r);
/**
* Fetches a list of ItemStacks from the loot table using the given Random instance.
* Fetches a list of ItemStacks from the loot table using the given RandomGenerator instance.
*
* @param r The Random instance to use.
* @param r The RandomGenerator instance to use.
*
* @return List&lt;ItemStack&gt; - The list of loot fetched.
*/
List<ItemStack> getLoot(Random r);
List<ItemStack> getLoot(RandomGenerator r);
}
@@ -7,7 +7,7 @@
package com.dfsek.terra.api.structure;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.vector.Vector3Int;
@@ -15,5 +15,5 @@ import com.dfsek.terra.api.world.WritableWorld;
public interface Structure {
boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation);
boolean generate(Vector3Int location, WritableWorld world, RandomGenerator random, Rotation rotation);
}
@@ -10,7 +10,7 @@ package com.dfsek.terra.api.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
import java.util.random.RandomGenerator;
public class ConstantRange implements Range {
@@ -36,7 +36,7 @@ public class ConstantRange implements Range {
}
@Override
public int get(Random r) {
public int get(RandomGenerator r) {
return r.nextInt(min, max);
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.function.Function.identity;
public class FileUtil {
public static Map<String, Path> filesWithExtension(Path start, String... extensions) throws IOException {
if(Files.notExists(start) || !Files.isDirectory(start)) return Collections.emptyMap();
try(Stream<Path> paths = Files.walk(start)) {
return paths
.filter(Files::isRegularFile)
.filter(p -> Arrays.stream(extensions).anyMatch(e -> p.getFileName().toString().endsWith(e)))
.collect(Collectors.toMap(p -> start.relativize(p).toString(), identity()));
}
}
public static String fileName(String path) {
if(path.contains(File.separator)) {
return path.substring(path.lastIndexOf(File.separatorChar) + 1, path.lastIndexOf('.'));
} else if(path.contains("/")) {
return path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf('.'));
} else if(path.contains(".")) {
return path.substring(0, path.lastIndexOf('.'));
} else {
return path;
}
}
}
@@ -7,18 +7,20 @@
package com.dfsek.terra.api.util;
import java.util.Random;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.world.chunk.Chunk;
public final class PopulationUtil {
public static Random getRandom(Chunk c) {
public static RandomGenerator getRandom(Chunk c) {
return getRandom(c, 0);
}
public static Random getRandom(Chunk c, long salt) {
return new Random(getCarverChunkSeed(c.getX(), c.getZ(), c.getWorld().getSeed() + salt));
public static RandomGenerator getRandom(Chunk c, long salt) {
return RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(
getCarverChunkSeed(c.getX(), c.getZ(), c.getWorld().getSeed() + salt));
}
/**
@@ -31,7 +33,7 @@ public final class PopulationUtil {
* @return long - The carver seed.
*/
public static long getCarverChunkSeed(int chunkX, int chunkZ, long seed) {
Random r = new Random(seed);
RandomGenerator r = RandomGeneratorFactory.<RandomGenerator.SplittableGenerator>of("Xoroshiro128PlusPlus").create(seed);
return chunkX * r.nextLong() ^ chunkZ * r.nextLong() ^ seed;
}
}
@@ -10,8 +10,8 @@ package com.dfsek.terra.api.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
import java.util.function.Supplier;
import java.util.random.RandomGenerator;
public interface Range extends Iterable<Integer> {
@@ -19,7 +19,7 @@ public interface Range extends Iterable<Integer> {
Range reflect(int pt);
int get(Random r);
int get(RandomGenerator r);
Range intersects(Range other);
@@ -1,25 +0,0 @@
/*
* Copyright (c) 2020-2023 Polyhedral Development
*
* The Terra API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the common/api directory.
*/
package com.dfsek.terra.api.util;
import java.io.File;
public class StringUtil {
public static String fileName(String path) {
if(path.contains(File.separator)) {
return path.substring(path.lastIndexOf(File.separatorChar) + 1, path.lastIndexOf('.'));
} else if(path.contains("/")) {
return path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf('.'));
} else if(path.contains(".")) {
return path.substring(0, path.lastIndexOf('.'));
} else {
return path;
}
}
}
@@ -15,9 +15,9 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
@@ -43,7 +43,7 @@ public class ProbabilityCollection<E> implements Collection<E> {
}
@SuppressWarnings("unchecked")
public E get(Random r) {
public E get(RandomGenerator r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
@@ -197,7 +197,7 @@ public class ProbabilityCollection<E> implements Collection<E> {
}
@Override
public T get(Random r) {
public T get(RandomGenerator r) {
return single;
}
@@ -51,6 +51,7 @@ import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.addon.bootstrap.BootstrapAddonClassLoader;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.config.PluginConfig;
import com.dfsek.terra.api.event.EventManager;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
@@ -72,6 +73,8 @@ import com.dfsek.terra.registry.CheckedRegistryImpl;
import com.dfsek.terra.registry.LockedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
import com.dfsek.terra.registry.master.MetaConfigRegistry;
/**
@@ -85,9 +88,12 @@ public abstract class AbstractPlatform implements Platform {
private static final MutableBoolean LOADED = new MutableBoolean(false);
private final EventManager eventManager = new EventManagerImpl();
private final ConfigRegistry configRegistry = new ConfigRegistry();
private final MetaConfigRegistry metaConfigRegistry = new MetaConfigRegistry();
private final CheckedRegistry<ConfigPack> checkedConfigRegistry = new CheckedRegistryImpl<>(configRegistry);
private final CheckedRegistry<MetaPack> checkedMetaConfigRegistry = new CheckedRegistryImpl<>(metaConfigRegistry);
private final Profiler profiler = new ProfilerImpl();
private final GenericLoaders loaders = new GenericLoaders(this);
@@ -102,6 +108,10 @@ public abstract class AbstractPlatform implements Platform {
return configRegistry;
}
public MetaConfigRegistry getRawMetaConfigRegistry() {
return metaConfigRegistry;
}
protected Iterable<BaseAddon> platformAddon() {
return Collections.emptySet();
}
@@ -147,11 +157,12 @@ public abstract class AbstractPlatform implements Platform {
eventManager.getHandler(FunctionalEventHandler.class)
.register(internalAddon, PlatformInitializationEvent.class)
.then(event -> {
logger.info("Loading config packs...");
configRegistry.loadAll(this);
logger.info("Loaded packs.");
})
.then(event -> loadConfigPacks())
.global();
eventManager.getHandler(FunctionalEventHandler.class)
.register(internalAddon, PlatformInitializationEvent.class)
.then(event -> loadMetaConfigPacks())
.global();
@@ -159,6 +170,38 @@ public abstract class AbstractPlatform implements Platform {
logger.info("Finished initialization.");
}
protected boolean loadConfigPacks() {
logger.info("Loading config packs...");
ConfigRegistry configRegistry = getRawConfigRegistry();
configRegistry.clear();
try {
configRegistry.loadAll(this);
} catch(IOException e) {
logger.error("Failed to load config packs", e);
return false;
} catch(PackLoadFailuresException e) {
e.getExceptions().forEach(ex -> logger.error("Failed to load config pack", ex));
return false;
}
return true;
}
protected boolean loadMetaConfigPacks() {
logger.info("Loading meta config packs...");
MetaConfigRegistry metaConfigRegistry = getRawMetaConfigRegistry();
metaConfigRegistry.clear();
try {
metaConfigRegistry.loadAll(this, configRegistry);
} catch(IOException e) {
logger.error("Failed to load meta config packs", e);
return false;
} catch(PackLoadFailuresException e) {
e.getExceptions().forEach(ex -> logger.error("Failed to meta load config pack", ex));
return false;
}
return true;
}
protected InternalAddon loadAddons() {
List<BaseAddon> addonList = new ArrayList<>();
@@ -325,6 +368,12 @@ public abstract class AbstractPlatform implements Platform {
return checkedConfigRegistry;
}
@Override
public @NotNull CheckedRegistry<MetaPack> getMetaConfigRegistry() {
return checkedMetaConfigRegistry;
}
@Override
public @NotNull Registry<BaseAddon> getAddons() {
return lockedAddonRegistry;
@@ -1,66 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* Load all {@code *.yml} files from a {@link java.nio.file.Path}.
*/
public class FolderLoader extends LoaderImpl {
private static final Logger logger = LoggerFactory.getLogger(FolderLoader.class);
private final Path path;
public FolderLoader(Path path) {
this.path = path;
}
@Override
public InputStream get(String singleFile) throws IOException {
return new FileInputStream(new File(path.toFile(), singleFile));
}
protected void load(String directory, String extension) {
File newPath = new File(path.toFile(), directory);
newPath.mkdirs();
try(Stream<Path> paths = Files.walk(newPath.toPath())) {
paths.filter(Files::isRegularFile).filter(file -> file.toString().toLowerCase().endsWith(extension)).forEach(file -> {
try {
String rel = newPath.toPath().relativize(file).toString();
streams.put(rel, new FileInputStream(file.toFile()));
} catch(FileNotFoundException e) {
logger.error("Could not find file to load", e);
}
});
} catch(IOException e) {
logger.error("Error while loading files", e);
}
}
}
@@ -1,83 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import com.dfsek.tectonic.api.exception.ConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import com.dfsek.terra.api.config.Loader;
public abstract class LoaderImpl implements Loader {
private static final Logger logger = LoggerFactory.getLogger(LoaderImpl.class);
protected final Map<String, InputStream> streams = new HashMap<>();
@Override
public Loader thenNames(Consumer<List<String>> consumer) throws ConfigException {
consumer.accept(new ArrayList<>(streams.keySet()));
return this;
}
@Override
public Loader thenEntries(Consumer<Set<Map.Entry<String, InputStream>>> consumer) throws ConfigException {
consumer.accept(streams.entrySet());
return this;
}
/**
* Open a subdirectory.
*
* @param directory Directory to open
* @param extension File extension
*/
@Override
public LoaderImpl open(String directory, String extension) {
if(!streams.isEmpty()) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams");
load(directory, extension);
return this;
}
/**
* Close all InputStreams opened.
*/
@Override
public Loader close() {
streams.forEach((name, input) -> {
try {
input.close();
} catch(IOException e) {
logger.error("Error occurred while loading", e);
}
});
streams.clear();
return this;
}
protected abstract void load(String directory, String extension);
}
@@ -1,63 +0,0 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.config.fileloaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ZIPLoader extends LoaderImpl {
private static final Logger logger = LoggerFactory.getLogger(ZIPLoader.class);
private final ZipFile file;
public ZIPLoader(ZipFile file) {
this.file = file;
}
@Override
public InputStream get(String singleFile) throws IOException {
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(!entry.isDirectory() && entry.getName().equals(singleFile)) return file.getInputStream(entry);
}
throw new IllegalArgumentException("No such file: " + singleFile);
}
protected void load(String directory, String extension) {
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(!entry.isDirectory() && entry.getName().startsWith(directory) && entry.getName().endsWith(extension)) {
try {
String rel = entry.getName().substring(directory.length());
streams.put(rel, file.getInputStream(entry));
} catch(IOException e) {
logger.error("Error while loading file from zip", e);
}
}
}
}
}
@@ -27,10 +27,10 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.AnnotatedType;
import java.nio.file.Files;
import java.util.concurrent.ConcurrentHashMap;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
@@ -39,12 +39,10 @@ import com.dfsek.terra.api.properties.Properties;
*/
@Deprecated
public class BufferedImageLoader implements TypeLoader<BufferedImage> {
private final Loader files;
private final ConfigPack pack;
public BufferedImageLoader(Loader files, ConfigPack pack) {
this.files = files;
public BufferedImageLoader(ConfigPack pack) {
this.pack = pack;
if(!pack.getContext().has(ImageCache.class))
pack.getContext().put(new ImageCache(new ConcurrentHashMap<>()));
@@ -55,7 +53,7 @@ public class BufferedImageLoader implements TypeLoader<BufferedImage> {
throws LoadException {
return pack.getContext().get(ImageCache.class).map.computeIfAbsent((String) c, s -> {
try {
return ImageIO.read(files.get(s));
return ImageIO.read(Files.newInputStream(pack.getRootPath().resolve(s)));
} catch(IOException e) {
throw new LoadException("Unable to load image", e, depthTracker);
}
@@ -33,15 +33,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -51,15 +51,12 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.ConfigType;
import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.event.events.config.ConfigurationDiscoveryEvent;
import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent;
@@ -72,15 +69,12 @@ import com.dfsek.terra.api.registry.OpenRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.tectonic.ShortcutLoader;
import com.dfsek.terra.api.util.generic.Construct;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import com.dfsek.terra.api.world.chunk.generation.util.provider.ChunkGeneratorProvider;
import com.dfsek.terra.config.fileloaders.FolderLoader;
import com.dfsek.terra.config.fileloaders.ZIPLoader;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.config.loaders.config.BufferedImageLoader;
import com.dfsek.terra.config.preprocessor.MetaListLikePreprocessor;
@@ -107,7 +101,7 @@ public class ConfigPackImpl implements ConfigPack {
private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader();
private final ConfigLoader selfLoader = new ConfigLoader();
private final Platform platform;
private final Loader loader;
private final Path rootPath;
private final Map<BaseAddon, VersionRange> addons;
@@ -121,40 +115,29 @@ public class ConfigPackImpl implements ConfigPack {
private final RegistryKey key;
public ConfigPackImpl(File folder, Platform platform) {
this(new FolderLoader(folder.toPath()), Construct.construct(() -> {
try {
return new YamlConfiguration(new FileInputStream(new File(folder, "pack.yml")), "pack.yml");
} catch(FileNotFoundException e) {
throw new UncheckedIOException("No pack.yml file found in " + folder.getAbsolutePath(), e);
}
}), platform);
}
public ConfigPackImpl(ZipFile file, Platform platform) {
this(new ZIPLoader(file), Construct.construct(() -> {
ZipEntry pack = null;
Enumeration<? extends ZipEntry> entries = file.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if(entry.getName().equals("pack.yml")) pack = entry;
}
if(pack == null) throw new IllegalArgumentException("No pack.yml file found in " + file.getName());
try {
return new YamlConfiguration(file.getInputStream(pack), "pack.yml");
} catch(IOException e) {
throw new UncheckedIOException("Unable to load pack.yml from ZIP file", e);
}
}), platform);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private ConfigPackImpl(Loader loader, Configuration packManifest, Platform platform) {
@SuppressWarnings({ "rawtypes" })
public ConfigPackImpl(Path path, Platform platform) throws IOException {
long start = System.nanoTime();
this.loader = loader;
if(Files.notExists(path)) throw new FileNotFoundException("Could not load config pack, " + path + " does not exist");
if(Files.isDirectory(path)) {
this.rootPath = path;
} else if(Files.isRegularFile(path)) {
if(!path.getFileName().toString().endsWith(".zip")) {
throw new IOException("Could not load config pack, file " + path + " is not a zip");
}
FileSystem zipfs = FileSystems.newFileSystem(path);
this.rootPath = zipfs.getPath("/");
} else {
throw new IOException("Could not load config pack from " + path);
}
Path packManifestPath = rootPath.resolve("pack.yml");
if(Files.notExists(packManifestPath)) throw new IOException("No pack.yml found in " + path);
Configuration packManifest = new YamlConfiguration(Files.newInputStream(packManifestPath),
packManifestPath.getFileName().toString());
this.platform = platform;
this.configTypeRegistry = createConfigRegistry();
@@ -238,7 +221,7 @@ public class ConfigPackImpl implements ConfigPack {
private Map<String, Configuration> discoverConfigurations() {
Map<String, Configuration> configurations = new HashMap<>();
platform.getEventManager().callEvent(new ConfigurationDiscoveryEvent(this, loader,
platform.getEventManager().callEvent(new ConfigurationDiscoveryEvent(this,
(s, c) -> configurations.put(s.replace("\\", "/"),
c))); // Create all the configs.
return configurations;
@@ -283,7 +266,7 @@ public class ConfigPackImpl implements ConfigPack {
@Override
public void register(TypeRegistry registry) {
registry.registerLoader(ConfigType.class, configTypeRegistry)
.registerLoader(BufferedImage.class, new BufferedImageLoader(loader, this));
.registerLoader(BufferedImage.class, new BufferedImageLoader(this));
registryMap.forEach(registry::registerLoader);
shortcuts.forEach(registry::registerLoader); // overwrite with delegated shortcuts if present
}
@@ -309,7 +292,6 @@ public class ConfigPackImpl implements ConfigPack {
return seededBiomeProvider;
}
@SuppressWarnings("unchecked")
@Override
public <T> CheckedRegistry<T> getOrCreateRegistry(TypeKey<T> typeKey) {
return (CheckedRegistry<T>) registryMap.computeIfAbsent(typeKey.getType(), c -> {
@@ -348,8 +330,8 @@ public class ConfigPackImpl implements ConfigPack {
}
@Override
public Loader getLoader() {
return loader;
public Path getRootPath() {
return rootPath;
}
@Override
@@ -362,7 +344,7 @@ public class ConfigPackImpl implements ConfigPack {
return template.getVersion();
}
@SuppressWarnings("unchecked,rawtypes")
@SuppressWarnings("rawtypes")
@Override
public <T> ConfigPack registerShortcut(TypeKey<T> clazz, String shortcut, ShortcutLoader<T> loader) {
ShortcutHolder<?> holder = shortcuts
@@ -406,12 +388,10 @@ public class ConfigPackImpl implements ConfigPack {
}
@Override
@SuppressWarnings("unchecked")
public <T> CheckedRegistry<T> getRegistry(Type type) {
return (CheckedRegistry<T>) registryMap.get(type);
}
@SuppressWarnings("unchecked")
@Override
public <T> CheckedRegistry<T> getCheckedRegistry(Type type) throws IllegalStateException {
return (CheckedRegistry<T>) registryMap.get(type);
@@ -0,0 +1,215 @@
package com.dfsek.terra.config.pack;
import ca.solostudios.strata.version.Version;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.config.Configuration;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.tectonic.api.loader.AbstractConfigLoader;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.tectonic.yaml.YamlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.properties.Context;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.OpenRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
import com.dfsek.terra.registry.CheckedRegistryImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry;
public class MetaPackImpl implements MetaPack {
private static final Pattern PATTERN = Pattern.compile(", ");
private static final Logger logger = LoggerFactory.getLogger(MetaPackImpl.class);
private final MetaPackTemplate template = new MetaPackTemplate();
private final Platform platform;
private final Path rootPath;
private final Map<String, ConfigPack> packs = new HashMap<>();
private final ConfigLoader selfLoader = new ConfigLoader();
private final Context context = new Context();
private final RegistryKey key;
private final Map<Type, CheckedRegistryImpl<?>> registryMap = new HashMap<>();
private final AbstractConfigLoader abstractConfigLoader = new AbstractConfigLoader();
private final String author;
public MetaPackImpl(Path path, Platform platform, ConfigRegistry configRegistry) throws IOException {
long start = System.nanoTime();
if(Files.notExists(path)) throw new FileNotFoundException("Could not load metapack, " + path + " does not exist");
if(Files.isDirectory(path)) {
this.rootPath = path;
} else if(Files.isRegularFile(path)) {
if(!path.getFileName().toString().endsWith(".zip")) {
throw new IOException("Could not load metapack, file " + path + " is not a zip");
}
FileSystem zipfs = FileSystems.newFileSystem(path);
this.rootPath = zipfs.getPath("/");
} else {
throw new IOException("Could not load metapack from " + path);
}
Path packManifestPath = rootPath.resolve("metapack.yml");
if(Files.notExists(packManifestPath)) throw new IOException("No metapack.yml found in " + path);
Configuration packManifest = new YamlConfiguration(Files.newInputStream(packManifestPath),
packManifestPath.getFileName().toString());
this.platform = platform;
register(selfLoader);
platform.register(selfLoader);
register(abstractConfigLoader);
platform.register(abstractConfigLoader);
selfLoader.load(template, packManifest);
String namespace;
String id;
if(template.getID().contains(":")) {
namespace = template.getID().substring(0, template.getID().indexOf(":"));
id = template.getID().substring(template.getID().indexOf(":") + 1);
} else {
id = template.getID();
namespace = template.getID();
}
this.key = RegistryKey.of(namespace, id);
logger.info("Loading metapack \"{}:{}\"", id, namespace);
template.getPacks().forEach((k, v) -> {
RegistryKey registryKey = RegistryKey.parse(v);
if(configRegistry.contains(registryKey)) {
packs.put(k, configRegistry.get(registryKey).get());
logger.info("Linked config pack \"{}\" to metapack \"{}:{}\".", v, namespace, id);
} else {
logger.warn("Failed to link config pack \"{}\" to metapack \"{}:{}\".", v, namespace, id);
}
});
HashSet<String> authors = new HashSet<>();
packs.forEach((k, v) -> {
authors.addAll(Arrays.asList(PATTERN.split(v.getAuthor())));
});
authors.addAll(Arrays.asList(PATTERN.split(template.getAuthor())));
this.author = String.join(", ", authors);
logger.info("Loaded metapack \"{}:{}\" v{} by {} in {}ms.",
namespace, id, getVersion().getFormatted(), author, (System.nanoTime() - start) / 1000000.0D);
}
@Override
public String getAuthor() {
return author;
}
@Override
public Version getVersion() {
return template.getVersion();
}
@Override
public Map<String, ConfigPack> packs() {
return packs;
}
@Override
public Context getContext() {
return context;
}
@Override
public RegistryKey getRegistryKey() {
return key;
}
@Override
public <T> CheckedRegistry<T> getRegistry(Type type) {
return (CheckedRegistry<T>) registryMap.get(type);
}
@Override
public <T> CheckedRegistry<T> getCheckedRegistry(Type type) throws IllegalStateException {
return (CheckedRegistry<T>) registryMap.get(type);
}
@Override
public <T> CheckedRegistry<T> getOrCreateRegistry(TypeKey<T> typeKey) {
return (CheckedRegistry<T>) registryMap.computeIfAbsent(typeKey.getType(), c -> {
OpenRegistry<T> registry = new OpenRegistryImpl<>(typeKey);
selfLoader.registerLoader(c, registry);
abstractConfigLoader.registerLoader(c, registry);
logger.debug("Registered loader for registry of class {}", ReflectionUtil.typeToString(c));
if(typeKey.getType() instanceof ParameterizedType param) {
Type base = param.getRawType();
if(base instanceof Class // should always be true but we'll check anyways
&& Supplier.class.isAssignableFrom((Class<?>) base)) { // If it's a supplier
Type supplied = param.getActualTypeArguments()[0]; // Grab the supplied type
if(supplied instanceof ParameterizedType suppliedParam) {
Type suppliedBase = suppliedParam.getRawType();
if(suppliedBase instanceof Class // should always be true but we'll check anyways
&& ObjectTemplate.class.isAssignableFrom((Class<?>) suppliedBase)) {
Type templateType = suppliedParam.getActualTypeArguments()[0];
GenericTemplateSupplierLoader<?> loader = new GenericTemplateSupplierLoader<>(
(Registry<Supplier<ObjectTemplate<Supplier<ObjectTemplate<?>>>>>) registry);
selfLoader.registerLoader(templateType, loader);
abstractConfigLoader.registerLoader(templateType, loader);
logger.debug("Registered template loader for registry of class {}", ReflectionUtil.typeToString(templateType));
}
}
}
}
return new CheckedRegistryImpl<>(registry);
});
}
@Override
public <T> MetaPackImpl applyLoader(Type type, TypeLoader<T> loader) {
abstractConfigLoader.registerLoader(type, loader);
selfLoader.registerLoader(type, loader);
return this;
}
@Override
public <T> MetaPackImpl applyLoader(Type type, Supplier<ObjectTemplate<T>> loader) {
abstractConfigLoader.registerLoader(type, loader);
selfLoader.registerLoader(type, loader);
return this;
}
@Override
public void register(TypeRegistry registry) {
registryMap.forEach(registry::registerLoader);
}
}
@@ -0,0 +1,41 @@
package com.dfsek.terra.config.pack;
import ca.solostudios.strata.version.Version;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class MetaPackTemplate implements ConfigTemplate {
@Value("id")
private String id;
@Value("author")
@Default
private String author = "Anon Y. Mous";
@Value("version")
private Version version;
@Value("packs")
private Map<String, String> packs;
public String getID() {
return id;
}
public String getAuthor() {
return author;
}
public Version getVersion() {
return version;
}
public Map<String, String> getPacks() {
return packs;
}
}
@@ -17,14 +17,13 @@
package com.dfsek.terra.registry.master;
import com.dfsek.tectonic.api.exception.ConfigException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.zip.ZipFile;
import java.io.Serial;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigPack;
@@ -37,44 +36,42 @@ import com.dfsek.terra.registry.OpenRegistryImpl;
* Class to hold config packs
*/
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
private static final Logger logger = LoggerFactory.getLogger(ConfigRegistry.class);
public ConfigRegistry() {
super(TypeKey.of(ConfigPack.class));
}
public void load(File folder, Platform platform) throws ConfigException {
ConfigPack pack = new ConfigPackImpl(folder, platform);
registerChecked(pack.getRegistryKey(), pack);
public void loadAll(Platform platform) throws IOException, PackLoadFailuresException {
Path packsDirectory = platform.getDataFolder().toPath().resolve("packs");
Files.createDirectories(packsDirectory);
List<IOException> failedLoads = new ArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
try {
ConfigPack pack = new ConfigPackImpl(path, platform);
registerChecked(pack.getRegistryKey(), pack);
} catch(IOException e) {
failedLoads.add(e);
}
});
}
if(!failedLoads.isEmpty()) {
throw new PackLoadFailuresException(failedLoads);
}
}
public boolean loadAll(Platform platform) {
boolean valid = true;
File packsFolder = new File(platform.getDataFolder(), "packs");
packsFolder.mkdirs();
for(File dir : Objects.requireNonNull(packsFolder.listFiles(File::isDirectory))) {
try {
load(dir, platform);
} catch(ConfigException e) {
logger.error("Error loading config pack {}", dir.getName(), e);
valid = false;
}
}
for(File zip : Objects.requireNonNull(
packsFolder.listFiles(file -> file.getName().endsWith(".zip") || file.getName().endsWith(".terra")))) {
try {
logger.info("Loading ZIP archive: {}", zip.getName());
load(new ZipFile(zip), platform);
} catch(IOException | ConfigException e) {
logger.error("Error loading config pack {}", zip.getName(), e);
valid = false;
}
}
return valid;
}
public static class PackLoadFailuresException extends Exception {
@Serial
private static final long serialVersionUID = 538998844645186306L;
public void load(ZipFile file, Platform platform) throws ConfigException {
ConfigPackImpl pack = new ConfigPackImpl(file, platform);
registerChecked(pack.getRegistryKey(), pack);
private final List<Throwable> exceptions;
public PackLoadFailuresException(List<? extends Throwable> exceptions) {
this.exceptions = (List<Throwable>) exceptions;
}
public List<Throwable> getExceptions() {
return exceptions;
}
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of Terra.
*
* Terra is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Terra is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
*/
package com.dfsek.terra.registry.master;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.pack.MetaPackImpl;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
/**
* Class to hold config packs
*/
public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> {
public MetaConfigRegistry() {
super(TypeKey.of(MetaPack.class));
}
public void loadAll(Platform platform, ConfigRegistry configRegistry) throws IOException, PackLoadFailuresException {
Path packsDirectory = platform.getDataFolder().toPath().resolve("metapacks");
Files.createDirectories(packsDirectory);
List<IOException> failedLoads = new ArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
try {
MetaPack pack = new MetaPackImpl(path, platform, configRegistry);
registerChecked(pack.getRegistryKey(), pack);
} catch(IOException e) {
failedLoads.add(e);
}
});
}
if(!failedLoads.isEmpty()) {
throw new PackLoadFailuresException(failedLoads);
}
}
}
@@ -63,8 +63,7 @@ public class PlatformImpl extends AbstractPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
Bukkit.getWorlds().forEach(world -> {
if(world.getGenerator() instanceof BukkitChunkGeneratorWrapper wrapper) {
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Random;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigPack;
@@ -61,8 +61,8 @@ public class BukkitWorldHandle implements WorldHandle {
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
if (!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();;
if(!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
@@ -1,11 +1,11 @@
package com.dfsek.terra.cli.handle;
import java.util.Set;
import com.dfsek.terra.api.handle.ItemHandle;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.item.Enchantment;
import java.util.Set;
public class CLIItemHandle implements ItemHandle {
@Override
@@ -20,6 +20,9 @@ package com.dfsek.terra.forge;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
import net.minecraft.MinecraftVersion;
import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer;
@@ -34,6 +37,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -62,8 +66,7 @@ public class ForgePlatform extends ModPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
MinecraftServer server = getServer();
@@ -31,6 +31,7 @@ import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.mod.config.PostLoadCompatibilityOptions;
import com.dfsek.terra.mod.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.mod.config.VanillaBiomeProperties;
import com.dfsek.terra.mod.config.VanillaWorldProperties;
public abstract class MinecraftAddon implements BaseAddon {
@@ -44,6 +45,12 @@ public abstract class MinecraftAddon implements BaseAddon {
@Override
public void initialize() {
modPlatform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(this, ConfigPackPostLoadEvent.class)
.then(event -> event.getPack().getContext().put(event.loadTemplate(new VanillaWorldProperties())))
.priority(100)
.global();
modPlatform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(this, ConfigPackPreLoadEvent.class)
@@ -5,6 +5,7 @@ import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.boss.dragon.EnderDragonFight;
import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.sound.BiomeAdditionsSound;
@@ -26,6 +27,7 @@ import net.minecraft.world.gen.WorldPreset;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
@@ -60,8 +62,17 @@ public abstract class ModPlatform extends AbstractPlatform {
public abstract MinecraftServer getServer();
public void registerWorldTypes(BiConsumer<Identifier, WorldPreset> registerFunction) {
HashSet<String> configPacksInMetaPack = new HashSet<>();
getRawMetaConfigRegistry().forEach(pack -> {
PresetUtil.createMetaPackPreset(pack, this).apply(registerFunction);
pack.packs().forEach((k, v) -> configPacksInMetaPack.add(v.getID()));
});
getRawConfigRegistry()
.forEach(pack -> PresetUtil.createDefault(pack, this).apply(registerFunction));
.forEach(pack -> {
if(!configPacksInMetaPack.contains(pack.getID())) {
PresetUtil.createDefault(pack, this).apply(registerFunction);
}
});
}
@Override
@@ -0,0 +1,48 @@
package com.dfsek.terra.mod.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.api.util.Range;
public class MonsterSettingsConfig implements ObjectTemplate<MonsterSettingsConfig> {
@Value("piglin-safe")
@Default
private Boolean piglinSafe = null;
@Value("has-raids")
@Default
private Boolean hasRaids = null;
@Value("monster-spawn-light")
@Default
private Range monsterSpawnLight = null;
@Value("monster-spawn-block-light-limit")
@Default
private Integer monsterSpawnBlockLightLimit = null;
public Boolean getPiglinSafe() {
return piglinSafe;
}
public Boolean getHasRaids() {
return hasRaids;
}
public Range getMonsterSpawnLight() {
return monsterSpawnLight;
}
public Integer getMonsterSpawnBlockLightLimit() {
return monsterSpawnBlockLightLimit;
}
@Override
public MonsterSettingsConfig get() {
return this;
}
}
@@ -0,0 +1,171 @@
package com.dfsek.terra.mod.config;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import net.minecraft.util.Identifier;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.ConstantRange;
import com.dfsek.terra.api.util.Range;
public class VanillaWorldProperties implements ConfigTemplate, Properties {
@Value("vanilla")
@Default
private String vanillaDimension = "minecraft:overworld";
@Value("vanilla-generation")
@Default
private String vanillaGeneration = vanillaDimension;
@Value("minecraft.fixed-time")
@Default
private Long fixedTime = null;
@Value("minecraft.has-sky-light")
@Default
private Boolean hasSkyLight = null;
@Value("minecraft.has-ceiling")
@Default
private Boolean hasCeiling = null;
@Value("minecraft.ultra-warm")
@Default
private Boolean ultraWarm = null;
@Value("minecraft.natural")
@Default
private Boolean natural = null;
@Value("minecraft.coordinate-scale")
@Default
private Double coordinateScale = null;
@Value("minecraft.bed-works")
@Default
private Boolean bedWorks = null;
@Value("minecraft.respawn-anchor-works")
@Default
private Boolean respawnAnchorWorks = null;
@Value("minecraft.height")
@Default
private Range height = null;
@Value("minecraft.height.logical")
@Default
private Integer logicalHeight = null;
@Value("minecraft.infiniburn")
@Default
private Identifier infiniburn = null;
@Value("minecraft.effects")
@Default
private Identifier effects = null;
@Value("minecraft.ambient-light")
@Default
private Float ambientLight = null;
@Value("minecraft.monster-settings")
@Default
private MonsterSettingsConfig monsterSettings = null;
@Value("minecraft.mob-generation")
@Default
private Boolean mobGeneration = null;
@Value("minecraft.sealevel")
@Default
private Integer sealevel = null;
@Value("minecraft.spawn-height")
@Default
private Integer spawnHeight = 64;
public String getVanillaDimension() {
return vanillaDimension;
}
public String getVanillaGeneration() {
return vanillaGeneration;
}
public Long getFixedTime() {
return fixedTime;
}
public Boolean getHasSkyLight() {
return hasSkyLight;
}
public Boolean getHasCeiling() {
return hasCeiling;
}
public Boolean getUltraWarm() {
return ultraWarm;
}
public Boolean getNatural() {
return natural;
}
public Double getCoordinateScale() {
return coordinateScale;
}
public Boolean getBedWorks() {
return bedWorks;
}
public Boolean getRespawnAnchorWorks() {
return respawnAnchorWorks;
}
public ConstantRange getHeight() {
//TODO THIS IS BAD
if(height != null) {
return new ConstantRange(height.getMin(), height.getMax());
} else {
return null;
}
}
public Integer getLogicalHeight() {
return logicalHeight;
}
public Identifier getInfiniburn() {
return infiniburn;
}
public Identifier getEffects() {
return effects;
}
public Float getAmbientLight() {
return ambientLight;
}
public MonsterSettingsConfig getMonsterSettings() {
return monsterSettings;
}
public Boolean getMobGeneration() {
return mobGeneration;
}
public Integer getSealevel() {
return sealevel;
}
public Integer getSpawnHeight() {
return spawnHeight;
}
}
@@ -2,13 +2,15 @@ package com.dfsek.terra.mod.data;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.util.ConstantRange;
import com.dfsek.terra.mod.CommonPlatform;
import com.dfsek.terra.mod.generation.GenerationSettings;
import com.dfsek.terra.mod.generation.MinecraftChunkGeneratorWrapper;
import com.dfsek.terra.mod.generation.TerraBiomeSource;
import com.dfsek.terra.mod.implmentation.TerraIntProvider;
public final class Codecs {
@@ -39,6 +41,19 @@ public final class Codecs {
.forGetter(TerraBiomeSource::getPack))
.apply(instance, instance.stable(TerraBiomeSource::new)));
public static final Codec<ConstantRange> TERRA_CONSTANT_RANGE = RecordCodecBuilder.create(range -> range.group(
Codec.INT.fieldOf("min").stable().forGetter(ConstantRange::getMin),
Codec.INT.fieldOf("max").stable().forGetter(ConstantRange::getMax)).apply(range, range.stable(ConstantRange::new)));
public static final Codec<GenerationSettings> TERRA_GENERATION_SETTINGS = RecordCodecBuilder
.create(instance -> instance.group(
TERRA_CONSTANT_RANGE.fieldOf("height").stable().forGetter(GenerationSettings::height),
Codec.INT.fieldOf("sea_level").forGetter(GenerationSettings::sealevel),
Codec.BOOL.fieldOf("mob_generation").forGetter(GenerationSettings::mobGeneration),
Codec.INT.fieldOf("spawn_height").forGetter(GenerationSettings::sealevel))
.apply(instance, instance.stable(GenerationSettings::new)));
public static final Codec<MinecraftChunkGeneratorWrapper> MINECRAFT_CHUNK_GENERATOR_WRAPPER = RecordCodecBuilder
.create(
instance -> instance.group(
@@ -48,10 +63,16 @@ public final class Codecs {
CONFIG_PACK.fieldOf("pack")
.stable()
.forGetter(MinecraftChunkGeneratorWrapper::getPack),
ChunkGeneratorSettings.REGISTRY_CODEC.fieldOf("settings")
TERRA_GENERATION_SETTINGS.fieldOf("settings")
.stable()
.forGetter(MinecraftChunkGeneratorWrapper::getSettings)
).apply(instance, instance.stable(
MinecraftChunkGeneratorWrapper::new))
);
public static final Codec<TerraIntProvider> TERRA_CONSTANT_RANGE_INT_PROVIDER_TYPE = RecordCodecBuilder.create(range -> range.group(
Codec.INT.fieldOf("min").stable().forGetter(TerraIntProvider::getMin),
Codec.INT.fieldOf("max").stable().forGetter(TerraIntProvider::getMax))
.apply(range, range.stable((min, max) -> new TerraIntProvider(new ConstantRange(
min, max)))));
}
@@ -0,0 +1,7 @@
package com.dfsek.terra.mod.generation;
import com.dfsek.terra.api.util.ConstantRange;
public record GenerationSettings(ConstantRange height, Integer sealevel, Boolean mobGeneration, Integer spawnHeight) {
}
@@ -35,11 +35,11 @@ import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.BiomeAccess;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.dimension.NetherPortal;
import net.minecraft.world.gen.GenerationStep.Carver;
import net.minecraft.world.gen.StructureAccessor;
import net.minecraft.world.gen.StructureWeightSampler;
import net.minecraft.world.gen.chunk.Blender;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.chunk.VerticalBlockSample;
import net.minecraft.world.gen.densityfunction.DensityFunction.UnblendedNoisePos;
import net.minecraft.world.gen.noise.NoiseConfig;
@@ -69,12 +69,13 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
private static final Logger logger = LoggerFactory.getLogger(MinecraftChunkGeneratorWrapper.class);
private final TerraBiomeSource biomeSource;
private final RegistryEntry<ChunkGeneratorSettings> settings;
private final GenerationSettings settings;
private ChunkGenerator delegate;
private ConfigPack pack;
public MinecraftChunkGeneratorWrapper(TerraBiomeSource biomeSource, ConfigPack configPack,
RegistryEntry<ChunkGeneratorSettings> settingsSupplier) {
GenerationSettings settingsSupplier) {
super(biomeSource);
this.pack = configPack;
this.settings = settingsSupplier;
@@ -96,7 +97,7 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
@Override
public void populateEntities(ChunkRegion region) {
if(!this.settings.value().mobGenerationDisabled()) {
if(this.settings.mobGeneration()) {
ChunkPos chunkPos = region.getCenterPos();
RegistryEntry<Biome> registryEntry = region.getBiome(chunkPos.getStartPos().withY(region.getTopY() - 1));
ChunkRandom chunkRandom = new ChunkRandom(new CheckedRandom(RandomSeed.getSeed()));
@@ -105,12 +106,6 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
}
}
@Override
public int getWorldHeight() {
return settings.value().generationShapeConfig().height();
}
@Override
public CompletableFuture<Chunk> populateNoise(Executor executor, Blender blender, NoiseConfig noiseConfig,
StructureAccessor structureAccessor, Chunk chunk) {
@@ -165,16 +160,25 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
});
}
@Override
public int getWorldHeight() {
return settings.height().getRange();
}
@Override
public int getSeaLevel() {
return settings.value().seaLevel();
return settings.sealevel();
}
@Override
public int getMinimumY() {
return settings.value().generationShapeConfig().minimumY();
return settings.height().getMin();
}
@Override
public int getSpawnHeight(HeightLimitView world) {
return settings.spawnHeight();
}
@Override
public int getHeight(int x, int z, Type heightmap, HeightLimitView height, NoiseConfig noiseConfig) {
@@ -202,7 +206,7 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
@Override
public void getDebugHudText(List<String> text, NoiseConfig noiseConfig, BlockPos pos) {
// no op
}
public ConfigPack getPack() {
@@ -228,7 +232,7 @@ public class MinecraftChunkGeneratorWrapper extends net.minecraft.world.gen.chun
return delegate;
}
public RegistryEntry<ChunkGeneratorSettings> getSettings() {
public GenerationSettings getSettings() {
return settings;
}
@@ -64,8 +64,8 @@ public class MinecraftWorldHandle implements WorldHandle {
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
if (!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();;
if(!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config packs as this translation will be removed in the next major " +
@@ -0,0 +1,42 @@
package com.dfsek.terra.mod.implmentation;
import net.minecraft.util.math.intprovider.IntProvider;
import net.minecraft.util.math.intprovider.IntProviderType;
import net.minecraft.util.math.random.Random;
import java.util.HashMap;
import java.util.Map;
import com.dfsek.terra.api.util.Range;
import com.dfsek.terra.mod.util.MinecraftAdapter;
public class TerraIntProvider extends IntProvider {
public static final Map<Class, IntProviderType> TERRA_RANGE_TYPE_TO_INT_PROVIDER_TYPE = new HashMap<>();
public Range delegate;
public TerraIntProvider(Range delegate) {
this.delegate = delegate;
}
@Override
public int get(Random random) {
return delegate.get(MinecraftAdapter.adapt(random));
}
@Override
public int getMin() {
return delegate.getMin();
}
@Override
public int getMax() {
return delegate.getMax();
}
@Override
public IntProviderType<?> getType() {
return TERRA_RANGE_TYPE_TO_INT_PROVIDER_TYPE.get(delegate.getClass());
}
}
@@ -1,30 +0,0 @@
package com.dfsek.terra.mod.mixin.fix;
import net.minecraft.world.gen.structure.NetherFossilStructure;
import net.minecraft.world.gen.structure.Structure.Context;
import net.minecraft.world.gen.structure.Structure.StructurePosition;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Optional;
import com.dfsek.terra.mod.generation.MinecraftChunkGeneratorWrapper;
/**
* Disable fossil generation in Terra worlds, as they are very expensive due to consistently triggering cache misses.
* <p>
* Currently, on Fabric, Terra cannot be specified as a Nether generator. TODO: logic to turn fossils back on if chunk generator is in
* nether.
*/
@Mixin(NetherFossilStructure.class)
public class NetherFossilOptimization {
@Inject(method = "getStructurePosition", at = @At("HEAD"), cancellable = true)
public void injectFossilPositions(Context context, CallbackInfoReturnable<Optional<StructurePosition>> cir) {
if(context.chunkGenerator() instanceof MinecraftChunkGeneratorWrapper) {
cir.setReturnValue(Optional.empty());
}
}
}
@@ -0,0 +1,62 @@
package com.dfsek.terra.mod.mixin.generalize;
import net.minecraft.block.entity.SignText;
import net.minecraft.client.render.DimensionEffects;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldGenerationProgressListener;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.random.RandomSequencesState;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.village.raid.RaidManager;
import net.minecraft.world.MutableWorldProperties;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.level.ServerWorldProperties;
import net.minecraft.world.level.storage.LevelStorage;
import net.minecraft.world.level.storage.LevelStorage.Session;
import net.minecraft.world.spawner.SpecialSpawner;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@Mixin(ServerWorld.class)
public abstract class ServerWorldMixin extends World {
protected ServerWorldMixin(MutableWorldProperties properties, RegistryKey<World> registryRef, DynamicRegistryManager registryManager,
RegistryEntry<DimensionType> dimensionEntry, Supplier<Profiler> profiler, boolean isClient,
boolean debugWorld, long biomeAccess, int maxChainedNeighborUpdates) {
super(properties, registryRef, registryManager, dimensionEntry, profiler, isClient, debugWorld, biomeAccess,
maxChainedNeighborUpdates);
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/entry/RegistryEntry;matchesKey(Lnet/minecraft/registry/RegistryKey;)Z"))
public <T> boolean matchesKeyProxy(RegistryEntry<T> instance, RegistryKey<T> tRegistryKey) {
if (tRegistryKey == DimensionTypes.THE_END) {
return (this.getRegistryKey() == World.END);
}
return instance.matchesKey(tRegistryKey);
}
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/village/raid/RaidManager;nameFor(Lnet/minecraft/registry/entry/RegistryEntry;)Ljava/lang/String;"))
public String nameForProxy(RegistryEntry<DimensionType> dimensionTypeEntry) {
RegistryEntry<DimensionType> entry = dimensionTypeEntry;
if (this.getRegistryKey() == World.END) {
entry = getRegistryManager().get(RegistryKeys.DIMENSION_TYPE).getEntry(DimensionTypes.THE_NETHER).orElseThrow();
}
return RaidManager.nameFor(entry);
}
}
@@ -59,7 +59,7 @@ public abstract class MobSpawnerBlockEntityMixin extends BlockEntity {
public void terra$setSpawnedType(@NotNull EntityType creatureType) {
Random rand;
if (hasWorld()) {
if(hasWorld()) {
rand = world.getRandom();
} else {
rand = Random.create();
@@ -0,0 +1,74 @@
package com.dfsek.terra.mod.util;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionType.MonsterSettings;
import org.jetbrains.annotations.NotNull;
import java.util.OptionalLong;
import com.dfsek.terra.mod.ModPlatform;
import com.dfsek.terra.mod.config.MonsterSettingsConfig;
import com.dfsek.terra.mod.config.VanillaWorldProperties;
import com.dfsek.terra.mod.implmentation.TerraIntProvider;
public class DimensionUtil {
public static DimensionType createDimension(VanillaWorldProperties vanillaWorldProperties, DimensionType defaultDimension,
ModPlatform platform) {
MonsterSettingsConfig monsterSettingsConfig;
if(vanillaWorldProperties.getMonsterSettings() != null) {
monsterSettingsConfig = vanillaWorldProperties.getMonsterSettings();
} else {
monsterSettingsConfig = new MonsterSettingsConfig();
}
MonsterSettings monsterSettings = getMonsterSettings(defaultDimension, monsterSettingsConfig);
return new DimensionType(
vanillaWorldProperties.getFixedTime() == null ? defaultDimension.fixedTime() : OptionalLong.of(
vanillaWorldProperties.getFixedTime()),
vanillaWorldProperties.getHasSkyLight() == null ? defaultDimension.hasSkyLight() : vanillaWorldProperties.getHasSkyLight(),
vanillaWorldProperties.getHasCeiling() == null ? defaultDimension.hasCeiling() : vanillaWorldProperties.getHasCeiling(),
vanillaWorldProperties.getUltraWarm() == null ? defaultDimension.ultrawarm() : vanillaWorldProperties.getUltraWarm(),
vanillaWorldProperties.getNatural() == null ? defaultDimension.natural() : vanillaWorldProperties.getNatural(),
vanillaWorldProperties.getCoordinateScale() == null
? defaultDimension.coordinateScale()
: vanillaWorldProperties.getCoordinateScale(),
vanillaWorldProperties.getBedWorks() == null ? defaultDimension.bedWorks() : vanillaWorldProperties.getBedWorks(),
vanillaWorldProperties.getRespawnAnchorWorks() == null
? defaultDimension.respawnAnchorWorks()
: vanillaWorldProperties.getRespawnAnchorWorks(),
vanillaWorldProperties.getHeight() == null ? defaultDimension.minY() : vanillaWorldProperties.getHeight().getMin(),
vanillaWorldProperties.getHeight() == null ? defaultDimension.height() : vanillaWorldProperties.getHeight().getRange(),
vanillaWorldProperties.getLogicalHeight() == null
? defaultDimension.logicalHeight()
: vanillaWorldProperties.getLogicalHeight(),
vanillaWorldProperties.getInfiniburn() == null
? defaultDimension.infiniburn()
: TagKey.of(RegistryKeys.BLOCK, vanillaWorldProperties.getInfiniburn()),
vanillaWorldProperties.getEffects() == null ? defaultDimension.effects() : vanillaWorldProperties.getEffects(),
vanillaWorldProperties.getAmbientLight() == null ? defaultDimension.ambientLight() : vanillaWorldProperties.getAmbientLight(),
monsterSettings
);
}
@NotNull
private static MonsterSettings getMonsterSettings(DimensionType defaultDimension, MonsterSettingsConfig monsterSettingsConfig) {
MonsterSettings defaultMonsterSettings = defaultDimension.monsterSettings();
MonsterSettings monsterSettings = new MonsterSettings(
monsterSettingsConfig.getPiglinSafe() == null ? defaultMonsterSettings.piglinSafe() : monsterSettingsConfig.getPiglinSafe(),
monsterSettingsConfig.getHasRaids() == null ? defaultMonsterSettings.hasRaids() : monsterSettingsConfig.getHasRaids(),
monsterSettingsConfig.getMonsterSpawnLight() == null ? defaultMonsterSettings.monsterSpawnLightTest() : new TerraIntProvider(
monsterSettingsConfig.getMonsterSpawnLight()),
monsterSettingsConfig.getMonsterSpawnBlockLightLimit() == null
? defaultMonsterSettings.monsterSpawnBlockLightLimit()
: monsterSettingsConfig.getMonsterSpawnBlockLightLimit()
);
return monsterSettings;
}
}
@@ -18,8 +18,11 @@
package com.dfsek.terra.mod.util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.HeightLimitView;
import java.util.random.RandomGenerator;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.terra.api.world.info.WorldProperties;
@@ -53,4 +56,48 @@ public final class MinecraftAdapter {
}
};
}
public static RandomGenerator adapt(Random random) {
return new RandomGenerator() {
@Override
public boolean nextBoolean() {
return random.nextBoolean();
}
@Override
public float nextFloat() {
return random.nextFloat();
}
@Override
public double nextDouble() {
return random.nextDouble();
}
@Override
public int nextInt() {
return random.nextInt();
}
@Override
public int nextInt(int bound) {
return random.nextInt(bound);
}
@Override
public long nextLong() {
return random.nextLong();
}
@Override
public double nextGaussian() {
return random.nextGaussian();
}
@Override
public int nextInt(int origin, int bound) {
return random.nextBetween(origin, bound);
}
};
}
}
@@ -10,11 +10,13 @@ import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.intprovider.IntProviderType;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biome.Builder;
import net.minecraft.world.biome.BiomeEffects;
import net.minecraft.world.biome.GenerationSettings;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,7 +33,10 @@ import com.dfsek.terra.api.block.entity.Container;
import com.dfsek.terra.api.block.entity.MobSpawner;
import com.dfsek.terra.api.block.entity.Sign;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.ConstantRange;
import com.dfsek.terra.mod.config.VanillaBiomeProperties;
import com.dfsek.terra.mod.data.Codecs;
import com.dfsek.terra.mod.implmentation.TerraIntProvider;
import com.dfsek.terra.mod.mixin.access.BiomeAccessor;
import com.dfsek.terra.mod.mixin_ifaces.FloraFeatureHolder;
@@ -92,10 +97,22 @@ public final class MinecraftUtil {
return Map.copyOf(TERRA_BIOME_MAP);
}
public static RegistryKey<Biome> registerKey(Identifier identifier) {
public static void registerIntProviderTypes() {
IntProviderType<TerraIntProvider> CONSTANT = IntProviderType.register("terra:constant_range",
Codecs.TERRA_CONSTANT_RANGE_INT_PROVIDER_TYPE);
TerraIntProvider.TERRA_RANGE_TYPE_TO_INT_PROVIDER_TYPE.put(ConstantRange.class, CONSTANT);
}
public static RegistryKey<Biome> registerBiomeKey(Identifier identifier) {
return RegistryKey.of(RegistryKeys.BIOME, identifier);
}
public static RegistryKey<DimensionType> registerDimensionTypeKey(Identifier identifier) {
return RegistryKey.of(RegistryKeys.DIMENSION_TYPE, identifier);
}
public static Biome createBiome(com.dfsek.terra.api.world.biome.Biome biome, Biome vanilla,
VanillaBiomeProperties vanillaBiomeProperties) {
GenerationSettings.Builder generationSettings = new GenerationSettings.Builder();
@@ -1,8 +1,11 @@
package com.dfsek.terra.mod.util;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
import net.minecraft.world.biome.source.MultiNoiseBiomeSourceParameterList;
import net.minecraft.world.biome.source.MultiNoiseBiomeSourceParameterLists;
@@ -18,13 +21,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.util.ConstantRange;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.mod.ModPlatform;
import com.dfsek.terra.mod.config.VanillaWorldProperties;
import com.dfsek.terra.mod.generation.GenerationSettings;
import com.dfsek.terra.mod.generation.MinecraftChunkGeneratorWrapper;
import com.dfsek.terra.mod.generation.TerraBiomeSource;
@@ -40,47 +47,147 @@ public class PresetUtil {
platform.multiNoiseBiomeSourceParameterListRegistry();
RegistryEntry<DimensionType> overworldDimensionType = dimensionTypeRegistry.getEntry(DimensionTypes.OVERWORLD).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> overworld = chunkGeneratorSettingsRegistry.getEntry(ChunkGeneratorSettings.OVERWORLD)
.orElseThrow();
Identifier generatorID = Identifier.of("terra", pack.getID().toLowerCase(Locale.ROOT) + "/" + pack.getNamespace().toLowerCase(
Locale.ROOT));
PRESETS.add(generatorID);
RegistryEntry<DimensionType> registryEntry = dimensionTypeRegistry.getEntry(DimensionTypes.THE_NETHER).orElseThrow();
RegistryEntry.Reference<MultiNoiseBiomeSourceParameterList> reference = multiNoiseBiomeSourceParameterLists.getEntry(
MultiNoiseBiomeSourceParameterLists.NETHER).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> registryEntry2 = chunkGeneratorSettingsRegistry.getEntry(ChunkGeneratorSettings.NETHER)
.orElseThrow();
HashMap<RegistryKey<DimensionOptions>, DimensionOptions> dimensionMap = new HashMap<>();
RegistryEntry<DimensionType> registryEntry3 = dimensionTypeRegistry.getEntry(DimensionTypes.THE_END).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> registryEntry4 = chunkGeneratorSettingsRegistry.getEntry(ChunkGeneratorSettings.END)
.orElseThrow();
insertCustom(platform, "minecraft:overworld", pack, dimensionTypeRegistry, chunkGeneratorSettingsRegistry, dimensionMap);
TerraBiomeSource biomeSource = new TerraBiomeSource(pack);
ChunkGenerator generator = new MinecraftChunkGeneratorWrapper(biomeSource, pack, overworld);
insertDefaults(dimensionTypeRegistry, chunkGeneratorSettingsRegistry, multiNoiseBiomeSourceParameterLists, platform.biomeRegistry(),
dimensionMap);
DimensionOptions dimensionOptions = new DimensionOptions(overworldDimensionType, generator);
DimensionOptions netherDimensionOptions = new DimensionOptions(registryEntry,
new NoiseChunkGenerator(MultiNoiseBiomeSource.create(reference),
registryEntry2));
DimensionOptions endDimensionOptions = new DimensionOptions(registryEntry3, new NoiseChunkGenerator(
TheEndBiomeSource.createVanilla(platform.biomeRegistry().getReadOnlyWrapper()), registryEntry4));
WorldPreset preset = createPreset(dimensionOptions, netherDimensionOptions, endDimensionOptions);
WorldPreset preset = new WorldPreset(dimensionMap);
LOGGER.info("Created world type \"{}\"", generatorID);
return Pair.of(generatorID, preset);
}
private static WorldPreset createPreset(DimensionOptions dimensionOptions, DimensionOptions netherDimensionOptions,
DimensionOptions endDimensionOptions) {
return new WorldPreset(
Map.of(DimensionOptions.OVERWORLD, dimensionOptions, DimensionOptions.NETHER, netherDimensionOptions, DimensionOptions.END,
endDimensionOptions)
);
public static Pair<Identifier, WorldPreset> createMetaPackPreset(MetaPack metaPack, ModPlatform platform) {
Registry<DimensionType> dimensionTypeRegistry = platform.dimensionTypeRegistry();
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry = platform.chunkGeneratorSettingsRegistry();
Registry<MultiNoiseBiomeSourceParameterList> multiNoiseBiomeSourceParameterLists =
platform.multiNoiseBiomeSourceParameterListRegistry();
Identifier generatorID = Identifier.of("terra",
metaPack.getID().toLowerCase(Locale.ROOT) + "/" + metaPack.getNamespace().toLowerCase(
Locale.ROOT));
PRESETS.add(generatorID);
HashMap<RegistryKey<DimensionOptions>, DimensionOptions> dimensionMap = new HashMap<>();
metaPack.packs().forEach((key, pack) -> {
insertCustom(platform, key, pack, dimensionTypeRegistry, chunkGeneratorSettingsRegistry, dimensionMap);
});
insertDefaults(dimensionTypeRegistry, chunkGeneratorSettingsRegistry, multiNoiseBiomeSourceParameterLists, platform.biomeRegistry(),
dimensionMap);
WorldPreset preset = new WorldPreset(dimensionMap);
LOGGER.info("Created world type \"{}\"", generatorID);
return Pair.of(generatorID, preset);
}
private static void insertCustom(ModPlatform platform, String key, ConfigPack pack, Registry<DimensionType> dimensionTypeRegistry,
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry,
HashMap<RegistryKey<DimensionOptions>, DimensionOptions> dimensionMap) {
Identifier demensionIdentifier = new Identifier(key);
VanillaWorldProperties vanillaWorldProperties;
if(pack.getContext().has(VanillaWorldProperties.class)) {
vanillaWorldProperties = pack.getContext().get(VanillaWorldProperties.class);
} else {
vanillaWorldProperties = new VanillaWorldProperties();
}
DimensionType defaultDimension = dimensionTypeRegistry.get(new Identifier(vanillaWorldProperties.getVanillaDimension()));
assert defaultDimension != null;
DimensionType dimensionType = DimensionUtil.createDimension(vanillaWorldProperties, defaultDimension, platform);
RegistryKey<DimensionType> dimensionTypeRegistryKey = MinecraftUtil.registerDimensionTypeKey(
new Identifier("terra", pack.getID().toLowerCase(
Locale.ROOT)));
Registry.registerReference(dimensionTypeRegistry, dimensionTypeRegistryKey, dimensionType);
RegistryEntry<DimensionType> dimensionTypeRegistryEntry = dimensionTypeRegistry.getEntry(dimensionType);
TerraBiomeSource biomeSource = new TerraBiomeSource(pack);
RegistryEntry<ChunkGeneratorSettings> defaultGeneratorSettings = chunkGeneratorSettingsRegistry.getEntry(
chunkGeneratorSettingsRegistry.get(new Identifier(vanillaWorldProperties.getVanillaGeneration())));
GenerationSettings generatorSettings = new GenerationSettings(
vanillaWorldProperties.getHeight() == null ? new ConstantRange(
defaultGeneratorSettings.value().generationShapeConfig().minimumY(),
defaultGeneratorSettings.value().generationShapeConfig().height()) : vanillaWorldProperties.getHeight(),
vanillaWorldProperties.getSealevel() == null
? defaultGeneratorSettings.value().seaLevel()
: vanillaWorldProperties.getSealevel(),
vanillaWorldProperties.getMobGeneration() == null
? !defaultGeneratorSettings.value().mobGenerationDisabled()
: vanillaWorldProperties.getMobGeneration(),
vanillaWorldProperties.getSpawnHeight());
ChunkGenerator generator = new MinecraftChunkGeneratorWrapper(biomeSource, pack, generatorSettings);
DimensionOptions dimensionOptions = new DimensionOptions(dimensionTypeRegistryEntry, generator);
RegistryKey<DimensionOptions> dimensionOptionsRegistryKey = RegistryKey.of(RegistryKeys.DIMENSION, demensionIdentifier);
dimensionMap.put(dimensionOptionsRegistryKey, dimensionOptions);
}
private static void insertDefaults(Registry<DimensionType> dimensionTypeRegistry,
Registry<ChunkGeneratorSettings> chunkGeneratorSettingsRegistry,
Registry<MultiNoiseBiomeSourceParameterList> multiNoiseBiomeSourceParameterLists,
Registry<Biome> biomeRegistry, HashMap<RegistryKey<DimensionOptions>, DimensionOptions> map) {
if(!map.containsKey(DimensionOptions.OVERWORLD)) {
RegistryEntry<DimensionType> overworldDimensionType = dimensionTypeRegistry.getEntry(DimensionTypes.OVERWORLD).orElseThrow();
RegistryEntry.Reference<MultiNoiseBiomeSourceParameterList> overworldChunkBiomeReference =
multiNoiseBiomeSourceParameterLists.getEntry(
MultiNoiseBiomeSourceParameterLists.OVERWORLD).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> overworldChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getEntry(
ChunkGeneratorSettings.OVERWORLD)
.orElseThrow();
DimensionOptions overworldDimensionOptions = new DimensionOptions(overworldDimensionType,
(new NoiseChunkGenerator(MultiNoiseBiomeSource.create(overworldChunkBiomeReference), overworldChunkGeneratorSettings)));
map.put(DimensionOptions.OVERWORLD, overworldDimensionOptions);
}
if(!map.containsKey(DimensionOptions.NETHER)) {
RegistryEntry<DimensionType> netherDimensionType = dimensionTypeRegistry.getEntry(DimensionTypes.THE_NETHER).orElseThrow();
RegistryEntry.Reference<MultiNoiseBiomeSourceParameterList> netherChunkBiomeReference =
multiNoiseBiomeSourceParameterLists.getEntry(
MultiNoiseBiomeSourceParameterLists.NETHER).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> netherChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getEntry(
ChunkGeneratorSettings.NETHER)
.orElseThrow();
DimensionOptions overworldDimensionOptions = new DimensionOptions(netherDimensionType,
(new NoiseChunkGenerator(MultiNoiseBiomeSource.create(netherChunkBiomeReference), netherChunkGeneratorSettings)));
map.put(DimensionOptions.NETHER, overworldDimensionOptions);
}
if(!map.containsKey(DimensionOptions.END)) {
RegistryEntry<DimensionType> endDimensionType = dimensionTypeRegistry.getEntry(DimensionTypes.THE_END).orElseThrow();
RegistryEntry<ChunkGeneratorSettings> endChunkGeneratorSettings = chunkGeneratorSettingsRegistry.getEntry(
ChunkGeneratorSettings.END)
.orElseThrow();
DimensionOptions overworldDimensionOptions = new DimensionOptions(endDimensionType,
(new NoiseChunkGenerator(TheEndBiomeSource.createVanilla(biomeRegistry.getReadOnlyWrapper()), endChunkGeneratorSettings)));
map.put(DimensionOptions.END, overworldDimensionOptions);
}
}
public static List<Identifier> getPresets() {
@@ -33,7 +33,7 @@ public final class TagUtil {
}
public static void registerWorldPresetTags(Registry<WorldPreset> registry) {
logger.info("Doing preset tag garbage....");
logger.info("Registering Preset Tags.");
Map<TagKey<WorldPreset>, List<RegistryEntry<WorldPreset>>> collect = tagsToMutableMap(registry);
PresetUtil
@@ -1,3 +1,4 @@
accessWidener v1 named
accessible class net/minecraft/world/biome/Biome$Weather
accessible class net/minecraft/world/gen/WorldPresets$Registrar
accessible field net/minecraft/world/dimension/DimensionOptionsRegistryHolder VANILLA_KEYS Ljava/util/Set;
@@ -10,7 +10,7 @@
"access.StructureAccessorAccessor",
"access.VillagerTypeAccessor",
"fix.BeeMoveGoalsUnsynchronizedRandomAccessFix",
"fix.NetherFossilOptimization",
"generalize.ServerWorldMixin",
"implementations.compat.GenerationSettingsFloraFeaturesMixin",
"implementations.terra.BiomeMixin",
"implementations.terra.HandleImplementationMixin",
@@ -4,6 +4,7 @@ import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import net.minecraft.MinecraftVersion;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
@@ -34,6 +35,8 @@ public abstract class LifecyclePlatform extends ModPlatform {
private static final AtomicReference<Registry<DimensionType>> DIMENSIONS = new AtomicReference<>();
private static final AtomicReference<Registry<ChunkGeneratorSettings>> SETTINGS = new AtomicReference<>();
private static final AtomicReference<Registry<MultiNoiseBiomeSourceParameterList>> NOISE = new AtomicReference<>();
private static final AtomicReference<DynamicRegistryManager.Immutable> DYNAMIC_REGISTRY_MANAGER = new AtomicReference<>();
private static MinecraftServer server;
public LifecyclePlatform() {
@@ -51,6 +54,10 @@ public abstract class LifecyclePlatform extends ModPlatform {
NOISE.set(multiNoiseBiomeSourceParameterListRegistry);
}
public static void setDynamicRegistryManager(DynamicRegistryManager.Immutable dynamicRegistryManager) {
DYNAMIC_REGISTRY_MANAGER.set(dynamicRegistryManager);
}
@Override
public MinecraftServer getServer() {
return server;
@@ -63,9 +70,7 @@ public abstract class LifecyclePlatform extends ModPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
if(server != null) {
BiomeUtil.registerBiomes(server.getRegistryManager().get(RegistryKeys.BIOME));
@@ -65,7 +65,7 @@ public final class BiomeUtil {
.orElseThrow());
} else {
((ProtoPlatformBiome) biome.getPlatformBiome()).setDelegate(Registry.registerReference(registry,
MinecraftUtil.registerKey(identifier)
MinecraftUtil.registerBiomeKey(identifier)
.getValue(),
minecraftBiome));
}
@@ -5,6 +5,7 @@ import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import com.dfsek.terra.mod.data.Codecs;
import com.dfsek.terra.mod.util.MinecraftUtil;
public final class RegistryUtil {
@@ -13,6 +14,7 @@ public final class RegistryUtil {
}
public static void register() {
MinecraftUtil.registerIntProviderTypes();
Registry.register(Registries.CHUNK_GENERATOR, new Identifier("terra:terra"), Codecs.MINECRAFT_CHUNK_GENERATOR_WRAPPER);
Registry.register(Registries.BIOME_SOURCE, new Identifier("terra:terra"), Codecs.TERRA_BIOME_SOURCE);
}