From 1863ba29fc68ff31e377bc92e72123f2d65bbc31 Mon Sep 17 00:00:00 2001 From: dfsek Date: Sun, 31 Jan 2021 01:41:05 -0700 Subject: [PATCH] implement UserDefinedFunction --- .../noise/samplers/ExpressionSampler.java | 17 +++++++-- .../terra/api/math/parsii/RandomFunction.java | 30 ---------------- .../defined/DefinedFunctionTemplate.java | 10 ++++++ .../parsii/defined/UserDefinedFunction.java | 35 +++++++++++++++++++ .../dfsek/terra/config/GenericLoaders.java | 2 ++ .../config/builder/GeneratorBuilder.java | 13 +++++-- .../terra/config/factories/BiomeFactory.java | 11 +++++- .../config/function/FunctionTemplate.java | 28 +++++++++++++++ .../terra/config/pack/ConfigPackTemplate.java | 9 +++++ .../terra/config/templates/BiomeTemplate.java | 31 +++++++++++++++- 10 files changed, 148 insertions(+), 38 deletions(-) delete mode 100644 common/src/main/java/com/dfsek/terra/api/math/parsii/RandomFunction.java create mode 100644 common/src/main/java/com/dfsek/terra/api/math/parsii/defined/DefinedFunctionTemplate.java create mode 100644 common/src/main/java/com/dfsek/terra/api/math/parsii/defined/UserDefinedFunction.java create mode 100644 common/src/main/java/com/dfsek/terra/config/loaders/config/function/FunctionTemplate.java diff --git a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/ExpressionSampler.java b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/ExpressionSampler.java index 3e93cb785..003f692dd 100644 --- a/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/ExpressionSampler.java +++ b/common/src/main/java/com/dfsek/terra/api/math/noise/samplers/ExpressionSampler.java @@ -1,17 +1,20 @@ package com.dfsek.terra.api.math.noise.samplers; import com.dfsek.terra.api.math.noise.NoiseSampler; -import com.dfsek.terra.api.math.parsii.RandomFunction; +import com.dfsek.terra.api.math.parsii.defined.UserDefinedFunction; import com.dfsek.terra.api.math.parsii.noise.NoiseFunction2; import com.dfsek.terra.api.math.parsii.noise.NoiseFunction3; import com.dfsek.terra.api.util.seeded.NoiseSeeded; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import parsii.eval.Expression; import parsii.eval.Parser; import parsii.eval.Scope; import parsii.eval.Variable; import parsii.tokenizer.ParseException; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Sampler implementation using parsii expression @@ -22,7 +25,7 @@ public class ExpressionSampler implements NoiseSampler { private final Variable y; private final Variable z; - public ExpressionSampler(String equation, Scope parent, long seed, Map functions) throws ParseException { + public ExpressionSampler(String equation, Scope parent, long seed, Map functions, Map definedFunctions) throws ParseException { Parser parser = new Parser(); Scope scope = new Scope().withParent(parent); @@ -41,7 +44,15 @@ public class ExpressionSampler implements NoiseSampler { } }); - parser.registerFunction("rand", new RandomFunction()); + for(Map.Entry entry : definedFunctions.entrySet()) { + String id = entry.getKey(); + FunctionTemplate fun = entry.getValue(); + + Scope functionScope = new Scope().withParent(parent); + List variables = fun.getArgs().stream().map(functionScope::create).collect(Collectors.toList()); + + parser.registerFunction(id, new UserDefinedFunction(parser.parse(fun.getFunction(), functionScope), variables)); + } this.expression = parser.parse(equation, scope); } diff --git a/common/src/main/java/com/dfsek/terra/api/math/parsii/RandomFunction.java b/common/src/main/java/com/dfsek/terra/api/math/parsii/RandomFunction.java deleted file mode 100644 index 1804ef9fb..000000000 --- a/common/src/main/java/com/dfsek/terra/api/math/parsii/RandomFunction.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dfsek.terra.api.math.parsii; - -import com.dfsek.terra.api.util.FastRandom; -import parsii.eval.Expression; -import parsii.eval.Function; - -import java.util.List; - -/** - * Provides access to a PRNG ({@link com.dfsek.terra.api.util.FastRandom}) - *

- * Takes 1 argument, which sets the seed - */ -public class RandomFunction implements Function { - @Override - public int getNumberOfArguments() { - return 1; - } - - @Override - public double eval(List list) { - long seed = (long) list.get(0).evaluate(); - return new FastRandom(seed).nextDouble(); - } - - @Override - public boolean isNaturalFunction() { - return true; - } -} diff --git a/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/DefinedFunctionTemplate.java b/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/DefinedFunctionTemplate.java new file mode 100644 index 000000000..639b20702 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/DefinedFunctionTemplate.java @@ -0,0 +1,10 @@ +package com.dfsek.terra.api.math.parsii.defined; + +import com.dfsek.terra.api.util.seeded.SeededBuilder; + +public class DefinedFunctionTemplate implements SeededBuilder { + @Override + public UserDefinedFunction apply(Long aLong) { + return null; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/UserDefinedFunction.java b/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/UserDefinedFunction.java new file mode 100644 index 000000000..d9e670683 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/math/parsii/defined/UserDefinedFunction.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.api.math.parsii.defined; + +import parsii.eval.Expression; +import parsii.eval.Function; +import parsii.eval.Variable; + +import java.util.List; + +public class UserDefinedFunction implements Function { + private final Expression expression; + private final List variables; + + public UserDefinedFunction(Expression expression, List variables) { + this.expression = expression; + this.variables = variables; + } + + @Override + public int getNumberOfArguments() { + return variables.size(); + } + + @Override + public synchronized double eval(List args) { + for(int i = 0; i < variables.size(); i++) { + variables.get(i).setValue(args.get(i).evaluate()); + } + return expression.evaluate(); + } + + @Override + public boolean isNaturalFunction() { + return true; + } +} diff --git a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java index fdb009286..1e8cda3de 100644 --- a/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java +++ b/common/src/main/java/com/dfsek/terra/config/GenericLoaders.java @@ -28,6 +28,7 @@ import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.Borde import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.ReplaceListMutatorTemplate; import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.ReplaceMutatorTemplate; import com.dfsek.terra.config.loaders.config.biome.templates.stage.mutator.SmoothMutatorTemplate; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; import com.dfsek.terra.config.loaders.config.sampler.templates.DomainWarpTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.FastNoiseTemplate; @@ -77,6 +78,7 @@ public class GenericLoaders implements LoaderRegistrar { .registerLoader(ReplaceListMutatorTemplate.class, ReplaceListMutatorTemplate::new) .registerLoader(BorderMutatorTemplate.class, BorderMutatorTemplate::new) .registerLoader(BorderListMutatorTemplate.class, BorderListMutatorTemplate::new) + .registerLoader(FunctionTemplate.class, FunctionTemplate::new) .registerLoader(ImageSampler.Channel.class, (t, object, cf) -> ImageSampler.Channel.valueOf((String) object)) .registerLoader(ExpanderStage.Type.class, (t, object, cf) -> ExpanderStage.Type.valueOf((String) object)) .registerLoader(MutatorStage.Type.class, (t, object, cf) -> MutatorStage.Type.valueOf((String) object)) diff --git a/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java b/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java index 2cd6f344b..8ec4a5a5b 100644 --- a/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java +++ b/common/src/main/java/com/dfsek/terra/config/builder/GeneratorBuilder.java @@ -5,6 +5,7 @@ import com.dfsek.terra.api.math.noise.samplers.ConstantSampler; import com.dfsek.terra.api.math.noise.samplers.ExpressionSampler; import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.api.world.palette.holder.PaletteHolder; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import com.dfsek.terra.world.generation.WorldGenerator; import parsii.eval.Scope; import parsii.tokenizer.ParseException; @@ -26,6 +27,8 @@ public class GeneratorBuilder { private Map noiseBuilderMap; + private Map functionTemplateMap; + private PaletteHolder palettes; private PaletteHolder slantPalettes; @@ -51,9 +54,9 @@ public class GeneratorBuilder { NoiseSampler elevation; NoiseSampler carving; try { - noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap); - elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap); - carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap); + noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); + elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); + carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap, functionTemplateMap); } catch(ParseException e) { throw new RuntimeException(e); } @@ -66,6 +69,10 @@ public class GeneratorBuilder { this.blendWeight = blendWeight; } + public void setFunctionTemplateMap(Map functionTemplateMap) { + this.functionTemplateMap = functionTemplateMap; + } + public void setBlendStep(int blendStep) { this.blendStep = blendStep; } diff --git a/common/src/main/java/com/dfsek/terra/config/factories/BiomeFactory.java b/common/src/main/java/com/dfsek/terra/config/factories/BiomeFactory.java index 29daeccff..61460c2f4 100644 --- a/common/src/main/java/com/dfsek/terra/config/factories/BiomeFactory.java +++ b/common/src/main/java/com/dfsek/terra/config/factories/BiomeFactory.java @@ -4,10 +4,14 @@ import com.dfsek.terra.api.core.TerraPlugin; import com.dfsek.terra.biome.TerraBiome; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.builder.GeneratorBuilder; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.templates.BiomeTemplate; import parsii.eval.Scope; +import java.util.HashMap; +import java.util.Map; + public class BiomeFactory implements TerraFactory { private final ConfigPack pack; @@ -21,7 +25,12 @@ public class BiomeFactory implements TerraFactory { generatorBuilder.setElevationEquation(template.getElevationEquation()); generatorBuilder.setNoiseEquation(template.getNoiseEquation()); generatorBuilder.setCarvingEquation(template.getCarvingEquation()); - generatorBuilder.setNoiseBuilderMap(template.getPack().getTemplate().getNoiseBuilderMap()); + generatorBuilder.setNoiseBuilderMap(pack.getTemplate().getNoiseBuilderMap()); + + Map functions = new HashMap<>(pack.getTemplate().getFunctions()); + functions.putAll(template.getFunctions()); + generatorBuilder.setFunctionTemplateMap(functions); + generatorBuilder.setPalettes(template.getPalette()); generatorBuilder.setSlantPalettes(template.getSlantPalette()); diff --git a/common/src/main/java/com/dfsek/terra/config/loaders/config/function/FunctionTemplate.java b/common/src/main/java/com/dfsek/terra/config/loaders/config/function/FunctionTemplate.java new file mode 100644 index 000000000..640dd1999 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/config/loaders/config/function/FunctionTemplate.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.config.loaders.config.function; + +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.loading.object.ObjectTemplate; + +import java.util.List; + +@SuppressWarnings("unused") +public class FunctionTemplate implements ObjectTemplate { + @Value("arguments") + private List args; + + @Value("function") + private String function; + + public List getArgs() { + return args; + } + + public String getFunction() { + return function; + } + + @Override + public FunctionTemplate get() { + return this; + } +} diff --git a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java index 6f2bf3a16..1ca7ae5e8 100644 --- a/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/pack/ConfigPackTemplate.java @@ -5,6 +5,7 @@ import com.dfsek.tectonic.annotations.Value; import com.dfsek.tectonic.config.ConfigTemplate; import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.biome.provider.BiomeProvider; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import java.util.HashMap; import java.util.Map; @@ -21,6 +22,10 @@ public class ConfigPackTemplate implements ConfigTemplate { @Default private Map variables = new HashMap<>(); + @Value("functions") + @Default + private Map functions = new HashMap<>(); + @Value("structures.locatable") @Default private Map locatable = new HashMap<>(); @@ -80,6 +85,10 @@ public class ConfigPackTemplate implements ConfigTemplate { return providerBuilder; } + public Map getFunctions() { + return functions; + } + public String getVersion() { return version; } diff --git a/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java b/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java index dd9244657..29cbfb193 100644 --- a/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java +++ b/common/src/main/java/com/dfsek/terra/config/templates/BiomeTemplate.java @@ -10,6 +10,7 @@ import com.dfsek.terra.api.math.ProbabilityCollection; import com.dfsek.terra.api.math.noise.NoiseSampler; import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite; import com.dfsek.terra.api.math.parsii.BlankFunction; +import com.dfsek.terra.api.math.parsii.defined.UserDefinedFunction; import com.dfsek.terra.api.platform.block.BlockData; import com.dfsek.terra.api.platform.block.MaterialData; import com.dfsek.terra.api.platform.world.Biome; @@ -18,6 +19,7 @@ import com.dfsek.terra.api.util.seeded.NoiseSeeded; import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.SinglePalette; import com.dfsek.terra.api.world.palette.holder.PaletteHolder; +import com.dfsek.terra.config.loaders.config.function.FunctionTemplate; import com.dfsek.terra.config.loaders.config.sampler.templates.FastNoiseTemplate; import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.world.population.items.TerraStructure; @@ -26,12 +28,14 @@ import com.dfsek.terra.world.population.items.ores.OreHolder; import com.dfsek.terra.world.population.items.tree.TreeLayer; import parsii.eval.Parser; import parsii.eval.Scope; +import parsii.eval.Variable; import parsii.tokenizer.ParseException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @SuppressWarnings({"FieldMayBeFinal", "unused"}) public class BiomeTemplate extends AbstractableTemplate implements ValidatedConfigTemplate { @@ -50,6 +54,11 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf @Default private Map variables = new HashMap<>(); + @Value("functions") + @Default + @Abstractable + private Map functions = new HashMap<>(); + @Value("carving.equation") @Abstractable @Default @@ -180,6 +189,10 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf return tags; } + public Map getFunctions() { + return functions; + } + public double getBlendWeight() { return blendWeight; } @@ -316,7 +329,7 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf @Override public boolean validate() throws ValidationException { - color |= 0x1fe00000; // Alpha adjustment + color |= 0xff000000; // Alpha adjustment Parser tester = new Parser(); Scope testScope = new Scope().withParent(pack.getVarScope()); @@ -329,6 +342,22 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf pack.getTemplate().getNoiseBuilderMap().forEach((id, builder) -> tester.registerFunction(id, new BlankFunction(builder.getDimensions()))); // Register dummy functions + Map testFunctions = new HashMap<>(pack.getTemplate().getFunctions()); + testFunctions.putAll(functions); + for(Map.Entry entry : testFunctions.entrySet()) { + String id = entry.getKey(); + FunctionTemplate fun = entry.getValue(); + + Scope functionScope = new Scope().withParent(testScope); + List variables = fun.getArgs().stream().map(functionScope::create).collect(Collectors.toList()); + + try { + tester.registerFunction(id, new UserDefinedFunction(tester.parse(fun.getFunction(), functionScope), variables)); + } catch(ParseException e) { + throw new ValidationException("Invalid function: ", e); + } + } + try { tester.parse(noiseEquation, testScope); } catch(ParseException e) {