Compare commits

..

44 Commits

Author SHA1 Message Date
Zoë 4917160123 Update README.md 2023-10-17 18:32:00 -06:00
Zoë Gidiere d71f7d4c36 clarify value and change default 2023-10-03 21:03:44 -06:00
Zoë Gidiere 84898a7a6b fixes 2023-10-03 20:03:46 -06:00
Zoë Gidiere 0c1a6efc72 refractor classes 2023-10-03 19:42:15 -06:00
Zoë Gidiere 200281f140 dep orev1 2023-10-03 11:58:32 -06:00
Zoë Gidiere f1ea8074de config-ore-v2 2023-10-03 11:54:18 -06:00
Astrash f896a9a278 Bump noise3d addon minor version 2023-10-03 11:01:50 +11:00
Astrash 79b3b34669 Add slant API for noise3d 2023-10-03 09:51:39 +11:00
Zoë 158deaa971 Merge pull request #408 from PolyhedralDev/dev/physics
Allows you to create a block with physics enabled
2023-10-01 19:07:15 -06:00
Astrash e51e025e9c Implement cubic spline sampler 2023-10-02 11:14:43 +11:00
Zoë Gidiere 8e0d64dccd Make bukkit work 2023-09-30 10:49:28 -06:00
Zoë Gidiere 23f47de10a fix pack loading error 2023-09-29 23:35:17 -06:00
Zoë Gidiere 89651597c2 default to info logging 2023-09-29 23:32:40 -06:00
Zoë 5c0c833b70 Update LifecycleEntryPoint.java 2023-09-29 23:09:41 -06:00
Zoë Gidiere 33d654dc8e impl fabric 2023-09-29 23:05:05 -06:00
Zoë Gidiere f0c602d7e7 implement physics on the api side
we will see if platform changes are needed
2023-09-29 22:10:03 -06:00
Zoë Gidiere 0e37a839fe We do a little commonifying 2023-09-29 21:44:12 -06:00
Astrash 3f9ead0d66 Remove repeated code in cellular sampler 2023-09-27 13:39:51 +10:00
David W 5eeb5af6c4 Add cell center offset return to CELLULAR sampler (#407)
* Add offset lookup return to cellular sampler

* bump noise function plugin version

* revert version to 1.1.0

* rename OffsetNoiseLookup, switch axis orientation

* rename return type aswell in cellcampler
2023-09-21 22:23:49 +00:00
Astrash 81e354f91c Use tectonic properly 2023-07-18 22:06:08 +10:00
Astrash aab28ff4f9 Bump version to 6.4.0 2023-07-18 14:32:12 +10:00
Astrash 0e3a756011 Bump config-noise-function to v1.1.0 2023-07-18 14:29:54 +10:00
Astrash 02198e1b88 Implement distance sampler 2023-07-18 14:29:28 +10:00
Astrash 00aeb98419 Implement translation sampler 2023-07-18 14:27:36 +10:00
Astrash 1a784b51ac Implement expression normalizer sampler 2023-07-18 14:25:07 +10:00
Astrash 34c0895c1f Make metalist injection error more user friendly 2023-07-16 22:46:23 +10:00
Astrash 379fa601a3 Meta annotate LINEAR_HEIGHTMAP sampler 2023-07-16 17:04:51 +10:00
Astrash fcbf51d80b Allow Range keys to be meta annotated 2023-07-16 11:51:51 +10:00
Astrash 9d83dfd164 Bump version to 6.3.2 2023-07-16 11:49:14 +10:00
Astrashh 72686601ee Merge pull request #406 from PolyhedralDev/ver/6.3.1
Ver/6.3.1
2023-07-15 07:04:10 +10:00
Astrash 73baaec6cd Bump version to 6.3.1 2023-07-11 09:33:57 +10:00
Astrashh 0be7213ee5 Merge pull request #401 from PolyhedralDev/dev/reduce-pipeline-caching
Reduce pipeline v2 caching
2023-06-20 10:10:35 +10:00
Astrash 3f3e2fe97c Reduce pipeline v2 caching 2023-06-20 09:57:43 +10:00
dfsek 549edd11ea update ServerCommandSourceMixin 2023-06-15 17:27:26 -07:00
dfsek 36f89946d4 update SignBlockEntityMixin 2023-06-15 17:26:04 -07:00
dfsek 18644d6100 update fabric dependencies 2023-06-15 17:22:21 -07:00
dfsek 9d38ee4329 repackage NMS 1.20.1 2023-06-15 17:18:36 -07:00
dfsek b75a8f85e4 restore NMSChunkGeneratorDelegate#getBaseColumn 2023-06-15 17:16:20 -07:00
dfsek aad58f5968 update nms imports 2023-06-15 17:14:45 -07:00
dfsek a548c30484 clean up unused bstats relocation 2023-06-15 17:11:53 -07:00
dfsek 9ba46ae3a5 make new bukkit NMS module 2023-06-15 17:11:32 -07:00
dfsek 49efbed6f5 update versions 2023-06-15 17:11:04 -07:00
dfsek 4001a56100 Merge pull request #400 from PolyhedralDev/ver/6.3.0
make forge compile
2023-06-15 15:55:49 -07:00
dfsek 70dbd2f2c0 Merge pull request #399 from PolyhedralDev/ver/6.3.0
Ver/6.3.0
2023-06-15 14:05:02 -07:00
67 changed files with 1755 additions and 384 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.3.0"))
versionProjects(":common:implementation", version("6.3.0"))
versionProjects(":platforms", version("6.3.0"))
versionProjects(":common:api", version("6.4.0"))
versionProjects(":common:implementation", version("6.4.0"))
versionProjects(":platforms", version("6.4.0"))
allprojects {
+6 -6
View File
@@ -19,7 +19,7 @@ object Versions {
object Fabric {
const val fabricLoader = "0.14.8"
const val fabricAPI = "0.78.0+1.19.4"
const val fabricAPI = "0.83.1+1.20.1"
}
object Quilt {
@@ -30,9 +30,9 @@ object Versions {
object Mod {
const val mixin = "0.11.2+mixin.0.8.5"
const val minecraft = "1.19.4"
const val minecraft = "1.20.1"
const val yarn = "$minecraft+build.2"
const val fabricLoader = "0.14.2"
const val fabricLoader = "0.14.21"
const val architecuryLoom = "0.12.0.290"
const val architecturyPlugin = "3.4-SNAPSHOT"
@@ -41,16 +41,16 @@ object Versions {
}
object Forge {
const val forge = "${Mod.minecraft}-45.0.43"
const val forge = "${Mod.minecraft}-47.0.3"
const val burningwave = "12.53.0"
}
object Bukkit {
const val paper = "1.18.2-R0.1-SNAPSHOT"
const val paperLib = "1.0.5"
const val minecraft = "1.19.4"
const val minecraft = "1.20.1"
const val reflectionRemapper = "0.1.0-SNAPSHOT"
const val paperDevBundle = "1.19.4-R0.1-SNAPSHOT"
const val paperDevBundle = "1.20.1-R0.1-SNAPSHOT"
}
object Sponge {
@@ -37,7 +37,7 @@ public class PipelineBiomeProvider implements BiomeProvider {
this.noiseAmp = noiseAmp;
this.chunkSize = pipeline.getChunkSize();
this.biomeChunkCache = Caffeine.newBuilder()
.maximumSize(1024)
.maximumSize(64)
.build(pipeline::generateChunk);
Set<PipelineBiome> biomeSet = new HashSet<>();
@@ -54,6 +54,6 @@ public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> {
@Override
public BiomeProvider get() {
return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 500), resolution, blendSampler, blendAmplitude);
return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 128), resolution, blendSampler, blendAmplitude);
}
}
@@ -1,4 +1,4 @@
version = version("1.1.0")
version = version("1.2.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -155,6 +155,16 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
return biomeProvider.getBiome(x, y, z, world.getSeed()).getContext().get(paletteInfoPropertyKey).paletteHolder().getPalette(y);
}
public double getSlant(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) {
int fdX = FastMath.floorMod(x, 16);
int fdZ = FastMath.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);
}
public SamplerProvider samplerProvider() {
return samplerCache;
}
@@ -1,6 +1,6 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
version = version("1.0.0")
version = version("1.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -14,6 +14,7 @@ import java.util.Map;
import java.util.function.Supplier;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
@@ -21,8 +22,10 @@ import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.ImageSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.KernelTemplate;
import com.dfsek.terra.addons.noise.config.templates.LinearHeightmapSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.TranslateSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.CellularNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ConstantNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.DistanceSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ExpressionFunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.GaborNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate;
@@ -30,11 +33,14 @@ import com.dfsek.terra.addons.noise.config.templates.noise.fractal.BrownianMotio
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.PingPongTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractalTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.CubicSplineNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ExpressionNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ProbabilityNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ScaleNormalizerTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.samplers.arithmetic.AdditionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.DivisionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MaxSampler;
@@ -42,6 +48,7 @@ import com.dfsek.terra.addons.noise.samplers.arithmetic.MinSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MultiplicationSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.SubtractionSampler;
import com.dfsek.terra.addons.noise.samplers.noise.CellularSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.GaussianNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.PositiveWhiteNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.WhiteNoiseSampler;
@@ -83,8 +90,11 @@ public class NoiseAddon implements AddonInitializer {
(type, o, loader, depthTracker) -> CellularSampler.DistanceFunction.valueOf((String) o))
.applyLoader(CellularSampler.ReturnType.class,
(type, o, loader, depthTracker) -> CellularSampler.ReturnType.valueOf((String) o))
.applyLoader(DistanceSampler.DistanceFunction.class,
(type, o, loader, depthTracker) -> DistanceSampler.DistanceFunction.valueOf((String) o))
.applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new)
.applyLoader(FunctionTemplate.class, FunctionTemplate::new);
.applyLoader(FunctionTemplate.class, FunctionTemplate::new)
.applyLoader(CubicSpline.Point.class, CubicSplinePointTemplate::new);
noiseRegistry.register(addon.key("LINEAR"), LinearNormalizerTemplate::new);
noiseRegistry.register(addon.key("NORMAL"), NormalNormalizerTemplate::new);
@@ -92,6 +102,7 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
noiseRegistry.register(addon.key("SCALE"), ScaleNormalizerTemplate::new);
noiseRegistry.register(addon.key("POSTERIZATION"), PosterizationNormalizerTemplate::new);
noiseRegistry.register(addon.key("CUBIC_SPLINE"), CubicSplineNormalizerTemplate::new);
noiseRegistry.register(addon.key("IMAGE"), ImageSamplerTemplate::new);
@@ -116,12 +127,15 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("WHITE_NOISE"), () -> new SimpleNoiseTemplate(WhiteNoiseSampler::new));
noiseRegistry.register(addon.key("POSITIVE_WHITE_NOISE"), () -> new SimpleNoiseTemplate(PositiveWhiteNoiseSampler::new));
noiseRegistry.register(addon.key("GAUSSIAN"), () -> new SimpleNoiseTemplate(GaussianNoiseSampler::new));
noiseRegistry.register(addon.key("DISTANCE"), DistanceSamplerTemplate::new);
noiseRegistry.register(addon.key("CONSTANT"), ConstantNoiseTemplate::new);
noiseRegistry.register(addon.key("KERNEL"), KernelTemplate::new);
noiseRegistry.register(addon.key("LINEAR_HEIGHTMAP"), LinearHeightmapSamplerTemplate::new);
noiseRegistry.register(addon.key("TRANSLATE"), TranslateSamplerTemplate::new);
noiseRegistry.register(addon.key("ADD"), () -> new BinaryArithmeticTemplate<>(AdditionSampler::new));
noiseRegistry.register(addon.key("SUB"), () -> new BinaryArithmeticTemplate<>(SubtractionSampler::new));
@@ -134,7 +148,7 @@ public class NoiseAddon implements AddonInitializer {
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
noiseRegistry.register(addon.key("EXPRESSION"), () -> new ExpressionFunctionTemplate(packSamplers, packFunctions));
noiseRegistry.register(addon.key("EXPRESSION_NORMALIZER"), () -> new ExpressionNormalizerTemplate(packSamplers, packFunctions));
NoiseConfigPackTemplate template = event.loadTemplate(new NoiseConfigPackTemplate());
packSamplers.putAll(template.getSamplers());
@@ -0,0 +1,25 @@
package com.dfsek.terra.addons.noise.config;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.api.config.meta.Meta;
public class CubicSplinePointTemplate implements ObjectTemplate<Point> {
@Value("from")
private @Meta double from;
@Value("to")
private @Meta double to;
@Value("gradient")
private @Meta double gradient;
@Override
public Point get() {
return new Point(from, to, gradient);
}
}
@@ -4,6 +4,7 @@ import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -11,14 +12,14 @@ import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearHeightmapSamplerTemplate extends SamplerTemplate<LinearHeightmapSampler> {
@Value("sampler")
@Default
private NoiseSampler sampler = NoiseSampler.zero();
private @Meta NoiseSampler sampler = NoiseSampler.zero();
@Value("base")
private double base;
private @Meta double base;
@Value("scale")
@Default
private double scale = 1;
private @Meta double scale = 1;
@Override
public NoiseSampler get() {
@@ -0,0 +1,32 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.TranslateSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSamplerTemplate extends SamplerTemplate<TranslateSampler> {
@Value("sampler")
private NoiseSampler sampler;
@Value("x")
@Default
private @Meta double x = 0;
@Value("y")
@Default
private @Meta double y = 0;
@Value("z")
@Default
private @Meta double z = 0;
@Override
public NoiseSampler get() {
return new TranslateSampler(sampler, x, y ,z);
}
}
@@ -0,0 +1,42 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler.DistanceFunction;
import com.dfsek.terra.api.config.meta.Meta;
public class DistanceSamplerTemplate extends SamplerTemplate<DistanceSampler> {
@Value("distance-function")
@Default
private DistanceSampler.@Meta DistanceFunction distanceFunction = DistanceFunction.Euclidean;
@Value("point.x")
@Default
private @Meta double x = 0;
@Value("point.y")
@Default
private @Meta double y = 0;
@Value("point.z")
@Default
private @Meta double z = 0;
@Value("normalize")
@Default
private @Meta boolean normalize = false;
@Value("radius")
@Default
private @Meta double normalizeRadius = 100;
@Override
public DistanceSampler get() {
return new DistanceSampler(distanceFunction, x, y, z, normalize, normalizeRadius);
}
}
@@ -8,7 +8,6 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -19,17 +18,16 @@ import java.util.Map;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import com.dfsek.terra.addons.noise.samplers.noise.ExpressionFunction;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFunction> {
private final Map<String, DimensionApplicableNoiseSampler> otherFunctions;
private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions;
@Value("variables")
@Default
@@ -43,44 +41,19 @@ public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFuncti
@Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> otherFunctions, Map<String, FunctionTemplate> samplers) {
this.otherFunctions = otherFunctions;
this.globalFunctions = samplers;
public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.globalSamplers = globalSamplers;
this.globalFunctions = globalFunctions;
}
@Override
public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try {
Map<String, Function> noiseFunctionMap = generateFunctions();
return new ExpressionFunction(noiseFunctionMap, expression, vars);
return new ExpressionFunction(convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
} catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e);
}
}
private Map<String, Function> generateFunctions() throws ParseException {
Map<String, Function> noiseFunctionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : globalFunctions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
otherFunctions.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
samplers.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
return noiseFunctionMap;
}
}
@@ -0,0 +1,23 @@
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.addons.noise.normalizer.CubicSplineNoiseSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.List;
public class CubicSplineNormalizerTemplate extends NormalizerTemplate<CubicSplineNoiseSampler> {
@Value("points")
private @Meta List<@Meta Point> points;
@Override
public NoiseSampler get() {
return new CubicSplineNoiseSampler(function, new CubicSpline(points));
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 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.noise.config.templates.normalizer;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.normalizer.ExpressionNormalizer;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class ExpressionNormalizerTemplate extends NormalizerTemplate<ExpressionNormalizer> {
private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions;
@Value("expression")
private @Meta String expression;
@Value("variables")
@Default
private @Meta Map<String, @Meta Double> vars = new HashMap<>();
@Value("samplers")
@Default
private @Meta LinkedHashMap<String, @Meta DimensionApplicableNoiseSampler> samplers = new LinkedHashMap<>();
@Value("functions")
@Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionNormalizerTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.globalSamplers = globalSamplers;
this.globalFunctions = globalFunctions;
}
@Override
public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try {
return new ExpressionNormalizer(function, convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
} catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e);
}
}
}
@@ -0,0 +1,89 @@
package com.dfsek.terra.addons.noise.math;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class CubicSpline {
private final double[] fromValues;
private final double[] toValues;
private final double[] gradients;
public CubicSpline(List<Point> points) {
Collections.sort(points);
this.fromValues = new double[points.size()];
this.toValues = new double[points.size()];
this.gradients = new double[points.size()];
for(int i = 0; i < points.size(); i++) {
fromValues[i] = points.get(i).from;
toValues[i] = points.get(i).to;
gradients[i] = points.get(i).gradient;
}
}
public double apply(double in) {
return calculate(in, fromValues, toValues, gradients);
}
public static double calculate(double in, double[] fromValues, double[] toValues, double[] gradients) {
int pointIdx = floorBinarySearch(in, fromValues) - 1;
int pointIdxLast = fromValues.length - 1;
if (pointIdx < 0) { // If to left of first point return linear function intersecting said point using point's gradient
return gradients[0] * (in - fromValues[0]) + toValues[0];
} else if (pointIdx == pointIdxLast) { // Do same if to right of last point
return gradients[pointIdxLast] * (in - fromValues[pointIdxLast]) + toValues[pointIdxLast];
} else {
double fromLeft = fromValues[pointIdx];
double fromRight = fromValues[pointIdx + 1];
double toLeft = toValues[pointIdx];
double toRight = toValues[pointIdx + 1];
double gradientLeft = gradients[pointIdx];
double gradientRight = gradients[pointIdx + 1];
double fromDelta = fromRight - fromLeft;
double toDelta = toRight - toLeft;
double t = (in - fromLeft) / fromDelta;
return lerp(t, toLeft, toRight) + t * (1.0F - t) * lerp(t, gradientLeft * fromDelta - toDelta, -gradientRight * fromDelta + toDelta);
}
}
private static int floorBinarySearch(double targetValue, double[] values) {
int left = 0;
int right = values.length;
int idx = right - left;
while (idx > 0) {
int halfDelta = idx / 2;
int mid = left + halfDelta;
if (targetValue < values[mid]) {
idx = halfDelta;
} else {
left = mid + 1;
idx -= halfDelta + 1;
}
}
return left;
}
private static double lerp(double t, double a, double b) {
return a + t * (b - a);
}
public record Point(double from, double to, double gradient) implements Comparable<Point> {
@Override
public int compareTo(@NotNull CubicSpline.Point o) {
return Double.compare(from, o.from);
}
}
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.api.noise.NoiseSampler;
public class CubicSplineNoiseSampler extends Normalizer {
private final CubicSpline spline;
public CubicSplineNoiseSampler(NoiseSampler sampler, CubicSpline spline) {
super(sampler);
this.spline = spline;
}
@Override
public double normalize(double in) {
return spline.apply(in);
}
}
@@ -0,0 +1,33 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.Map;
public class ExpressionNormalizer extends Normalizer {
private final Expression expression;
public ExpressionNormalizer(NoiseSampler sampler, Map<String, Function> functions, String eq, Map<String, Double> vars)
throws ParseException {
super(sampler);
Parser p = new Parser();
Scope scope = new Scope();
scope.addInvocationVariable("in");
vars.forEach(scope::create);
functions.forEach(p::registerFunction);
expression = p.parse(eq, scope);
}
@Override
public double normalize(double in) {
return expression.evaluate(in);
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.addons.noise.paralithic;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import java.util.HashMap;
import java.util.Map;
public class FunctionUtil {
private FunctionUtil() {}
public static Map<String, Function> convertFunctionsAndSamplers(Map<String, FunctionTemplate> functions,
Map<String, DimensionApplicableNoiseSampler> samplers) throws ParseException {
Map<String, Function> functionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
functionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
samplers.forEach((id, sampler) -> functionMap.put(id,
sampler.getDimensions() == 2 ?
new NoiseFunction2(sampler.getSampler()) :
new NoiseFunction3(sampler.getSampler())));
return functionMap;
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final double dx, dy, dz;
public TranslateSampler(NoiseSampler sampler, double dx, double dy, double dz) {
this.sampler = sampler;
this.dx = dx;
this.dy = dy;
this.dz = dz;
}
@Override
public double noise(long seed, double x, double y) {
return sampler.noise(seed, x - dx, y - dz);
}
@Override
public double noise(long seed, double x, double y, double z) {
return sampler.noise(seed, x - dx, y - dy, z - dz);
}
}
@@ -240,99 +240,37 @@ public class CellularSampler extends NoiseFunction {
double centerX = x;
double centerY = y;
switch(distanceFunction) {
default:
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY;
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = switch(distanceFunction) {
case Manhattan -> fastAbs(vecX) + fastAbs(vecY);
case Hybrid -> (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
default -> vecX * vecX + vecY * vecY;
};
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -351,6 +289,7 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -382,120 +321,47 @@ public class CellularSampler extends NoiseFunction {
double centerY = y;
double centerZ = z;
switch(distanceFunction) {
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) +
(vecX * vecX + vecY * vecY + vecZ * vecZ);
double newDistance = 0;
switch(distanceFunction) {
case Euclidean, EuclideanSq -> newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
case Manhattan -> newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
case Hybrid -> {
newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
break;
default:
break;
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -514,6 +380,7 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -540,6 +407,7 @@ public class CellularSampler extends NoiseFunction {
Distance2Mul,
Distance2Div,
NoiseLookup,
LocalNoiseLookup,
Distance3,
Distance3Add,
Distance3Sub,
@@ -0,0 +1,66 @@
package com.dfsek.terra.addons.noise.samplers.noise;
public class DistanceSampler extends NoiseFunction {
private final DistanceFunction distanceFunction;
private final double ox, oy, oz;
private final boolean normalize;
private final double radius;
private final double distanceAtRadius;
public DistanceSampler(DistanceFunction distanceFunction, double ox, double oy, double oz, boolean normalize, double radius) {
frequency = 1;
this.distanceFunction = distanceFunction;
this.ox = ox;
this.oy = oy;
this.oz = oz;
this.normalize = normalize;
this.radius = radius;
this.distanceAtRadius = distance2d(distanceFunction, radius, 0); // distance2d and distance3d should return the same value
}
@Override
public double getNoiseRaw(long seed, double x, double y) {
double dx = x - ox;
double dy = y - oz;
if (normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius)) return 1;
double dist = distance2d(distanceFunction, dx, dy);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
@Override
public double getNoiseRaw(long seed, double x, double y, double z) {
double dx = x - ox;
double dy = y - oy;
double dz = z - oz;
if(normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius || fastAbs(dz) > radius)) return 1;
double dist = distance3d(distanceFunction, dx, dy, dz);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
private static double distance2d(DistanceFunction distanceFunction, double x, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + z*z);
case EuclideanSq -> x*x + z*z;
case Manhattan -> fastAbs(x) + fastAbs(z);
};
}
private static double distance3d(DistanceFunction distanceFunction, double x, double y, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + y*y + z*z);
case EuclideanSq -> x*x + y*y + z*z;
case Manhattan -> fastAbs(x) + fastAbs(y) + fastAbs(z);
};
}
public enum DistanceFunction {
Euclidean,
EuclideanSq,
Manhattan
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Polyhedral Development
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# config-ore-v2
Registers the default configuration for Terra Ores, `ORE`.
@@ -0,0 +1,11 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
relocate("net.jafama", "com.dfsek.terra.addons.ore.lib.jafama")
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020-2021 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.ore.v2;
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.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
public class OreAddon implements AddonInitializer {
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().registerConfigType(new OreConfigType(), addon.key("ORE"), 1))
.failThrough();
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 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.ore.v2;
import com.dfsek.terra.api.Platform;
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.structure.Structure;
import com.dfsek.terra.api.util.reflection.TypeKey;
public class OreConfigType implements ConfigType<OreTemplate, Structure> {
public static final TypeKey<Structure> ORE_TYPE_TOKEN = new TypeKey<>() {
};
private final OreFactory factory = new OreFactory();
@Override
public OreTemplate getTemplate(ConfigPack pack, Platform platform) {
return new OreTemplate();
}
@Override
public ConfigFactory<OreTemplate, Structure> getFactory() {
return factory;
}
@Override
public TypeKey<Structure> getTypeKey() {
return ORE_TYPE_TOKEN;
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2020-2021 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.ore.v2;
import com.dfsek.terra.addons.ore.v2.ores.VanillaOre;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.structure.Structure;
public class OreFactory implements ConfigFactory<OreTemplate, Structure> {
@Override
public VanillaOre build(OreTemplate config, Platform platform) {
BlockState m = config.getMaterial();
return new VanillaOre(m, config.getSize(), config.getReplaceable(), config.doPhysics(), config.isExposed(),
config.getMaterialOverrides());
}
}
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2020-2021 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.ore.v2;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Final;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.HashMap;
import java.util.Map;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.AbstractableTemplate;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.MaterialSet;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class OreTemplate implements AbstractableTemplate {
@Value("id")
@Final
private String id;
@Value("material")
private @Meta BlockState material;
@Value("material-overrides")
@Default
private @Meta Map<@Meta BlockType, @Meta BlockState> materials = new HashMap<>();
@Value("replace")
private @Meta MaterialSet replaceable;
@Value("physics")
@Default
private @Meta boolean physics = false;
@Value("size")
private @Meta double size;
@Value("exposed")
@Default
@Description("The chance that ore blocks bordering air will be discarded as candidates for ore. 0 = 0%, 1 = 100%")
private @Meta double exposed = 0.0f;
public boolean doPhysics() {
return physics;
}
public double getSize() {
return size;
}
public BlockState getMaterial() {
return material;
}
public MaterialSet getReplaceable() {
return replaceable;
}
public String getID() {
return id;
}
public Map<BlockType, BlockState> getMaterialOverrides() {
return materials;
}
public double isExposed() {
return exposed;
}
}
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2020-2021 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.ore.v2.ores;
import net.jafama.FastMath;
import java.util.BitSet;
import java.util.Map;
import java.util.Random;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.collection.MaterialSet;
import com.dfsek.terra.api.util.vector.Vector3Int;
import com.dfsek.terra.api.world.WritableWorld;
public class VanillaOre implements Structure {
private final BlockState material;
private final double size;
private final MaterialSet replaceable;
private final boolean applyGravity;
private final double exposed;
private final Map<BlockType, BlockState> materials;
public VanillaOre(BlockState material, double size, MaterialSet replaceable, boolean applyGravity,
double exposed, Map<BlockType, BlockState> materials) {
this.material = material;
this.size = size;
this.replaceable = replaceable;
this.applyGravity = applyGravity;
this.exposed = exposed;
this.materials = materials;
}
protected static boolean shouldNotDiscard(Random random, double chance) {
if(chance <= 0.0F) {
return true;
} else if(chance >= 1.0F) {
return false;
} else {
return random.nextFloat() >= chance;
}
}
public static double lerp(double t, double v0, double v1) {
return v0 + t * (v1 - v0);
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
float f = random.nextFloat() * (float) Math.PI;
double g = size / 8.0F;
int i = (int) FastMath.ceil((size / 16.0F * 2.0F + 1.0F) / 2.0F);
double startX = (double) location.getX() + FastMath.sin(f) * g;
double endX = (double) location.getX() - FastMath.sin(f) * g;
double startZ = (double) location.getZ() + FastMath.cos(f) * g;
double endZ = (double) location.getZ() - FastMath.cos(f) * g;
double startY = location.getY() + random.nextInt(3) - 2;
double endY = location.getY() + random.nextInt(3) - 2;
int x = (int) (location.getX() - FastMath.ceil(g) - i);
int y = location.getY() - 2 - i;
int z = (int) (location.getZ() - FastMath.ceil(g) - i);
int horizontalSize = (int) (2 * (FastMath.ceil(g) + i));
int verticalSize = 2 * (2 + i);
int i1 = 0;
BitSet bitSet = new BitSet(horizontalSize * verticalSize * horizontalSize);
int j1 = (int) size;
double[] ds = new double[j1 * 4];
for(int k1 = 0; k1 < j1; ++k1) {
float f1 = (float) k1 / (float) j1;
double d1 = lerp(f1, startX, endX);
double e1 = lerp(f1, startY, endY);
double g1 = lerp(f1, startZ, endZ);
double h1 = random.nextDouble() * (double) j1 / 16.0;
double l1 = ((FastMath.sin((float) Math.PI * f1) + 1.0F) * h1 + 1.0) / 2.0;
ds[k1 * 4] = d1;
ds[k1 * 4 + 1] = e1;
ds[k1 * 4 + 2] = g1;
ds[k1 * 4 + 3] = l1;
}
for(int k1 = 0; k1 < j1 - 1; ++k1) {
if(!(ds[k1 * 4 + 3] <= 0.0)) {
for(int m1 = k1 + 1; m1 < j1; ++m1) {
if(!(ds[m1 * 4 + 3] <= 0.0)) {
double d1 = ds[k1 * 4] - ds[m1 * 4];
double e1 = ds[k1 * 4 + 1] - ds[m1 * 4 + 1];
double g1 = ds[k1 * 4 + 2] - ds[m1 * 4 + 2];
double h1 = ds[k1 * 4 + 3] - ds[m1 * 4 + 3];
if(h1 * h1 > d1 * d1 + e1 * e1 + g1 * g1) {
if(h1 > 0.0) {
ds[m1 * 4 + 3] = -1.0;
} else {
ds[k1 * 4 + 3] = -1.0;
}
}
}
}
}
}
for(int m1 = 0; m1 < j1; ++m1) {
double d1 = ds[m1 * 4 + 3];
if(!(d1 < 0.0)) {
double e1 = ds[m1 * 4];
double g1 = ds[m1 * 4 + 1];
double h1 = ds[m1 * 4 + 2];
int n1 = (int) FastMath.max(FastMath.floor(e1 - d1), x);
int o1 = (int) FastMath.max(FastMath.floor(g1 - d1), y);
int p1 = (int) FastMath.max(FastMath.floor(h1 - d1), z);
int q1 = (int) FastMath.max(FastMath.floor(e1 + d1), n1);
int r1 = (int) FastMath.max(FastMath.floor(g1 + d1), o1);
int s1 = (int) FastMath.max(FastMath.floor(h1 + d1), p1);
for(int t1 = n1; t1 <= q1; ++t1) {
double u1 = ((double) t1 + 0.5 - e1) / d1;
if(u1 * u1 < 1.0) {
for(int v1 = o1; v1 <= r1; ++v1) {
double w1 = ((double) v1 + 0.5 - g1) / d1;
if(u1 * u1 + w1 * w1 < 1.0) {
for(int aa = p1; aa <= s1; ++aa) {
double ab = ((double) aa + 0.5 - h1) / d1;
if(u1 * u1 + w1 * w1 + ab * ab < 1.0 && !(v1 < world.getMinHeight() || v1 >= world.getMaxHeight())) {
int ac = t1 - x + (v1 - y) * horizontalSize + (aa - z) * horizontalSize * verticalSize;
if(!bitSet.get(ac)) {
bitSet.set(ac);
BlockType block = world.getBlockState(x, y, z).getBlockType();
if(shouldPlace(block, random, world, t1, v1, aa)) {
world.setBlockState(t1, v1, aa, getMaterial(block), isApplyGravity());
++i1;
break;
}
}
}
}
}
}
}
}
}
}
return i1 > 0;
}
public boolean shouldPlace(BlockType type, Random random, WritableWorld world, int x, int y, int z) {
if(!getReplaceable().contains(type)) {
return false;
} else if(shouldNotDiscard(random, exposed)) {
return true;
} else {
return !(world.getBlockState(x, y, z - 1).isAir() ||
world.getBlockState(x, y, z + 1).isAir() ||
world.getBlockState(x, y - 1, z).isAir() ||
world.getBlockState(x, y + 1, z).isAir() ||
world.getBlockState(x - 1, y, z).isAir() ||
world.getBlockState(x + 1, y, z).isAir());
}
}
public BlockState getMaterial(BlockType replace) {
return materials.getOrDefault(replace, material);
}
public MaterialSet getReplaceable() {
return replaceable;
}
public boolean isApplyGravity() {
return applyGravity;
}
}
@@ -0,0 +1,12 @@
schema-version: 1
contributors:
- Terra contributors
id: config-ore-v2
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.ore.v2.OreAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
@@ -14,8 +14,13 @@ import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OreAddon implements AddonInitializer {
private static final Logger logger = LoggerFactory.getLogger(OreAddon.class);
@Inject
private Platform platform;
@@ -29,5 +34,7 @@ public class OreAddon implements AddonInitializer {
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().registerConfigType(new OreConfigType(), addon.key("ORE"), 1))
.failThrough();
logger.warn("The ore-config addon is deprecated and scheduled for removal in Terra 7.0. It is recommended to use the ore-config-v2 addon for future pack development instead.");
}
}
@@ -30,15 +30,17 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
@Override
public BlockFunction build(List<Returnable<?>> argumentList, Position position) {
if(argumentList.size() < 4) throw new ParseException("Expected data", position);
Returnable<Boolean> booleanReturnable = new BooleanConstant(true, position);
if(argumentList.size() == 5) booleanReturnable = (Returnable<Boolean>) argumentList.get(4);
Returnable<Boolean> overwrite = new BooleanConstant(true, position);
if(argumentList.size() >= 5) overwrite = (Returnable<Boolean>) argumentList.get(4);
Returnable<Boolean> physics = new BooleanConstant(false, position);
if(argumentList.size() == 6) physics = (Returnable<Boolean>) argumentList.get(5);
if(argumentList.get(3) instanceof StringConstant) {
return new BlockFunction.Constant((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (StringConstant) argumentList.get(3),
booleanReturnable, platform, position);
overwrite, physics, platform, position);
}
return new BlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), booleanReturnable,
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), overwrite, physics,
platform, position);
}
@@ -52,7 +54,7 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 4 -> Returnable.ReturnType.BOOLEAN;
case 4, 5 -> Returnable.ReturnType.BOOLEAN;
default -> null;
};
}
@@ -35,10 +35,11 @@ public class BlockFunction implements Function<Void> {
protected final Platform platform;
private final Map<String, BlockState> data = new HashMap<>();
private final Returnable<Boolean> overwrite;
private final Returnable<Boolean> physics;
private final Position position;
public BlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> blockData,
Returnable<Boolean> overwrite, Platform platform, Position position) {
Returnable<Boolean> overwrite, Returnable<Boolean> physics, Platform platform, Position position) {
this.x = x;
this.y = y;
this.z = z;
@@ -46,6 +47,7 @@ public class BlockFunction implements Function<Void> {
this.overwrite = overwrite;
this.platform = platform;
this.position = position;
this.physics = physics;
}
@Override
@@ -76,7 +78,7 @@ public class BlockFunction implements Function<Void> {
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin());
BlockState current = arguments.getWorld().getBlockState(set);
if(overwrite.apply(implementationArguments, scope) || current.isAir()) {
arguments.getWorld().setBlockState(set, rot);
arguments.getWorld().setBlockState(set, rot, physics.apply(implementationArguments, scope));
}
} catch(RuntimeException e) {
logger.error("Failed to place block at location {}", arguments.getOrigin(), e);
@@ -92,8 +94,8 @@ public class BlockFunction implements Function<Void> {
private final BlockState state;
public Constant(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, StringConstant blockData,
Returnable<Boolean> overwrite, Platform platform, Position position) {
super(x, y, z, blockData, overwrite, platform, position);
Returnable<Boolean> overwrite, Returnable<Boolean> physics, Platform platform, Position position) {
super(x, y, z, blockData, overwrite, physics, platform, position);
this.state = platform.getWorldHandle().createBlockState(blockData.getConstant());
}
@@ -17,10 +17,17 @@
package com.dfsek.terra.config.loaders;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import com.dfsek.tectonic.impl.MapConfiguration;
import com.dfsek.terra.api.config.meta.Meta;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
@@ -36,11 +43,26 @@ public class RangeLoader implements TypeLoader<Range> {
public Range load(@NotNull AnnotatedType type, @NotNull Object o, @NotNull ConfigLoader configLoader, DepthTracker depthTracker)
throws LoadException {
if(o instanceof Map) {
Map<String, Integer> map = (Map<String, Integer>) o;
return new ConstantRange(map.get("min"), map.get("max"));
return configLoader.load(new RangeMapTemplate(), new MapConfiguration((Map<String, Object>) o), depthTracker).get();
} else {
int h = configLoader.loadType(Integer.class, o, depthTracker);
return new ConstantRange(h, h + 1);
}
}
/*
* Template needed so keys can be meta annotated, otherwise the loader could just grab keys directly from the object
*/
public static class RangeMapTemplate implements ObjectTemplate<Range> {
@Value("min")
private @Meta int min;
@Value("max")
private @Meta int max;
@Override
public Range get() {
return new ConstantRange(min, max);
}
}
}
@@ -68,7 +68,7 @@ public class MetaListLikePreprocessor extends MetaPreprocessor<Meta> {
if(!(metaValue instanceof List)) {
throw new LoadException(
"MetaList/Set injection candidate must be list, is type " + metaValue.getClass().getCanonicalName(),
"Meta list / set injection (via <<) must point to a list. '" + meta + "' points to type " + metaValue.getClass().getCanonicalName(),
depthTracker);
}
-1
View File
@@ -8,5 +8,4 @@ terra.license=MIT
# Gradle options
org.gradle.jvmargs=-Xmx4096M -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.warning.mode=all
#org.gradle.parallel=true
+1 -1
View File
@@ -12,12 +12,12 @@ dependencies {
shaded(project(":platforms:bukkit:nms:v1_19_R1", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_19_R2", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_19_R3", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_20_R1", configuration = "reobf"))
shaded("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
tasks {
shadowJar {
relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats")
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("com.google.common", "com.dfsek.terra.lib.google.common")
relocate("org.apache.logging.slf4j", "com.dfsek.terra.lib.slf4j-over-log4j")
@@ -0,0 +1,12 @@
package com.dfsek.terra.bukkit.util;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
public class BukkitUtils {
public static boolean isLiquid(BlockData blockState) {
Material material = blockState.getMaterial();
return material == Material.WATER || material == Material.LAVA;
}
}
@@ -1,6 +1,9 @@
package com.dfsek.terra.bukkit.world;
import com.dfsek.terra.bukkit.util.BukkitUtils;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.LimitedRegion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,9 +50,14 @@ public class BukkitProtoWorld implements ProtoWorld {
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
access(x, y, z, () -> {
delegate.setBlockData(x, y, z, BukkitAdapter.adapt(data));
BlockData bukkitData = BukkitAdapter.adapt(data);
delegate.setBlockData(x, y, z, bukkitData);
if(physics) {
delegate.scheduleBlockUpdate(x, y, z);
if (BukkitUtils.isLiquid(bukkitData)) {
delegate.scheduleFluidUpdate(x, y, z);
} else {
delegate.scheduleBlockUpdate(x, y, z);
}
}
});
}
@@ -6,7 +6,7 @@ repositories {
dependencies {
api(project(":platforms:bukkit:common"))
paperDevBundle(Versions.Bukkit.paperDevBundle)
paperDevBundle("1.19.4-R0.1-SNAPSHOT")
implementation("xyz.jpenilla", "reflection-remapper", "0.1.0-SNAPSHOT")
}
@@ -10,6 +10,7 @@ import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.Beardifier;
@@ -152,18 +153,14 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
@Override
public @NotNull NoiseColumn getBaseColumn(int x, int z, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
/*
BlockState[] array = new BlockState[world.getHeight()];
WorldProperties properties = new NMSWorldProperties(seed, world);
BiomeProvider biomeProvider = pack.getBiomeProvider().caching(properties);
BiomeProvider biomeProvider = pack.getBiomeProvider();
for(int y = properties.getMaxHeight() - 1; y >= properties.getMinHeight(); y--) {
array[y - properties.getMinHeight()] = ((CraftBlockData) delegate.getBlock(properties, x, y, z, biomeProvider)
.getHandle()).getState();
}
return new NoiseColumn(getMinY(), array);
*/
return vanilla.getBaseColumn(x, z, world, noiseConfig);
}
@Override
@@ -0,0 +1,17 @@
apply(plugin = "io.papermc.paperweight.userdev")
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
api(project(":platforms:bukkit:common"))
paperDevBundle(Versions.Bukkit.paperDevBundle)
implementation("xyz.jpenilla", "reflection-remapper", "0.1.0-SNAPSHOT")
}
tasks {
assemble {
dependsOn("reobfJar")
}
}
@@ -0,0 +1,101 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.biome.Biome;
import org.bukkit.NamespacedKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
import com.dfsek.terra.registry.master.ConfigRegistry;
public class AwfulBukkitHacks {
private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.class);
private static final Map<ResourceLocation, List<ResourceLocation>> terraBiomeMap = new HashMap<>();
public static void registerBiomes(ConfigRegistry configRegistry) {
try {
LOGGER.info("Hacking biome registry...");
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) RegistryFetcher.biomeRegistry();
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, false);
configRegistry.forEach(pack -> pack.getRegistry(com.dfsek.terra.api.world.biome.Biome.class).forEach((key, biome) -> {
try {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey();
ResourceLocation vanillaMinecraftKey = new ResourceLocation(vanillaBukkitKey.getNamespace(), vanillaBukkitKey.getKey());
Biome platform = NMSBiomeInjector.createBiome(biome, Objects.requireNonNull(biomeRegistry.get(vanillaMinecraftKey)));
ResourceKey<Biome> delegateKey = ResourceKey.create(
Registries.BIOME,
new ResourceLocation("terra", NMSBiomeInjector.createBiomeID(pack, key))
);
Reference<Biome> holder = biomeRegistry.register(delegateKey, platform, Lifecycle.stable());
Reflection.REFERENCE.invokeBindValue(holder, platform); // IMPORTANT: bind holder.
platformBiome.getContext().put(new NMSBiomeInfo(delegateKey));
terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.location());
LOGGER.debug("Registered biome: " + delegateKey);
} catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}));
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, true); // freeze registry again :)
LOGGER.info("Doing tag garbage....");
Map<TagKey<Biome>, List<Holder<Biome>>> collect = biomeRegistry
.getTags() // streamKeysAndEntries
.collect(HashMap::new,
(map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())),
HashMap::putAll);
terraBiomeMap
.forEach((vb, terraBiomes) ->
NMSBiomeInjector.getEntry(biomeRegistry, vb).ifPresentOrElse(
vanilla -> terraBiomes.forEach(
tb -> NMSBiomeInjector.getEntry(biomeRegistry, tb).ifPresentOrElse(
terra -> {
LOGGER.debug("{} (vanilla for {}): {}",
vanilla.unwrapKey().orElseThrow().location(),
terra.unwrapKey().orElseThrow().location(),
vanilla.tags().toList());
vanilla.tags()
.forEach(tag -> collect
.computeIfAbsent(tag, t -> new ArrayList<>())
.add(terra));
},
() -> LOGGER.error("No such biome: {}", tb))),
() -> LOGGER.error("No vanilla biome: {}", vb)));
biomeRegistry.resetTags();
biomeRegistry.bindTags(ImmutableMap.copyOf(collect));
} catch(SecurityException | IllegalArgumentException exception) {
throw new RuntimeException(exception);
}
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import com.dfsek.terra.api.properties.Properties;
public record NMSBiomeInfo(ResourceKey<Biome> biomeKey) implements Properties {
}
@@ -0,0 +1,83 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.core.Holder;
import java.util.stream.Stream;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.config.VanillaBiomeProperties;
import static net.minecraft.world.level.biome.Biome.ClimateSettings;
import static net.minecraft.world.level.biome.Biome.Precipitation;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, ResourceLocation identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.flatMap(registry::getHolder);
}
public static Biome createBiome(com.dfsek.terra.api.world.biome.Biome biome, Biome vanilla)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder();
builder
.downfall(vanilla.climateSettings.downfall())
.temperature(vanilla.getBaseTemperature())
.mobSpawnSettings(vanilla.getMobSettings())
.generationSettings(vanilla.getGenerationSettings());
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
effects.grassColorModifier(vanilla.getSpecialEffects().getGrassColorModifier());
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()))
.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
.waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
.skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()));
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride);
} else {
// grass
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
vanilla.getAmbientLoop().ifPresent(effects::ambientLoopSound);
vanilla.getAmbientAdditions().ifPresent(effects::ambientAdditionsSound);
vanilla.getAmbientMood().ifPresent(effects::ambientMoodSound);
vanilla.getBackgroundMusic().ifPresent(effects::backgroundMusic);
vanilla.getAmbientParticle().ifPresent(effects::ambientParticle);
builder.specialEffects(effects.build());
return builder.build();
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
@@ -0,0 +1,47 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import java.util.stream.Stream;
import net.minecraft.core.Registry;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Climate.Sampler;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
public class NMSBiomeProvider extends BiomeSource {
private final BiomeProvider delegate;
private final long seed;
private final Registry<Biome> biomeRegistry = RegistryFetcher.biomeRegistry();
public NMSBiomeProvider(BiomeProvider delegate, long seed) {
super();
this.delegate = delegate;
this.seed = seed;
}
@Override
protected Stream<Holder<Biome>> collectPossibleBiomes() {
return delegate.stream()
.map(biome -> RegistryFetcher.biomeRegistry()
.getHolderOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
.get(NMSBiomeInfo.class)
.biomeKey()));
}
@Override
protected @NotNull Codec<? extends BiomeSource> codec() {
return BiomeSource.CODEC;
}
@Override
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
return biomeRegistry.getHolderOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
.getPlatformBiome()).getContext()
.get(NMSBiomeInfo.class)
.biomeKey());
}
}
@@ -0,0 +1,170 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.NoiseColumn;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.Beardifier;
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.blending.Blender;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.info.WorldProperties;
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
public class NMSChunkGeneratorDelegate extends ChunkGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class);
private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate;
private final ChunkGenerator vanilla;
private final ConfigPack pack;
private final long seed;
public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) {
super(biomeProvider);
this.delegate = pack.getGeneratorProvider().newInstance(pack);
this.vanilla = vanilla;
this.pack = pack;
this.seed = seed;
}
@Override
protected @NotNull Codec<? extends ChunkGenerator> codec() {
return ChunkGenerator.CODEC;
}
@Override
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull RandomState noiseConfig, @NotNull BiomeManager world,
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk, @NotNull Carving carverStep) {
// no-op
}
@Override
public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig,
@NotNull ChunkAccess chunk) {
// no-op
}
@Override
public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk,
@NotNull StructureManager structureAccessor) {
vanilla.applyBiomeDecoration(world, chunk, structureAccessor);
}
@Override
public void spawnOriginalMobs(@NotNull WorldGenRegion region) {
vanilla.spawnOriginalMobs(region);
}
@Override
public int getGenDepth() {
return vanilla.getGenDepth();
}
@Override
public @NotNull CompletableFuture<ChunkAccess> fillFromNoise(@NotNull Executor executor, @NotNull Blender blender,
@NotNull RandomState noiseConfig,
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk)
.thenApply(c -> {
LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor);
BiomeProvider biomeProvider = pack.getBiomeProvider();
PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class);
if(compatibilityOptions.isBeard()) {
beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()), biomeProvider, compatibilityOptions);
}
return c;
});
}
private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider,
PreLoadCompatibilityOptions compatibilityOptions) {
Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos());
double threshold = compatibilityOptions.getBeardThreshold();
double airThreshold = compatibilityOptions.getAirThreshold();
int xi = chunk.getPos().x << 4;
int zi = chunk.getPos().z << 4;
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
int depth = 0;
for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) {
double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi));
if(noise > threshold) {
chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate
.getPalette(x + xi, y, z + zi, world, biomeProvider)
.get(depth, x + xi, y, z + zi, world.getSeed())).getHandle()).getState(), false);
depth++;
} else if(noise < airThreshold) {
chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false);
} else {
depth = 0;
}
}
}
}
}
@Override
public int getSeaLevel() {
return vanilla.getSeaLevel();
}
@Override
public int getMinY() {
return vanilla.getMinY();
}
@Override
public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
WorldProperties properties = new NMSWorldProperties(seed, world);
int y = properties.getMaxHeight();
BiomeProvider biomeProvider = pack.getBiomeProvider();
while(y >= getMinY() && !heightmap.isOpaque().test(
((CraftBlockData) delegate.getBlock(properties, x, y - 1, z, biomeProvider).getHandle()).getState())) {
y--;
}
return y;
}
@Override
public @NotNull NoiseColumn getBaseColumn(int x, int z, @NotNull LevelHeightAccessor world, @NotNull RandomState noiseConfig) {
BlockState[] array = new BlockState[world.getHeight()];
WorldProperties properties = new NMSWorldProperties(seed, world);
BiomeProvider biomeProvider = pack.getBiomeProvider();
for(int y = properties.getMaxHeight() - 1; y >= properties.getMinHeight(); y--) {
array[y - properties.getMinHeight()] = ((CraftBlockData) delegate.getBlock(properties, x, y, z, biomeProvider)
.getHandle()).getState();
}
return new NoiseColumn(getMinY(), array);
}
@Override
public void addDebugScreenInfo(@NotNull List<String> text, @NotNull RandomState noiseConfig, @NotNull BlockPos pos) {
}
}
@@ -0,0 +1,15 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import org.bukkit.Bukkit;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.nms.Initializer;
public class NMSInitializer implements Initializer {
@Override
public void initialize(PlatformImpl platform) {
AwfulBukkitHacks.registerBiomes(platform.getRawConfigRegistry());
Bukkit.getPluginManager().registerEvents(new NMSInjectListener(), platform.getPlugin());
}
}
@@ -0,0 +1,48 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkGenerator;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper;
public class NMSInjectListener implements Listener {
private static final Logger LOGGER = LoggerFactory.getLogger(NMSInjectListener.class);
private static final Set<World> INJECTED = new HashSet<>();
private static final ReentrantLock INJECT_LOCK = new ReentrantLock();
@EventHandler
public void onWorldInit(WorldInitEvent event) {
if(!INJECTED.contains(event.getWorld()) &&
event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) {
INJECT_LOCK.lock();
INJECTED.add(event.getWorld());
LOGGER.info("Preparing to take over the world: {}", event.getWorld().getName());
CraftWorld craftWorld = (CraftWorld) event.getWorld();
ServerLevel serverWorld = craftWorld.getHandle();
ConfigPack pack = bukkitChunkGeneratorWrapper.getPack();
ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator();
NMSBiomeProvider provider = new NMSBiomeProvider(pack.getBiomeProvider(), craftWorld.getSeed());
serverWorld.getChunkSource().chunkMap.generator = new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed());
LOGGER.info("Successfully injected into world.");
INJECT_LOCK.unlock();
}
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.world.level.LevelHeightAccessor;
import com.dfsek.terra.api.world.info.WorldProperties;
public class NMSWorldProperties implements WorldProperties {
private final long seed;
private final LevelHeightAccessor height;
public NMSWorldProperties(long seed, LevelHeightAccessor height) {
this.seed = seed;
this.height = height;
}
@Override
public Object getHandle() {
return height;
}
@Override
public long getSeed() {
return seed;
}
@Override
public int getMaxHeight() {
return height.getMaxBuildHeight();
}
@Override
public int getMinHeight() {
return height.getMinBuildHeight();
}
}
@@ -0,0 +1,50 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.MappedRegistry;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureManager;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.MethodName;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
public class Reflection {
public static final MappedRegistryProxy MAPPED_REGISTRY;
public static final StructureManagerProxy STRUCTURE_MANAGER;
public static final ReferenceProxy REFERENCE;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
Reflection.class.getClassLoader());
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
REFERENCE = reflectionProxyFactory.reflectionProxy(ReferenceProxy.class);
}
@Proxies(MappedRegistry.class)
public interface MappedRegistryProxy {
@FieldSetter("frozen")
void setFrozen(MappedRegistry<?> instance, boolean frozen);
}
@Proxies(StructureManager.class)
public interface StructureManagerProxy {
@FieldGetter("level")
LevelAccessor getLevel(StructureManager instance);
}
@Proxies(Holder.Reference.class)
public interface ReferenceProxy {
@MethodName("bindValue")
<T> void invokeBindValue(Reference<T> instance, T value);
}
}
@@ -0,0 +1,24 @@
package com.dfsek.terra.bukkit.nms.v1_20_R1;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.level.biome.Biome;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
public class RegistryFetcher {
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> key) {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return dedicatedserver
.registryAccess()
.registryOrThrow(key);
}
public static Registry<Biome> biomeRegistry() {
return getRegistry(Registries.BIOME);
}
}
+2 -2
View File
@@ -50,10 +50,10 @@ loom {
launches {
named("client") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
}
named("server") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
}
}
}
@@ -17,35 +17,16 @@
package com.dfsek.terra.fabric;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricServerCommandManager;
import net.fabricmc.api.ModInitializer;
import net.minecraft.server.command.ServerCommandSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.lifecycle.LifecycleEntryPoint;
public class FabricEntryPoint implements ModInitializer {
private static final Logger logger = LoggerFactory.getLogger(FabricEntryPoint.class);
public class FabricEntryPoint extends LifecycleEntryPoint implements ModInitializer {
private static final FabricPlatform TERRA_PLUGIN = new FabricPlatform();
@Override
public void onInitialize() {
logger.info("Initializing Terra Fabric mod...");
FabricServerCommandManager<CommandSender> manager = new FabricServerCommandManager<>(
CommandExecutionCoordinator.simpleCoordinator(),
serverCommandSource -> (CommandSender) serverCommandSource,
commandSender -> (ServerCommandSource) commandSender
);
manager.brigadierManager().setNativeNumberSuggestions(false);
TERRA_PLUGIN.getEventManager().callEvent(new CommandRegistrationEvent(manager));
initialize("Fabric", TERRA_PLUGIN);
}
}
@@ -17,43 +17,24 @@
package com.dfsek.terra.fabric;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import net.fabricmc.loader.api.FabricLoader;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dfsek.terra.addon.EphemeralAddon;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.lifecycle.LifecyclePlatform;
public class FabricPlatform extends LifecyclePlatform {
private static final Logger LOGGER = LoggerFactory.getLogger(FabricPlatform.class);
@Override
protected Collection<BaseAddon> getPlatformMods() {
return FabricLoader.getInstance().getAllMods().stream().flatMap(mod -> {
String id = mod.getMetadata().getId();
if(id.equals("terra") || id.equals("minecraft") || id.equals("java")) return Stream.empty();
try {
Version version = Versions.parseVersion(mod.getMetadata().getVersion().getFriendlyString());
return Stream.<BaseAddon>of(new EphemeralAddon(version, "fabric:" + id));
} catch(ParseException e) {
LOGGER.warn(
"Mod {}, version {} does not follow semantic versioning specification, Terra addons will be unable to depend on " +
"it.",
id, mod.getMetadata().getVersion().getFriendlyString());
}
return Stream.empty();
}).collect(Collectors.toList());
return FabricLoader.getInstance().getAllMods().stream().flatMap(
mod -> parseModData(mod.getMetadata().getId(), mod.getMetadata().getVersion().getFriendlyString())).collect(
Collectors.toList());
}
@Override
@@ -28,6 +28,6 @@
"depends": {
"fabricloader": ">=0.14.2",
"java": ">=17",
"minecraft": "1.19.x"
"minecraft": "1.20.x"
}
}
+2 -2
View File
@@ -39,11 +39,11 @@ loom {
launches {
named("client") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
property("mixin.env.disableRefMap", "true")
}
named("server") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
property("mixin.env.disableRefMap", "true")
}
}
@@ -18,6 +18,7 @@
package com.dfsek.terra.mod.mixin.implementations.terra.block.entity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.entity.SignText;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Final;
@@ -34,17 +35,17 @@ import com.dfsek.terra.api.block.entity.Sign;
@Implements(@Interface(iface = Sign.class, prefix = "terra$"))
public abstract class SignBlockEntityMixin {
@Shadow
@Final
private Text[] texts;
private SignText frontText;
@Shadow
public abstract void setTextOnRow(int row, Text text);
public abstract boolean setText(SignText text, boolean front);
public void terra$setLine(int index, @NotNull String line) throws IndexOutOfBoundsException {
setTextOnRow(index, Text.literal(line));
setText(frontText.withMessage(index, Text.literal(line)), true);
}
public @NotNull String[] terra$getLines() {
Text[] texts = frontText.getMessages(false);
String[] lines = new String[texts.length];
for(int i = 0; i < texts.length; i++) {
lines[i] = texts[i].getString();
@@ -53,7 +54,7 @@ public abstract class SignBlockEntityMixin {
}
public @NotNull String terra$getLine(int index) throws IndexOutOfBoundsException {
return texts[index].getString();
return frontText.getMessage(index, false).getString();
}
public void terra$applyState(String state) {
@@ -17,8 +17,11 @@
package com.dfsek.terra.mod.mixin.implementations.terra.chunk;
import net.minecraft.block.Block;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.chunk.Chunk.TickSchedulers;
import net.minecraft.world.chunk.WorldChunk;
import net.minecraft.world.tick.OrderedTick;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
@@ -47,8 +50,21 @@ public abstract class WorldChunkMixin {
@Nullable
public abstract net.minecraft.block.BlockState setBlockState(BlockPos pos, net.minecraft.block.BlockState state, boolean moved);
@Shadow
public abstract TickSchedulers getTickSchedulers();
public void terra$setBlock(int x, int y, int z, BlockState data, boolean physics) {
setBlockState(new BlockPos(x, y, z), (net.minecraft.block.BlockState) data, false);
BlockPos blockPos = new BlockPos(x, y, z);
setBlockState(blockPos, (net.minecraft.block.BlockState) data, false);
if (physics) {
net.minecraft.block.BlockState state = ((net.minecraft.block.BlockState) data);
if (state.isLiquid()) {
world.getFluidTickScheduler().scheduleTick(OrderedTick.create(state.getFluidState().getFluid(), blockPos));
} else {
world.getBlockTickScheduler().scheduleTick(OrderedTick.create(state.getBlock(), blockPos));
}
}
}
public void terra$setBlock(int x, int y, int z, @NotNull BlockState blockState) {
@@ -37,9 +37,6 @@ import com.dfsek.terra.api.entity.Player;
@Mixin(ServerCommandSource.class)
@Implements(@Interface(iface = CommandSender.class, prefix = "terra$"))
public abstract class ServerCommandSourceMixin {
@Shadow
public abstract void sendFeedback(Text message, boolean broadcastToOps);
@Shadow
public abstract ServerPlayerEntity getPlayer() throws CommandSyntaxException;
@@ -47,8 +44,11 @@ public abstract class ServerCommandSourceMixin {
@Nullable
public abstract net.minecraft.entity.@Nullable Entity getEntity();
@Shadow
public abstract void sendMessage(Text message);
public void terra$sendMessage(String message) {
sendFeedback(Text.literal(message), true);
sendMessage(Text.literal(message));
}
@Nullable
@@ -15,6 +15,11 @@ dependencies {
minecraft("com.mojang:minecraft:${Versions.Mod.minecraft}")
mappings("net.fabricmc:yarn:${Versions.Mod.yarn}:v2")
modImplementation("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud) {
exclude("net.fabricmc")
exclude("net.fabricmc.fabric-api")
}
}
loom {
@@ -0,0 +1,34 @@
package com.dfsek.terra.lifecycle;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricServerCommandManager;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.mod.MinecraftAddon;
import net.minecraft.server.command.ServerCommandSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LifecycleEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(LifecycleEntryPoint.class);
protected static void initialize(String modName, LifecyclePlatform platform) {
logger.info("Initializing Terra {} mod...", modName);
FabricServerCommandManager<CommandSender> manager = new FabricServerCommandManager<>(
CommandExecutionCoordinator.simpleCoordinator(),
serverCommandSource -> (CommandSender) serverCommandSource,
commandSender -> (ServerCommandSource) commandSender
);
manager.brigadierManager().setNativeNumberSuggestions(false);
platform.getEventManager().callEvent(new CommandRegistrationEvent(manager));
}
}
@@ -2,6 +2,7 @@ package com.dfsek.terra.lifecycle;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import com.dfsek.terra.lifecycle.util.BiomeUtil;
@@ -20,6 +21,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import java.util.concurrent.atomic.AtomicReference;
import com.dfsek.terra.addon.EphemeralAddon;
@@ -103,12 +105,26 @@ public abstract class LifecyclePlatform extends ModPlatform {
LOGGER.warn("Failed to parse Minecraft version", e);
}
}
addons.addAll(getPlatformMods());
return addons;
}
protected Stream<EphemeralAddon> parseModData(String id, String modVersion) {
if(id.equals("terra") || id.equals("minecraft") || id.equals("java")) return Stream.empty();
try {
Version version = Versions.parseVersion(modVersion);
return Stream.of(new EphemeralAddon(version, "quilt:" + id));
} catch(ParseException e) {
LOGGER.warn(
"Mod {}, version {} does not follow semantic versioning specification, Terra addons will be unable to depend on " +
"it.",
id, modVersion);
}
return Stream.empty();
}
@Override
public Registry<DimensionType> dimensionTypeRegistry() {
return DIMENSIONS.get();
+2 -2
View File
@@ -50,10 +50,10 @@ loom {
launches {
named("client") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
}
named("server") {
property("fabric.log.level", "debug")
property("fabric.log.level", "info")
}
}
}
@@ -17,36 +17,17 @@
package com.dfsek.terra.quilt;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricServerCommandManager;
import net.minecraft.server.command.ServerCommandSource;
import org.quiltmc.loader.api.ModContainer;
import org.quiltmc.qsl.base.api.entrypoint.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.lifecycle.LifecycleEntryPoint;
public class QuiltEntryPoint implements ModInitializer {
private static final Logger logger = LoggerFactory.getLogger(QuiltEntryPoint.class);
public class QuiltEntryPoint extends LifecycleEntryPoint implements ModInitializer {
private static final QuiltPlatform TERRA_PLUGIN = new QuiltPlatform();
@Override
public void onInitialize(ModContainer container) {
logger.info("Initializing Terra Quilt mod...");
FabricServerCommandManager<CommandSender> manager = new FabricServerCommandManager<>(
CommandExecutionCoordinator.simpleCoordinator(),
serverCommandSource -> (CommandSender) serverCommandSource,
commandSender -> (ServerCommandSource) commandSender
);
manager.brigadierManager().setNativeNumberSuggestions(false);
TERRA_PLUGIN.getEventManager().callEvent(new CommandRegistrationEvent(manager));
initialize("Quilt", TERRA_PLUGIN);
}
}
@@ -17,43 +17,23 @@
package com.dfsek.terra.quilt;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import org.jetbrains.annotations.NotNull;
import org.quiltmc.loader.api.QuiltLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dfsek.terra.addon.EphemeralAddon;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.lifecycle.LifecyclePlatform;
public class QuiltPlatform extends LifecyclePlatform {
private static final Logger LOGGER = LoggerFactory.getLogger(QuiltPlatform.class);
@Override
protected Collection<BaseAddon> getPlatformMods() {
return QuiltLoader.getAllMods().stream().flatMap(mod -> {
String id = mod.metadata().id();
if(id.equals("terra") || id.equals("minecraft") || id.equals("java")) return Stream.empty();
try {
Version version = Versions.parseVersion(mod.metadata().version().raw());
return Stream.of(new EphemeralAddon(version, "quilt:" + id));
} catch(ParseException e) {
LOGGER.warn(
"Mod {}, version {} does not follow semantic versioning specification, Terra addons will be unable to depend on " +
"it.",
id, mod.metadata().version().raw());
}
return Stream.empty();
}).collect(Collectors.toList());
return QuiltLoader.getAllMods().stream().flatMap(mod -> parseModData(mod.metadata().id(), mod.metadata().version().raw())).collect(
Collectors.toList());
}
@Override