diff --git a/common/addons/biome-provider-pipeline/LICENSE b/common/addons/biome-provider-pipeline/LICENSE new file mode 100644 index 000000000..64c1cd516 --- /dev/null +++ b/common/addons/biome-provider-pipeline/LICENSE @@ -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. \ No newline at end of file diff --git a/common/addons/biome-provider-pipeline/README.md b/common/addons/biome-provider-pipeline/README.md new file mode 100644 index 000000000..9e55109a7 --- /dev/null +++ b/common/addons/biome-provider-pipeline/README.md @@ -0,0 +1,7 @@ +# biome-provider-pipeline + +Implements the Biome Pipeline, a procedural biome provider that uses a series +of "stages" to apply "mutations" to a 2D grid of biomes. + +This addon registers the `PIPELINE` biome provider type, and all associated +configurations. \ No newline at end of file diff --git a/common/addons/biome-provider-pipeline/build.gradle.kts b/common/addons/biome-provider-pipeline/build.gradle.kts new file mode 100644 index 000000000..2629ac476 --- /dev/null +++ b/common/addons/biome-provider-pipeline/build.gradle.kts @@ -0,0 +1,12 @@ +version = version("1.0.1") + +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("shadowJar") { + relocate("net.jafama", "com.dfsek.terra.addons.biome.pipeline.lib.jafama") +} \ No newline at end of file diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomeHolderImpl.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomeHolderImpl.java new file mode 100644 index 000000000..57304011b --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomeHolderImpl.java @@ -0,0 +1,94 @@ +/* + * 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.biome.pipeline; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; +import com.dfsek.terra.api.util.vector.Vector2; + + +public class BiomeHolderImpl implements BiomeHolder { + private final Vector2.Mutable origin; + private final int width; + private final int offset; + private BiomeDelegate[][] biomes; + + public BiomeHolderImpl(int width, Vector2.Mutable origin) { + width += 4; + this.width = width; + biomes = new BiomeDelegate[width][width]; + this.origin = origin; + this.offset = 2; + } + + private BiomeHolderImpl(BiomeDelegate[][] biomes, Vector2.Mutable origin, int width, int offset) { + this.biomes = biomes; + this.origin = origin; + this.width = width; + this.offset = 2 * offset; + } + + @Override + public BiomeHolder expand(BiomeExpander expander, long seed) { + BiomeDelegate[][] old = biomes; + int newWidth = width * 2 - 1; + + biomes = new BiomeDelegate[newWidth][newWidth]; + + for(int x = 0; x < width; x++) { + for(int z = 0; z < width; z++) { + biomes[x * 2][z * 2] = old[x][z]; + if(z != width - 1) + biomes[x * 2][z * 2 + 1] = expander.getBetween(x + origin.getX(), z + 1 + origin.getZ(), seed, old[x][z], + old[x][z + 1]); + if(x != width - 1) + biomes[x * 2 + 1][z * 2] = expander.getBetween(x + 1 + origin.getX(), z + origin.getZ(), seed, old[x][z], + old[x + 1][z]); + if(x != width - 1 && z != width - 1) + biomes[x * 2 + 1][z * 2 + 1] = expander.getBetween(x + 1 + origin.getX(), z + 1 + origin.getZ(), seed, old[x][z], + old[x + 1][z + 1], old[x][z + 1], old[x + 1][z]); + } + } + return new BiomeHolderImpl(biomes, origin.setX(origin.getX() * 2 - 1).setZ(origin.getZ() * 2 - 1), newWidth, offset); + } + + @Override + public void mutate(BiomeMutator mutator, long seed) { + for(int x = 0; x < width; x++) { + for(int z = 0; z < width; z++) { + BiomeMutator.ViewPoint viewPoint = new BiomeMutator.ViewPoint(this, x, z); + biomes[x][z] = mutator.mutate(viewPoint, x + origin.getX(), z + origin.getZ(), seed); + } + } + } + + @Override + public void fill(BiomeSource source, long seed) { + for(int x = 0; x < width; x++) { + for(int z = 0; z < width; z++) { + biomes[x][z] = source.getBiome(origin.getX() + x, origin.getZ() + z, seed); + } + } + } + + @Override + public BiomeDelegate getBiome(int x, int z) { + x += offset; + z += offset; + return getBiomeRaw(x, z); + } + + @Override + public BiomeDelegate getBiomeRaw(int x, int z) { + if(x >= width || z >= width || x < 0 || z < 0) return null; + return biomes[x][z]; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipeline.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipeline.java new file mode 100644 index 000000000..8a4be5d7d --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipeline.java @@ -0,0 +1,83 @@ +/* + * 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.biome.pipeline; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; +import com.dfsek.terra.api.util.vector.Vector2; + + +public class BiomePipeline { + private final BiomeSource source; + private final List stages; + private final int size; + private final int init; + + private BiomePipeline(BiomeSource source, List stages, int size, int init) { + this.source = source; + this.stages = stages; + this.size = size; + this.init = init; + } + + /** + * Get biomes in a chunk + * + * @param x Chunk X coord + * @param z Chunk Z coord + * + * @return BiomeHolder containing biomes. + */ + public BiomeHolder getBiomes(int x, int z, long seed) { + BiomeHolder holder = new BiomeHolderImpl(init, Vector2.of(x * (init - 1), z * (init - 1)).mutable()); + holder.fill(source, seed); + for(Stage stage : stages) holder = stage.apply(holder, seed); + return holder; + } + + public BiomeSource getSource() { + return source; + } + + public List getStages() { + return Collections.unmodifiableList(stages); + } + + public int getSize() { + return size; + } + + public static final class BiomePipelineBuilder { + private final int init; + private final List stages = new ArrayList<>(); + private int expand; + + public BiomePipelineBuilder(int init) { + this.init = init; + expand = init; + } + + public BiomePipeline build(BiomeSource source) { + for(Stage stage : stages) { + if(stage.isExpansion()) expand = expand * 2 - 1; + } + + return new BiomePipeline(source, stages, expand, init); + } + + public BiomePipelineBuilder addStage(Stage stage) { + stages.add(stage); + return this; + } + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineAddon.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineAddon.java new file mode 100644 index 000000000..42aa95b0e --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineAddon.java @@ -0,0 +1,89 @@ +/* + * 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.biome.pipeline; + +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; + +import java.util.function.Supplier; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.BiomeDelegateLoader; +import com.dfsek.terra.addons.biome.pipeline.config.BiomePipelineTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.SamplerSourceTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.expander.ExpanderStageTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderListMutatorTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderMutatorTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceListMutatorTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceMutatorTemplate; +import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.SmoothMutatorTemplate; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; +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.ConfigPackPostLoadEvent; +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 com.dfsek.terra.api.registry.CheckedRegistry; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.util.reflection.TypeKey; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public class BiomePipelineAddon implements AddonInitializer { + + public static final TypeKey>> SOURCE_REGISTRY_KEY = new TypeKey<>() { + }; + + public static final TypeKey>> STAGE_REGISTRY_KEY = new TypeKey<>() { + }; + public static final TypeKey>> PROVIDER_REGISTRY_KEY = new TypeKey<>() { + }; + @Inject + private Platform platform; + + @Inject + private BaseAddon addon; + + @Override + public void initialize() { + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(addon, ConfigPackPreLoadEvent.class) + .then(event -> { + CheckedRegistry>> providerRegistry = event.getPack().getOrCreateRegistry( + PROVIDER_REGISTRY_KEY); + providerRegistry.register(addon.key("PIPELINE"), BiomePipelineTemplate::new); + }) + .then(event -> { + CheckedRegistry>> sourceRegistry = event.getPack().getOrCreateRegistry( + SOURCE_REGISTRY_KEY); + sourceRegistry.register(addon.key("SAMPLER"), SamplerSourceTemplate::new); + }) + .then(event -> { + CheckedRegistry>> stageRegistry = event.getPack().getOrCreateRegistry( + STAGE_REGISTRY_KEY); + stageRegistry.register(addon.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new); + stageRegistry.register(addon.key("SMOOTH"), SmoothMutatorTemplate::new); + stageRegistry.register(addon.key("REPLACE"), ReplaceMutatorTemplate::new); + stageRegistry.register(addon.key("REPLACE_LIST"), ReplaceListMutatorTemplate::new); + stageRegistry.register(addon.key("BORDER"), BorderMutatorTemplate::new); + stageRegistry.register(addon.key("BORDER_LIST"), BorderListMutatorTemplate::new); + }) + .failThrough(); + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(addon, ConfigPackPostLoadEvent.class) + .then(event -> { + Registry biomeRegistry = event.getPack().getRegistry(Biome.class); + event.getPack().applyLoader(BiomeDelegate.class, new BiomeDelegateLoader(biomeRegistry)); + }); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineColumn.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineColumn.java new file mode 100644 index 000000000..609d22ee1 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineColumn.java @@ -0,0 +1,71 @@ +package com.dfsek.terra.addons.biome.pipeline; + +import java.util.function.Consumer; + +import com.dfsek.terra.api.util.Column; +import com.dfsek.terra.api.util.function.IntIntObjConsumer; +import com.dfsek.terra.api.util.function.IntObjConsumer; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +class BiomePipelineColumn implements Column { + private final int min; + private final int max; + + private final int x; + private final int z; + private final Biome biome; + + protected BiomePipelineColumn(BiomeProvider biomeProvider, int min, int max, int x, int z, long seed) { + this.min = min; + this.max = max; + this.x = x; + this.z = z; + this.biome = biomeProvider.getBiome(x, 0, z, seed); + } + + @Override + public int getMinY() { + return min; + } + + @Override + public int getMaxY() { + return max; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getZ() { + return z; + } + + @Override + public Biome get(int y) { + return biome; + } + + @Override + public void forRanges(int resolution, IntIntObjConsumer consumer) { + consumer.accept(min, max, biome); + } + + @Override + public void forEach(Consumer consumer) { + for(int y = min; y < max; y++) { + consumer.accept(biome); + } + } + + @Override + public void forEach(IntObjConsumer consumer) { + for(int y = min; y < max; y++) { + consumer.accept(y, biome); + } + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineProvider.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineProvider.java new file mode 100644 index 000000000..f3f4c3aa9 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineProvider.java @@ -0,0 +1,131 @@ +/* + * 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.biome.pipeline; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import net.jafama.FastMath; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.stream.StreamSupport; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.registry.key.StringIdentifiable; +import com.dfsek.terra.api.util.Column; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public class BiomePipelineProvider implements BiomeProvider { + private final LoadingCache holderCache; + private final BiomePipeline pipeline; + private final int resolution; + private final NoiseSampler mutator; + private final double noiseAmp; + + private final Set biomes; + + public BiomePipelineProvider(BiomePipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) { + this.resolution = resolution; + this.mutator = mutator; + this.noiseAmp = noiseAmp; + holderCache = Caffeine.newBuilder() + .maximumSize(1024) + .build(key -> pipeline.getBiomes(key.x, key.z, key.seed)); + this.pipeline = pipeline; + + Set biomeSet = new HashSet<>(); + pipeline.getSource().getBiomes().forEach(biomeSet::add); + Iterable result = biomeSet; + for(Stage stage : pipeline.getStages()) { + result = stage.getBiomes(result); // pass through all stages + } + this.biomes = new HashSet<>(); + Iterable finalResult = result; + result.forEach(biomeDelegate -> { + if(biomeDelegate.isEphemeral()) { + + StringBuilder biomeList = new StringBuilder("\n"); + StreamSupport.stream(finalResult.spliterator(), false) + .sorted(Comparator.comparing(StringIdentifiable::getID)) + .forEach(delegate -> biomeList + .append(" - ") + .append(delegate.getID()) + .append(':') + .append(delegate.getClass().getCanonicalName()) + .append('\n')); + throw new IllegalArgumentException("Biome Pipeline leaks ephemeral biome \"" + biomeDelegate.getID() + + "\". Ensure there is a stage to guarantee replacement of the ephemeral biome. Biomes: " + + biomeList); + } + this.biomes.add(biomeDelegate.getBiome()); + }); + } + + @Override + public Biome getBiome(int x, int y, int z, long seed) { + return getBiome(x, z, seed); + } + + public Biome getBiome(int x, int z, long seed) { + x += mutator.noise(seed + 1, x, z) * noiseAmp; + z += mutator.noise(seed + 2, x, z) * noiseAmp; + + + x /= resolution; + z /= resolution; + + int fdX = FastMath.floorDiv(x, pipeline.getSize()); + int fdZ = FastMath.floorDiv(z, pipeline.getSize()); + return holderCache.get(new SeededVector(fdX, fdZ, seed)).getBiome(x - fdX * pipeline.getSize(), + z - fdZ * pipeline.getSize()).getBiome(); + } + + @Override + public Optional getBaseBiome(int x, int z, long seed) { + return Optional.of(getBiome(x, z, seed)); + } + + @Override + public Iterable getBiomes() { + return biomes; + } + + @Override + public Column getColumn(int x, int z, long seed, int min, int max) { + return new BiomePipelineColumn(this, min, max, x, z, seed); + } + + @Override + public int resolution() { + return resolution; + } + + private record SeededVector(int x, int z, long seed) { + @Override + public boolean equals(Object obj) { + if(obj instanceof SeededVector that) { + return this.z == that.z && this.x == that.x && this.seed == that.seed; + } + return false; + } + + @Override + public int hashCode() { + int code = x; + code = 31 * code + z; + return 31 * code + ((int) (seed ^ (seed >>> 32))); + } + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/BiomeHolder.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/BiomeHolder.java new file mode 100644 index 000000000..26bb63315 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/BiomeHolder.java @@ -0,0 +1,26 @@ +/* + * 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.biome.pipeline.api; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; + + +public interface BiomeHolder { + BiomeHolder expand(BiomeExpander expander, long seed); + + void mutate(BiomeMutator mutator, long seed); + + void fill(BiomeSource source, long seed); + + BiomeDelegate getBiome(int x, int z); + + BiomeDelegate getBiomeRaw(int x, int z); +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/BiomeDelegate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/BiomeDelegate.java new file mode 100644 index 000000000..7e339285a --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/BiomeDelegate.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.biome.pipeline.api.delegate; + +import java.util.Set; + +import com.dfsek.terra.api.registry.key.StringIdentifiable; +import com.dfsek.terra.api.world.biome.Biome; + + +public interface BiomeDelegate extends StringIdentifiable { + static BiomeDelegate ephemeral(String id) { + return new EphemeralBiomeDelegate(id); + } + + static BiomeDelegate from(Biome biome) { + return new DelegatedBiome(biome); + } + + static BiomeDelegate self() { + return SelfDelegate.INSTANCE; + } + + Biome getBiome(); + + Set getTags(); + + default boolean isEphemeral() { + return false; + } + + default boolean isSelf() { + return false; + } + + +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/DelegatedBiome.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/DelegatedBiome.java new file mode 100644 index 000000000..4085f6a43 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/DelegatedBiome.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.biome.pipeline.api.delegate; + +import java.util.Set; + +import com.dfsek.terra.api.world.biome.Biome; + + +final class DelegatedBiome implements BiomeDelegate { + private final Biome biome; + + public DelegatedBiome(Biome biome) { + this.biome = biome; + } + + @Override + public Biome getBiome() { + return biome; + } + + @Override + public int hashCode() { + return biome.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof DelegatedBiome that)) return false; + return that.biome.equals(this.biome); + } + + @Override + public Set getTags() { + return biome.getTags(); + } + + @Override + public String getID() { + return biome.getID(); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/EphemeralBiomeDelegate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/EphemeralBiomeDelegate.java new file mode 100644 index 000000000..d02221f1b --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/EphemeralBiomeDelegate.java @@ -0,0 +1,51 @@ +package com.dfsek.terra.addons.biome.pipeline.api.delegate; + +import java.util.HashSet; +import java.util.Set; + +import com.dfsek.terra.api.world.biome.Biome; + + +final class EphemeralBiomeDelegate implements BiomeDelegate { + private final Set tags; + private final String id; + + public EphemeralBiomeDelegate(String id) { + this.id = id; + tags = new HashSet<>(); + tags.add(id); + tags.add("ALL"); + } + + @Override + public Biome getBiome() { + throw new UnsupportedOperationException("Cannot get biome from ephemeral delegate"); + } + + @Override + public Set getTags() { + return tags; + } + + @Override + public String getID() { + return id; + } + + @Override + public boolean isEphemeral() { + return true; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof EphemeralBiomeDelegate that)) return false; + + return this.id.equals(that.id); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/SelfDelegate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/SelfDelegate.java new file mode 100644 index 000000000..60b744059 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/delegate/SelfDelegate.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.biome.pipeline.api.delegate; + +import java.util.Collections; +import java.util.Set; + +import com.dfsek.terra.api.world.biome.Biome; + + +final class SelfDelegate implements BiomeDelegate { + public static final SelfDelegate INSTANCE = new SelfDelegate(); + + private SelfDelegate() { + + } + + @Override + public Biome getBiome() { + throw new UnsupportedOperationException("Cannot get biome from self delegate"); + } + + @Override + public boolean isSelf() { + return true; + } + + @Override + public boolean isEphemeral() { + return true; + } + + @Override + public Set getTags() { + return Collections.emptySet(); + } + + @Override + public String getID() { + return "SELF"; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/Stage.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/Stage.java new file mode 100644 index 000000000..fb4b1007b --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/Stage.java @@ -0,0 +1,20 @@ +/* + * 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.biome.pipeline.api.stage; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; + + +public interface Stage { + BiomeHolder apply(BiomeHolder in, long seed); + + boolean isExpansion(); + + Iterable getBiomes(Iterable biomes); +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeExpander.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeExpander.java new file mode 100644 index 000000000..c3e721c08 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeExpander.java @@ -0,0 +1,15 @@ +/* + * 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.biome.pipeline.api.stage.type; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; + + +public interface BiomeExpander { + BiomeDelegate getBetween(double x, double z, long seed, BiomeDelegate... others); +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeMutator.java new file mode 100644 index 000000000..f7cc3b7bd --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/api/stage/type/BiomeMutator.java @@ -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.biome.pipeline.api.stage.type; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; + + +public interface BiomeMutator { + BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed); + + default Iterable getBiomes(Iterable biomes) { + return biomes; + } + + class ViewPoint { + private final BiomeHolder biomes; + private final int offX; + private final int offZ; + + public ViewPoint(BiomeHolder biomes, int offX, int offZ) { + this.biomes = biomes; + this.offX = offX; + this.offZ = offZ; + } + + + public BiomeDelegate getBiome(int x, int z) { + return biomes.getBiomeRaw(x + offX, z + offZ); + } + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeDelegateLoader.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeDelegateLoader.java new file mode 100644 index 000000000..c416e4804 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeDelegateLoader.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.addons.biome.pipeline.config; + +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 org.jetbrains.annotations.NotNull; + +import java.lang.reflect.AnnotatedType; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.world.biome.Biome; + + +public class BiomeDelegateLoader implements TypeLoader { + private final Registry biomeRegistry; + + public BiomeDelegateLoader(Registry biomeRegistry) { + this.biomeRegistry = biomeRegistry; + } + + @Override + public BiomeDelegate load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker) + throws LoadException { + if(c.equals("SELF")) return BiomeDelegate.self(); + return biomeRegistry + .getByID((String) c) + .map(BiomeDelegate::from) + .orElseGet(() -> BiomeDelegate.ephemeral((String) c)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomePipelineTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomePipelineTemplate.java new file mode 100644 index 000000000..e35d621e2 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomePipelineTemplate.java @@ -0,0 +1,54 @@ +/* + * 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.biome.pipeline.config; + +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.Value; + +import java.util.List; + +import com.dfsek.terra.addons.biome.pipeline.BiomePipeline; +import com.dfsek.terra.addons.biome.pipeline.BiomePipelineProvider; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +@SuppressWarnings({ "FieldMayBeFinal", "unused" }) +public class BiomePipelineTemplate extends BiomeProviderTemplate { + @Value("pipeline.initial-size") + @Default + @Description(""" + The initial size of biome chunks. This value must be at least 2. + This is not the final size of biome chunks. Final chunks will be much larger. + + It is recommended to keep biome chunks' final size in the range of [50, 300] + to prevent performance issues. To calculate the size of biome chunks, simply + take initial-size and for each expand stage, multiply the running value by 2 + and subtract 1. (The size is also printed to the server console if you + have debug mode enabled)""") + private @Meta int initialSize = 2; + + @Value("pipeline.source") + @Description("The Biome Source to use for initial population of biomes.") + private @Meta BiomeSource source; + + @Value("pipeline.stages") + @Description("A list of pipeline stages to apply to the result of #source") + private @Meta List<@Meta Stage> stages; + + @Override + public BiomeProvider get() { + BiomePipeline.BiomePipelineBuilder biomePipelineBuilder = new BiomePipeline.BiomePipelineBuilder(initialSize); + stages.forEach(biomePipelineBuilder::addStage); + BiomePipeline pipeline = biomePipelineBuilder.build(source); + return new BiomePipelineProvider(pipeline, resolution, blend, blendAmp); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeProviderTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeProviderTemplate.java new file mode 100644 index 000000000..1412485fe --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/BiomeProviderTemplate.java @@ -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.biome.pipeline.config; + +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.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; + +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public abstract class BiomeProviderTemplate implements ObjectTemplate { + @Value("resolution") + @Default + @Description(""" + The resolution at which to sample biomes. + + Larger values are quadratically faster, but produce lower quality results. + For example, a value of 3 would sample every 3 blocks.""") + protected @Meta int resolution = 1; + @Value("blend.sampler") + @Default + @Description("A sampler to use for blending the edges of biomes via domain warping.") + protected @Meta NoiseSampler blend = NoiseSampler.zero(); + @Value("blend.amplitude") + @Default + @Description("The amplitude at which to perform blending.") + protected @Meta double blendAmp = 0d; +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SamplerSourceTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SamplerSourceTemplate.java new file mode 100644 index 000000000..2ba70b608 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SamplerSourceTemplate.java @@ -0,0 +1,34 @@ +/* + * 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.biome.pipeline.config; + +import com.dfsek.tectonic.api.config.template.annotations.Description; +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; +import com.dfsek.terra.addons.biome.pipeline.source.SamplerSource; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class SamplerSourceTemplate extends SourceTemplate { + @Value("sampler") + @Description("The sampler used to distribute biomes.") + private @Meta NoiseSampler noise; + + @Value("biomes") + @Description("The biomes to be distributed.") + private @Meta ProbabilityCollection<@Meta BiomeDelegate> biomes; + + @Override + public BiomeSource get() { + return new SamplerSource(biomes, noise); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SourceTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SourceTemplate.java new file mode 100644 index 000000000..c80ec0cee --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/SourceTemplate.java @@ -0,0 +1,17 @@ +/* + * 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.biome.pipeline.config; + +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; + +import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; + + +public abstract class SourceTemplate implements ObjectTemplate { + +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/StageTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/StageTemplate.java new file mode 100644 index 000000000..ba4bee85d --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/StageTemplate.java @@ -0,0 +1,23 @@ +/* + * 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.biome.pipeline.config.stage; + +import com.dfsek.tectonic.api.config.template.annotations.Description; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; + +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.noise.NoiseSampler; + + +public abstract class StageTemplate implements ObjectTemplate { + @Value("sampler") + @Description("Sampler to use for stage distribution.") + protected @Meta NoiseSampler noise; +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/expander/ExpanderStageTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/expander/ExpanderStageTemplate.java new file mode 100644 index 000000000..8f66315ef --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/expander/ExpanderStageTemplate.java @@ -0,0 +1,21 @@ +/* + * 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.biome.pipeline.config.stage.expander; + +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.expand.FractalExpander; +import com.dfsek.terra.addons.biome.pipeline.stages.ExpanderStage; + + +public class ExpanderStageTemplate extends StageTemplate { + @Override + public Stage get() { + return new ExpanderStage(new FractalExpander(noise)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderListMutatorTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderListMutatorTemplate.java new file mode 100644 index 000000000..fcf523e44 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderListMutatorTemplate.java @@ -0,0 +1,42 @@ +/* + * 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.biome.pipeline.config.stage.mutator; + +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import java.util.Map; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.mutator.BorderListMutator; +import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +@SuppressWarnings("unused") +public class BorderListMutatorTemplate extends StageTemplate { + @Value("from") + private @Meta String from; + + @Value("default-replace") + private @Meta String defaultReplace; + + @Value("default-to") + private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo; + + @Value("replace") + private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace; + + + @Override + public Stage get() { + return new MutatorStage(new BorderListMutator(replace, from, defaultReplace, noise, defaultTo)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderMutatorTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderMutatorTemplate.java new file mode 100644 index 000000000..4db9e4e99 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/BorderMutatorTemplate.java @@ -0,0 +1,36 @@ +/* + * 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.biome.pipeline.config.stage.mutator; + +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.mutator.BorderMutator; +import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +@SuppressWarnings("unused") +public class BorderMutatorTemplate extends StageTemplate { + @Value("from") + private @Meta String from; + + @Value("replace") + private @Meta String replace; + + @Value("to") + private @Meta ProbabilityCollection<@Meta BiomeDelegate> to; + + @Override + public Stage get() { + return new MutatorStage(new BorderMutator(from, replace, noise, to)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceListMutatorTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceListMutatorTemplate.java new file mode 100644 index 000000000..5713b3ff0 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceListMutatorTemplate.java @@ -0,0 +1,38 @@ +/* + * 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.biome.pipeline.config.stage.mutator; + +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import java.util.Map; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceListMutator; +import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +@SuppressWarnings("unused") +public class ReplaceListMutatorTemplate extends StageTemplate { + @Value("default-from") + private @Meta String defaultFrom; + + @Value("default-to") + private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo; + + @Value("to") + private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace; + + @Override + public Stage get() { + return new MutatorStage(new ReplaceListMutator(replace, defaultFrom, defaultTo, noise)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceMutatorTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceMutatorTemplate.java new file mode 100644 index 000000000..dae592e2b --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/ReplaceMutatorTemplate.java @@ -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.biome.pipeline.config.stage.mutator; + +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceMutator; +import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +@SuppressWarnings("unused") +public class ReplaceMutatorTemplate extends StageTemplate { + @Value("from") + private @Meta String from; + + @Value("to") + private @Meta ProbabilityCollection<@Meta BiomeDelegate> to; + + @Override + public Stage get() { + return new MutatorStage(new ReplaceMutator(from, to, noise)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/SmoothMutatorTemplate.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/SmoothMutatorTemplate.java new file mode 100644 index 000000000..b26dce902 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/config/stage/mutator/SmoothMutatorTemplate.java @@ -0,0 +1,21 @@ +/* + * 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.biome.pipeline.config.stage.mutator; + +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate; +import com.dfsek.terra.addons.biome.pipeline.mutator.SmoothMutator; +import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; + + +public class SmoothMutatorTemplate extends StageTemplate { + @Override + public Stage get() { + return new MutatorStage(new SmoothMutator(noise)); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/expand/FractalExpander.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/expand/FractalExpander.java new file mode 100644 index 000000000..50d36a5c4 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/expand/FractalExpander.java @@ -0,0 +1,27 @@ +/* + * 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.biome.pipeline.expand; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.MathUtil; + + +public class FractalExpander implements BiomeExpander { + private final NoiseSampler sampler; + + public FractalExpander(NoiseSampler sampler) { + this.sampler = sampler; + } + + @Override + public BiomeDelegate getBetween(double x, double z, long seed, BiomeDelegate... others) { + return others[MathUtil.normalizeIndex(sampler.noise(seed, x, z), others.length)]; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderListMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderListMutator.java new file mode 100644 index 000000000..59f6dd9b4 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderListMutator.java @@ -0,0 +1,67 @@ +/* + * 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.biome.pipeline.mutator; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class BorderListMutator implements BiomeMutator { + private final String border; + private final NoiseSampler noiseSampler; + private final ProbabilityCollection replaceDefault; + private final String defaultReplace; + private final Map> replace; + + public BorderListMutator(Map> replace, String border, String defaultReplace, + NoiseSampler noiseSampler, ProbabilityCollection replaceDefault) { + this.border = border; + this.noiseSampler = noiseSampler; + this.replaceDefault = replaceDefault; + this.defaultReplace = defaultReplace; + this.replace = replace; + } + + @Override + public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { + BiomeDelegate origin = viewPoint.getBiome(0, 0); + if(origin.getTags().contains(defaultReplace)) { + for(int xi = -1; xi <= 1; xi++) { + for(int zi = -1; zi <= 1; zi++) { + if(xi == 0 && zi == 0) continue; + BiomeDelegate current = viewPoint.getBiome(xi, zi); + if(current != null && current.getTags().contains(border)) { + if(replace.containsKey(origin)) { + BiomeDelegate biome = replace.get(origin).get(noiseSampler, x, z, seed); + return biome.isSelf() ? origin : biome; + } + BiomeDelegate biome = replaceDefault.get(noiseSampler, x, z, seed); + return biome.isSelf() ? origin : biome; + } + } + } + } + return origin; + } + + @Override + public Iterable getBiomes(Iterable biomes) { + Set biomeSet = new HashSet<>(); + biomes.forEach(biomeSet::add); + biomeSet.addAll(replaceDefault.getContents().stream().filter(Predicate.not(BiomeDelegate::isSelf)).toList()); + replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents())); + return biomeSet; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderMutator.java new file mode 100644 index 000000000..5755719f4 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/BorderMutator.java @@ -0,0 +1,66 @@ +/* + * 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.biome.pipeline.mutator; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class BorderMutator implements BiomeMutator { + private final String border; + private final NoiseSampler noiseSampler; + private final ProbabilityCollection replace; + private final String replaceTag; + + public BorderMutator(String border, String replaceTag, NoiseSampler noiseSampler, ProbabilityCollection replace) { + this.border = border; + this.noiseSampler = noiseSampler; + this.replace = replace; + this.replaceTag = replaceTag; + } + + @Override + public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { + BiomeDelegate origin = viewPoint.getBiome(0, 0); + if(origin.getTags().contains(replaceTag)) { + for(int xi = -1; xi <= 1; xi++) { + for(int zi = -1; zi <= 1; zi++) { + if(xi == 0 && zi == 0) continue; + BiomeDelegate current = viewPoint.getBiome(xi, zi); + if(current != null && current.getTags().contains(border)) { + BiomeDelegate biome = replace.get(noiseSampler, x, z, seed); + return biome.isSelf() ? origin : biome; + } + } + } + } + return origin; + } + + @Override + public Iterable getBiomes(Iterable biomes) { + Set biomeSet = new HashSet<>(); + biomes.forEach(biomeSet::add); + biomeSet.addAll( + replace + .getContents() + .stream() + .filter( + Predicate.not(BiomeDelegate::isSelf) + ) + .toList() + ); + return biomeSet; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceListMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceListMutator.java new file mode 100644 index 000000000..51a74c285 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceListMutator.java @@ -0,0 +1,72 @@ +/* + * 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.biome.pipeline.mutator; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class ReplaceListMutator implements BiomeMutator { + private final Map> replace; + private final NoiseSampler sampler; + private final ProbabilityCollection replaceDefault; + private final String defaultTag; + + public ReplaceListMutator(Map> replace, String defaultTag, + ProbabilityCollection replaceDefault, NoiseSampler sampler) { + this.replace = replace; + this.sampler = sampler; + this.defaultTag = defaultTag; + this.replaceDefault = replaceDefault; + } + + @Override + public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { + BiomeDelegate center = viewPoint.getBiome(0, 0); + if(replace.containsKey(center)) { + BiomeDelegate biome = replace.get(center).get(sampler, x, z, seed); + return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; + } + if(viewPoint.getBiome(0, 0).getTags().contains(defaultTag)) { + BiomeDelegate biome = replaceDefault.get(sampler, x, z, seed); + return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; + } + return center; + } + + @Override + public Iterable getBiomes(Iterable biomes) { + Set biomeSet = new HashSet<>(); + + Set reject = new HashSet<>(); + + biomes.forEach(biome -> { + if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) { + biomeSet.add(biome); + } else { + reject.add(biome); + } + }); + biomeSet.addAll(replaceDefault.getContents().stream().flatMap(terraBiome -> { + if(terraBiome.isSelf()) return reject.stream(); + return Stream.of(terraBiome); + }).toList()); + replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents().stream().map(terraBiome -> { + if(terraBiome.isSelf()) return biome; + return terraBiome; + }).toList())); + return biomeSet; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceMutator.java new file mode 100644 index 000000000..cec3e5e9e --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/ReplaceMutator.java @@ -0,0 +1,57 @@ +/* + * 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.biome.pipeline.mutator; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class ReplaceMutator implements BiomeMutator { + private final String replaceableTag; + private final ProbabilityCollection replace; + private final NoiseSampler sampler; + + public ReplaceMutator(String replaceable, ProbabilityCollection replace, NoiseSampler sampler) { + this.replaceableTag = replaceable; + this.replace = replace; + this.sampler = sampler; + } + + @Override + public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { + if(viewPoint.getBiome(0, 0).getTags().contains(replaceableTag)) { + BiomeDelegate biome = replace.get(sampler, x, z, seed); + return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; + } + return viewPoint.getBiome(0, 0); + } + + @Override + public Iterable getBiomes(Iterable biomes) { + Set biomeSet = new HashSet<>(); + Set reject = new HashSet<>(); + biomes.forEach(biome -> { + if(!biome.getTags().contains(replaceableTag)) { + biomeSet.add(biome); + } else { + reject.add(biome); + } + }); + biomeSet.addAll(replace.getContents().stream().flatMap(terraBiome -> { + if(terraBiome.isSelf()) return reject.stream(); + return Stream.of(terraBiome); + }).toList()); + return biomeSet; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/SmoothMutator.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/SmoothMutator.java new file mode 100644 index 000000000..4785216e8 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/mutator/SmoothMutator.java @@ -0,0 +1,46 @@ +/* + * 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.biome.pipeline.mutator; + +import java.util.Objects; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.MathUtil; + + +public class SmoothMutator implements BiomeMutator { + + private final NoiseSampler sampler; + + public SmoothMutator(NoiseSampler sampler) { + this.sampler = sampler; + } + + @Override + public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { + BiomeDelegate top = viewPoint.getBiome(1, 0); + BiomeDelegate bottom = viewPoint.getBiome(-1, 0); + BiomeDelegate left = viewPoint.getBiome(0, 1); + BiomeDelegate right = viewPoint.getBiome(0, -1); + + + boolean vert = Objects.equals(top, bottom) && top != null; + boolean horiz = Objects.equals(left, right) && left != null; + + if(vert && horiz) { + return MathUtil.normalizeIndex(sampler.noise(seed, x, z), 2) == 0 ? left : top; + } + + if(vert) return top; + if(horiz) return left; + + return viewPoint.getBiome(0, 0); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/BiomeSource.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/BiomeSource.java new file mode 100644 index 000000000..d56151f07 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/BiomeSource.java @@ -0,0 +1,17 @@ +/* + * 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.biome.pipeline.source; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; + + +public interface BiomeSource { + BiomeDelegate getBiome(double x, double z, long seed); + + Iterable getBiomes(); +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/SamplerSource.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/SamplerSource.java new file mode 100644 index 000000000..62656ac17 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/source/SamplerSource.java @@ -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.biome.pipeline.source; + +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.api.noise.NoiseSampler; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class SamplerSource implements BiomeSource { + private final ProbabilityCollection biomes; + private final NoiseSampler sampler; + + public SamplerSource(ProbabilityCollection biomes, NoiseSampler sampler) { + this.biomes = biomes; + this.sampler = sampler; + } + + @Override + public BiomeDelegate getBiome(double x, double z, long seed) { + return biomes.get(sampler, x, z, seed); + } + + @Override + public Iterable getBiomes() { + return biomes.getContents(); + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/ExpanderStage.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/ExpanderStage.java new file mode 100644 index 000000000..e99c13e6b --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/ExpanderStage.java @@ -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.biome.pipeline.stages; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander; + + +public class ExpanderStage implements Stage { + private final BiomeExpander expander; + + public ExpanderStage(BiomeExpander expander) { + this.expander = expander; + } + + @Override + public BiomeHolder apply(BiomeHolder in, long seed) { + return in.expand(expander, seed); + } + + @Override + public boolean isExpansion() { + return true; + } + + @Override + public Iterable getBiomes(Iterable biomes) { + return biomes; + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/MutatorStage.java b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/MutatorStage.java new file mode 100644 index 000000000..8a9a06aae --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/stages/MutatorStage.java @@ -0,0 +1,46 @@ +/* + * 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.biome.pipeline.stages; + +import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; +import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; +import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; +import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; + + +public class MutatorStage implements Stage { + private final BiomeMutator mutator; + + public MutatorStage(BiomeMutator mutator) { + this.mutator = mutator; + } + + @Override + public BiomeHolder apply(BiomeHolder in, long seed) { + in.mutate(mutator, seed); + return in; + } + + @Override + public boolean isExpansion() { + return false; + } + + @Override + public Iterable getBiomes(Iterable biomes) { + return mutator.getBiomes(biomes); + } + + public enum Type { + REPLACE, + REPLACE_LIST, + BORDER, + BORDER_LIST, + SMOOTH + } +} diff --git a/common/addons/biome-provider-pipeline/src/main/resources/terra.addon.yml b/common/addons/biome-provider-pipeline/src/main/resources/terra.addon.yml new file mode 100644 index 000000000..abd2555a5 --- /dev/null +++ b/common/addons/biome-provider-pipeline/src/main/resources/terra.addon.yml @@ -0,0 +1,12 @@ +schema-version: 1 +contributors: + - Terra contributors +id: biome-provider-pipeline +version: @VERSION@ +entrypoints: + - "com.dfsek.terra.addons.biome.pipeline.BiomePipelineAddon" +website: + issues: https://github.com/PolyhedralDev/Terra/issues + source: https://github.com/PolyhedralDev/Terra + docs: https://terra.polydev.org +license: MIT License \ No newline at end of file