Merge remote-tracking branch 'origin/ver/7.0.0' into dev/pure

# Conflicts:
#	common/addons/biome-provider-pipeline/src/main/java/com/dfsek/terra/addons/biome/pipeline/BiomePipelineAddon.java
#	common/addons/config-distributors/src/main/java/com/dfsek/terra/addons/feature/distributor/DistributorAddon.java
#	common/addons/config-locators/src/main/java/com/dfsek/terra/addons/feature/locator/LocatorAddon.java
This commit is contained in:
dfsek committed 2022-08-21 13:40:57 -07:00
commit 91766599c6
149 files changed
+2108 -2289

No files matched your search

+6 -18
View File
@@ -3,32 +3,20 @@ plugins {
kotlin("jvm") version embeddedKotlinVersion kotlin("jvm") version embeddedKotlinVersion
} }
buildscript {
configurations.all {
resolutionStrategy {
force("org.ow2.asm:asm:9.3") // TODO: remove when ShadowJar updates ASM version
force("org.ow2.asm:asm-commons:9.3")
}
}
}
repositories { repositories {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven("https://repo.codemc.org/repository/maven-public") { maven("https://repo.codemc.org/repository/maven-public") {
name = "CodeMC" name = "CodeMC"
} }
maven("https://papermc.io/repo/repository/maven-public/") {
name = "PaperMC"
}
} }
dependencies { dependencies {
implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:+") implementation(libs.libraries.internal.shadow)
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.3.5")
implementation("org.ow2.asm:asm:9.3")
implementation("org.ow2.asm:asm-tree:9.3") implementation(libs.libraries.internal.asm)
implementation("com.dfsek.tectonic:common:4.2.0") implementation(libs.libraries.internal.asm.tree)
implementation("org.yaml:snakeyaml:1.27") implementation(libs.libraries.tectonic)
implementation(libs.libraries.snakeyaml)
} }
+7
View File
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
+12 -5
View File
@@ -5,6 +5,8 @@ import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.getting import org.gradle.kotlin.dsl.getting
import org.gradle.kotlin.dsl.maven import org.gradle.kotlin.dsl.maven
import org.gradle.kotlin.dsl.repositories import org.gradle.kotlin.dsl.repositories
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.kotlin.dsl.apply
fun Project.configureDependencies() { fun Project.configureDependencies() {
val testImplementation by configurations.getting val testImplementation by configurations.getting
@@ -15,6 +17,8 @@ fun Project.configureDependencies() {
val shaded by configurations.creating val shaded by configurations.creating
val libs = rootProject.project.versionCatalogs.libs
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
val shadedApi by configurations.creating { val shadedApi by configurations.creating {
shaded.extendsFrom(this) shaded.extendsFrom(this)
@@ -48,14 +52,17 @@ fun Project.configureDependencies() {
maven("https://jitpack.io") { maven("https://jitpack.io") {
name = "JitPack" name = "JitPack"
} }
maven("Modrinth") {
url = uri("https://api.modrinth.com/maven")
}
} }
dependencies { dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0") testImplementation(libs.findLibrary("libraries.internal.junit.jupiter.api").get())
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.0") testImplementation(libs.findLibrary("libraries.internal.junit.jupiter.engine").get())
compileOnly("org.jetbrains:annotations:23.0.0") compileOnly(libs.findLibrary("libraries.internal.jetbrains.annotations").get())
compileOnly("com.google.guava:guava:30.0-jre") compileOnly(libs.findLibrary("libraries.guava").get())
testImplementation("com.google.guava:guava:30.0-jre") testImplementation(libs.findLibrary("libraries.guava").get())
} }
} }
+12
View File
@@ -0,0 +1,12 @@
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
internal
val Project.versionCatalogs: VersionCatalogsExtension
get() = the<VersionCatalogsExtension>()
internal
val VersionCatalogsExtension.libs: VersionCatalog
get() = named("libs")
-72
View File
@@ -1,72 +0,0 @@
object Versions {
object Libraries {
const val tectonic = "4.2.0"
const val paralithic = "0.7.0"
const val strata = "1.1.1"
const val cloud = "1.7.0"
const val slf4j = "1.7.36"
const val log4j_slf4j_impl = "2.14.1"
const val caffeine = "3.1.0"
const val vavr = "0.10.4"
object Internal {
const val apacheText = "1.9"
const val jafama = "2.3.2"
const val apacheIO = "2.6"
const val fastutil = "8.5.6"
}
}
object Fabric {
const val fabricLoader = "0.14.8"
const val fabricAPI = "0.57.0+1.19"
}
object Quilt {
const val quiltLoader = "0.17.0"
const val fabricApi = "2.0.0-beta.4+0.57.0-1.19"
}
object Mod {
const val mixin = "0.11.2+mixin.0.8.5"
const val minecraft = "1.19"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.14.2"
const val architecuryLoom = "0.12.0.290"
const val architecturyPlugin = "3.4-SNAPSHOT"
const val loomQuiltflower = "1.7.1"
const val lazyDfu = "0.1.2"
}
object Forge {
const val forge = "${Mod.minecraft}-41.0.63"
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"
const val reflectionRemapper = "0.1.0-SNAPSHOT"
}
object Sponge {
const val sponge = "9.0.0-SNAPSHOT"
const val mixin = "0.8.2"
const val minecraft = "1.17.1"
}
object CLI {
const val nbt = "6.1"
const val logback = "1.2.9"
const val commonsIO = "2.7"
const val guava = "31.0.1-jre"
}
}
@@ -2,10 +2,4 @@ version = version("1.0.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.biome.image.lib.jafama")
} }
@@ -2,11 +2,4 @@ version = version("1.0.1")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.biome.pipeline.lib.jafama")
} }
@@ -1,94 +0,0 @@
/*
* 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];
}
}
@@ -1,83 +0,0 @@
/*
* 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<Stage> stages;
private final int size;
private final int init;
private BiomePipeline(BiomeSource source, List<Stage> 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<Stage> getStages() {
return Collections.unmodifiableList(stages);
}
public int getSize() {
return size;
}
public static final class BiomePipelineBuilder {
private final int init;
private final List<Stage> 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;
}
}
}
@@ -11,18 +11,19 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier; 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.BiomePipelineTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.SamplerSourceTemplate; import com.dfsek.terra.addons.biome.pipeline.config.PipelineBiomeLoader;
import com.dfsek.terra.addons.biome.pipeline.config.source.SamplerSourceTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.expander.ExpanderStageTemplate; 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.BorderListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderMutatorTemplate; import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceListMutatorTemplate; import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceMutatorTemplate; import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.SmoothMutatorTemplate; import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.SmoothStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.manifest.api.MonadAddonInitializer; import com.dfsek.terra.addons.manifest.api.MonadAddonInitializer;
import com.dfsek.terra.addons.manifest.api.monad.Do; import com.dfsek.terra.addons.manifest.api.monad.Do;
import com.dfsek.terra.addons.manifest.api.monad.Get; import com.dfsek.terra.addons.manifest.api.monad.Get;
@@ -30,6 +31,7 @@ import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent; 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.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.function.monad.Monad; import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.util.generic.Construct; import com.dfsek.terra.api.util.generic.Construct;
@@ -37,12 +39,10 @@ import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import org.jetbrains.annotations.NotNull;
public class BiomePipelineAddon implements MonadAddonInitializer { public class BiomePipelineAddon implements MonadAddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<BiomeSource>>> SOURCE_REGISTRY_KEY = new TypeKey<>() { public static final TypeKey<Supplier<ObjectTemplate<Source>>> SOURCE_REGISTRY_KEY = new TypeKey<>() {
}; };
public static final TypeKey<Supplier<ObjectTemplate<Stage>>> STAGE_REGISTRY_KEY = new TypeKey<>() { public static final TypeKey<Supplier<ObjectTemplate<Stage>>> STAGE_REGISTRY_KEY = new TypeKey<>() {
@@ -51,45 +51,44 @@ public class BiomePipelineAddon implements MonadAddonInitializer {
}; };
@Override @Override
public @NotNull Monad<?, Init<?>> initialize() { public Monad<?, Init<?>> initialize() {
return Do.with( return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)), Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(), Get.addon(),
((functionalEventHandler, base) -> Init.ofPure(Construct.construct(() -> { ((functionalEventHandler, base) -> Init.ofPure(Construct.construct(() -> {
functionalEventHandler.register(base, ConfigPackPreLoadEvent.class) functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry =
event.getPack().createRegistry( event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY); PROVIDER_REGISTRY_KEY);
providerRegistry.register(base.key("PIPELINE"), BiomePipelineTemplate::new); providerRegistry.register(base.key("PIPELINE"), BiomePipelineTemplate::new);
}) })
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<BiomeSource>>> sourceRegistry = CheckedRegistry<Supplier<ObjectTemplate<Source>>> sourceRegistry =
event.getPack().createRegistry( event.getPack().getOrCreateRegistry(
SOURCE_REGISTRY_KEY); SOURCE_REGISTRY_KEY);
sourceRegistry.register(base.key("SAMPLER"), SamplerSourceTemplate::new); sourceRegistry.register(base.key("SAMPLER"), SamplerSourceTemplate::new);
}) })
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<Stage>>> stageRegistry = CheckedRegistry<Supplier<ObjectTemplate<Stage>>> stageRegistry =
event.getPack().createRegistry( event.getPack().getOrCreateRegistry(
STAGE_REGISTRY_KEY); STAGE_REGISTRY_KEY);
stageRegistry.register(base.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new); stageRegistry.register(base.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new);
stageRegistry.register(base.key("SMOOTH"), SmoothMutatorTemplate::new); stageRegistry.register(base.key("SMOOTH"), SmoothStageTemplate::new);
stageRegistry.register(base.key("REPLACE"), ReplaceMutatorTemplate::new); stageRegistry.register(base.key("REPLACE"), ReplaceStageTemplate::new);
stageRegistry.register(base.key("REPLACE_LIST"), ReplaceListMutatorTemplate::new); stageRegistry.register(base.key("REPLACE_LIST"), ReplaceListStageTemplate::new);
stageRegistry.register(base.key("BORDER"), BorderMutatorTemplate::new); stageRegistry.register(base.key("BORDER"), BorderStageTemplate::new);
stageRegistry.register(base.key("BORDER_LIST"), BorderListMutatorTemplate::new); stageRegistry.register(base.key("BORDER_LIST"), BorderListStageTemplate::new);
}) })
.failThrough(); .failThrough();
return functionalEventHandler.register(base, ConfigPackPostLoadEvent.class) return functionalEventHandler.register(base, ConfigPackPostLoadEvent.class)
.then(event -> { .then(event -> {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class); Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(BiomeDelegate.class, event.getPack().applyLoader(PipelineBiome.class,
new BiomeDelegateLoader(biomeRegistry)); new PipelineBiomeLoader(biomeRegistry));
}); });
}))) })))
); );
} }
} }
@@ -9,7 +9,7 @@ import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
class BiomePipelineColumn implements Column<Biome> { public class BiomePipelineColumn implements Column<Biome> {
private final int min; private final int min;
private final int max; private final int max;
@@ -1,10 +1,3 @@
/*
* 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; package com.dfsek.terra.addons.biome.pipeline;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
@@ -13,13 +6,14 @@ import net.jafama.FastMath;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder; import com.dfsek.terra.addons.biome.pipeline.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; import com.dfsek.terra.addons.biome.pipeline.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.registry.key.StringIdentifiable; import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.util.Column; import com.dfsek.terra.api.util.Column;
@@ -27,34 +21,34 @@ import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineProvider implements BiomeProvider { public class PipelineBiomeProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeHolder> holderCache;
private final BiomePipeline pipeline; private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache;
private final int chunkSize;
private final int resolution; private final int resolution;
private final NoiseSampler mutator; private final NoiseSampler mutator;
private final double noiseAmp; private final double noiseAmp;
private final Set<Biome> biomes; private final Set<Biome> biomes;
public BiomePipelineProvider(BiomePipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) { public PipelineBiomeProvider(Pipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) {
this.resolution = resolution; this.resolution = resolution;
this.mutator = mutator; this.mutator = mutator;
this.noiseAmp = noiseAmp; this.noiseAmp = noiseAmp;
holderCache = Caffeine.newBuilder() this.chunkSize = pipeline.getChunkSize();
.maximumSize(1024) this.biomeChunkCache = Caffeine.newBuilder()
.build(key -> pipeline.getBiomes(key.x, key.z, key.seed)); .maximumSize(1024)
this.pipeline = pipeline; .build(pipeline::generateChunk);
Set<BiomeDelegate> biomeSet = new HashSet<>(); Set<PipelineBiome> biomeSet = new HashSet<>();
pipeline.getSource().getBiomes().forEach(biomeSet::add); pipeline.getSource().getBiomes().forEach(biomeSet::add);
Iterable<BiomeDelegate> result = biomeSet; Iterable<PipelineBiome> result = biomeSet;
for(Stage stage : pipeline.getStages()) { for(Stage stage : pipeline.getStages()) {
result = stage.getBiomes(result); // pass through all stages result = stage.getBiomes(result);
} }
this.biomes = new HashSet<>(); this.biomes = new HashSet<>();
Iterable<BiomeDelegate> finalResult = result; Iterable<PipelineBiome> finalResult = result;
result.forEach(biomeDelegate -> { result.forEach(pipelineBiome -> {
if(biomeDelegate.isEphemeral()) { if(pipelineBiome.isPlaceholder()) {
StringBuilder biomeList = new StringBuilder("\n"); StringBuilder biomeList = new StringBuilder("\n");
StreamSupport.stream(finalResult.spliterator(), false) StreamSupport.stream(finalResult.spliterator(), false)
@@ -65,11 +59,11 @@ public class BiomePipelineProvider implements BiomeProvider {
.append(':') .append(':')
.append(delegate.getClass().getCanonicalName()) .append(delegate.getClass().getCanonicalName())
.append('\n')); .append('\n'));
throw new IllegalArgumentException("Biome Pipeline leaks ephemeral biome \"" + biomeDelegate.getID() + throw new IllegalArgumentException("Biome Pipeline leaks placeholder biome \"" + pipelineBiome.getID() +
"\". Ensure there is a stage to guarantee replacement of the ephemeral biome. Biomes: " + "\". Ensure there is a stage to guarantee replacement of the placeholder biome. Biomes: " +
biomeList); biomeList);
} }
this.biomes.add(biomeDelegate.getBiome()); this.biomes.add(pipelineBiome.getBiome());
}); });
} }
@@ -79,22 +73,23 @@ public class BiomePipelineProvider implements BiomeProvider {
} }
public Biome getBiome(int x, int z, long seed) { public Biome getBiome(int x, int z, long seed) {
x += mutator.noise(seed + 1, x, z) * noiseAmp; x += mutator.noise(seed + 1, x, z) * noiseAmp;
z += mutator.noise(seed + 2, x, z) * noiseAmp; z += mutator.noise(seed + 2, x, z) * noiseAmp;
x /= resolution; x /= resolution;
z /= resolution; z /= resolution;
int fdX = FastMath.floorDiv(x, pipeline.getSize()); int chunkX = FastMath.floorDiv(x, chunkSize);
int fdZ = FastMath.floorDiv(z, pipeline.getSize()); int chunkZ = FastMath.floorDiv(z, chunkSize);
return holderCache.get(new SeededVector(fdX, fdZ, seed)).getBiome(x - fdX * pipeline.getSize(),
z - fdZ * pipeline.getSize()).getBiome();
}
@Override int chunkWorldX = chunkX * chunkSize;
public Optional<Biome> getBaseBiome(int x, int z, long seed) { int chunkWorldZ = chunkZ * chunkSize;
return Optional.of(getBiome(x, z, seed));
int xInChunk = x - chunkWorldX;
int zInChunk = z - chunkWorldZ;
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome();
} }
@Override @Override
@@ -111,21 +106,4 @@ public class BiomePipelineProvider implements BiomeProvider {
public int resolution() { public int resolution() {
return 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)));
}
}
} }
@@ -0,0 +1,10 @@
package com.dfsek.terra.addons.biome.pipeline.api;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
public interface BiomeChunk {
PipelineBiome get(int xInChunk, int zInChunk);
}
@@ -1,26 +0,0 @@
/*
* 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);
}
@@ -0,0 +1,29 @@
package com.dfsek.terra.addons.biome.pipeline.api;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
/**
* Resizes the internal grid of a BiomeChunk when applied, serves the purpose of
* filling in null biomes as a result of this resizing.
*/
public interface Expander extends Stage {
PipelineBiome fillBiome(ViewPoint viewPoint);
@Override
default int maxRelativeReadDistance() {
return 0;
}
@Override
default PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome currentBiome = viewPoint.getBiome();
if(currentBiome == null) {
return fillBiome(viewPoint);
} else {
return currentBiome;
}
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.addons.biome.pipeline.api;
import java.util.List;
public interface Pipeline {
BiomeChunk generateChunk(SeededVector worldCoordinates);
int getChunkSize();
Source getSource();
List<Stage> getStages();
}
@@ -0,0 +1,19 @@
package com.dfsek.terra.addons.biome.pipeline.api;
public record SeededVector(long seed, int x, int z) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
@@ -0,0 +1,11 @@
package com.dfsek.terra.addons.biome.pipeline.api;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
public interface Source {
PipelineBiome get(long seed, int x, int z);
Iterable<PipelineBiome> getBiomes();
}
@@ -0,0 +1,15 @@
package com.dfsek.terra.addons.biome.pipeline.api;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
public interface Stage {
PipelineBiome apply(ViewPoint viewPoint);
int maxRelativeReadDistance();
default Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
return biomes;
}
}
@@ -1,14 +1,14 @@
package com.dfsek.terra.addons.biome.pipeline.api.delegate; package com.dfsek.terra.addons.biome.pipeline.api.biome;
import java.util.Set; import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
final class DelegatedBiome implements BiomeDelegate { public final class DelegatedPipelineBiome implements PipelineBiome {
private final Biome biome; private final Biome biome;
public DelegatedBiome(Biome biome) { public DelegatedPipelineBiome(Biome biome) {
this.biome = biome; this.biome = biome;
} }
@@ -24,7 +24,7 @@ final class DelegatedBiome implements BiomeDelegate {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if(!(obj instanceof DelegatedBiome that)) return false; if(!(obj instanceof DelegatedPipelineBiome that)) return false;
return that.biome.equals(this.biome); return that.biome.equals(this.biome);
} }
@@ -0,0 +1,35 @@
package com.dfsek.terra.addons.biome.pipeline.api.biome;
import java.util.Set;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.world.biome.Biome;
public interface PipelineBiome extends StringIdentifiable {
Biome getBiome();
static PipelineBiome placeholder(String id) {
return new PlaceholderPipelineBiome(id);
}
static PipelineBiome from(Biome biome) {
return new DelegatedPipelineBiome(biome);
}
static PipelineBiome self() {
return SelfPipelineBiome.INSTANCE;
}
Set<String> getTags();
default boolean isPlaceholder() {
return false;
}
default boolean isSelf() {
return false;
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.addons.biome.pipeline.api.delegate; package com.dfsek.terra.addons.biome.pipeline.api.biome;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -6,11 +6,11 @@ import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
final class EphemeralBiomeDelegate implements BiomeDelegate { final class PlaceholderPipelineBiome implements PipelineBiome {
private final Set<String> tags; private final Set<String> tags;
private final String id; private final String id;
public EphemeralBiomeDelegate(String id) { public PlaceholderPipelineBiome(String id) {
this.id = id; this.id = id;
tags = new HashSet<>(); tags = new HashSet<>();
tags.add(id); tags.add(id);
@@ -19,7 +19,7 @@ final class EphemeralBiomeDelegate implements BiomeDelegate {
@Override @Override
public Biome getBiome() { public Biome getBiome() {
throw new UnsupportedOperationException("Cannot get biome from ephemeral delegate"); throw new UnsupportedOperationException("Cannot get raw biome from placeholder pipeline biome");
} }
@Override @Override
@@ -33,7 +33,7 @@ final class EphemeralBiomeDelegate implements BiomeDelegate {
} }
@Override @Override
public boolean isEphemeral() { public boolean isPlaceholder() {
return true; return true;
} }
@@ -44,7 +44,7 @@ final class EphemeralBiomeDelegate implements BiomeDelegate {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if(!(obj instanceof EphemeralBiomeDelegate that)) return false; if(!(obj instanceof PlaceholderPipelineBiome that)) return false;
return this.id.equals(that.id); return this.id.equals(that.id);
} }
@@ -1,4 +1,4 @@
package com.dfsek.terra.addons.biome.pipeline.api.delegate; package com.dfsek.terra.addons.biome.pipeline.api.biome;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
@@ -6,10 +6,10 @@ import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
final class SelfDelegate implements BiomeDelegate { final class SelfPipelineBiome implements PipelineBiome {
public static final SelfDelegate INSTANCE = new SelfDelegate(); public static final SelfPipelineBiome INSTANCE = new SelfPipelineBiome();
private SelfDelegate() { private SelfPipelineBiome() {
} }
@@ -24,7 +24,7 @@ final class SelfDelegate implements BiomeDelegate {
} }
@Override @Override
public boolean isEphemeral() { public boolean isPlaceholder() {
return true; return true;
} }
@@ -1,35 +0,0 @@
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<String> getTags();
default boolean isEphemeral() {
return false;
}
default boolean isSelf() {
return false;
}
}
@@ -1,20 +0,0 @@
/*
* 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes);
}
@@ -1,15 +0,0 @@
/*
* 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);
}
@@ -1,37 +0,0 @@
/*
* 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> 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);
}
}
}
@@ -10,45 +10,50 @@ 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.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description; 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.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.BiomePipeline; import com.dfsek.terra.addons.biome.pipeline.PipelineBiomeProvider;
import com.dfsek.terra.addons.biome.pipeline.BiomePipelineProvider; import com.dfsek.terra.addons.biome.pipeline.pipeline.PipelineImpl;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
@SuppressWarnings({ "FieldMayBeFinal", "unused" }) @SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class BiomePipelineTemplate extends BiomeProviderTemplate { public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> {
@Value("pipeline.initial-size") @Value("resolution")
@Default @Default
@Description(""" @Description("""
The initial size of biome chunks. This value must be at least 2. The resolution at which to sample biomes.
<b>This is not the final size of biome chunks. Final chunks will be much larger</b>.
It is recommended to keep biome chunks' final size in the range of [50, 300] Larger values are quadratically faster, but produce lower quality results.
to prevent performance issues. To calculate the size of biome chunks, simply For example, a value of 3 would sample every 3 blocks.""")
take initial-size and for each expand stage, multiply the running value by 2 protected @Meta int resolution = 1;
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") @Value("pipeline.source")
@Description("The Biome Source to use for initial population of biomes.") @Description("The Biome Source to use for initial population of biomes.")
private @Meta BiomeSource source; private @Meta Source source;
@Value("pipeline.stages") @Value("pipeline.stages")
@Description("A list of pipeline stages to apply to the result of #source") @Description("A list of pipeline stages to apply to the result of #source")
private @Meta List<@Meta Stage> stages; private @Meta List<@Meta Stage> stages;
@Value("blend.sampler")
@Default
@Description("A sampler to use for blending the edges of biomes via domain warping.")
protected @Meta NoiseSampler blendSampler = NoiseSampler.zero();
@Value("blend.amplitude")
@Default
@Description("The amplitude at which to perform blending.")
protected @Meta double blendAmplitude = 0d;
@Override @Override
public BiomeProvider get() { public BiomeProvider get() {
BiomePipeline.BiomePipelineBuilder biomePipelineBuilder = new BiomePipeline.BiomePipelineBuilder(initialSize); return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 500), resolution, blendSampler, blendAmplitude);
stages.forEach(biomePipelineBuilder::addStage);
BiomePipeline pipeline = biomePipelineBuilder.build(source);
return new BiomePipelineProvider(pipeline, resolution, blend, blendAmp);
} }
} }
@@ -1,37 +0,0 @@
/*
* 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<BiomeProvider> {
@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;
}
@@ -8,25 +8,25 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType; import java.lang.reflect.AnnotatedType;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.Biome;
public class BiomeDelegateLoader implements TypeLoader<BiomeDelegate> { public class PipelineBiomeLoader implements TypeLoader<PipelineBiome> {
private final Registry<Biome> biomeRegistry; private final Registry<Biome> biomeRegistry;
public BiomeDelegateLoader(Registry<Biome> biomeRegistry) { public PipelineBiomeLoader(Registry<Biome> biomeRegistry) {
this.biomeRegistry = biomeRegistry; this.biomeRegistry = biomeRegistry;
} }
@Override @Override
public BiomeDelegate load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker) public PipelineBiome load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
throws LoadException { throws LoadException {
if(c.equals("SELF")) return BiomeDelegate.self(); if(c.equals("SELF")) return PipelineBiome.self();
return biomeRegistry return biomeRegistry
.getByID((String) c) .getByID((String) c)
.map(BiomeDelegate::from) .map(PipelineBiome::from)
.orElseGet(() -> BiomeDelegate.ephemeral((String) c)); .orElseGet(() -> PipelineBiome.placeholder((String) c));
} }
} }
@@ -5,13 +5,13 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.biome.pipeline.config; package com.dfsek.terra.addons.biome.pipeline.config.source;
import com.dfsek.tectonic.api.config.template.annotations.Description; 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.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.source.SamplerSource; import com.dfsek.terra.addons.biome.pipeline.source.SamplerSource;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
@@ -25,10 +25,10 @@ public class SamplerSourceTemplate extends SourceTemplate {
@Value("biomes") @Value("biomes")
@Description("The biomes to be distributed.") @Description("The biomes to be distributed.")
private @Meta ProbabilityCollection<@Meta BiomeDelegate> biomes; private @Meta ProbabilityCollection<@Meta PipelineBiome> biomes;
@Override @Override
public BiomeSource get() { public Source get() {
return new SamplerSource(biomes, noise); return new SamplerSource(biomes, noise);
} }
} }
@@ -5,13 +5,13 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.biome.pipeline.config; package com.dfsek.terra.addons.biome.pipeline.config.source;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource; import com.dfsek.terra.addons.biome.pipeline.api.Source;
public abstract class SourceTemplate implements ObjectTemplate<BiomeSource> { public abstract class SourceTemplate implements ObjectTemplate<Source> {
} }
@@ -11,7 +11,7 @@ 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.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
@@ -7,15 +7,14 @@
package com.dfsek.terra.addons.biome.pipeline.config.stage.expander; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.expand.FractalExpander; import com.dfsek.terra.addons.biome.pipeline.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.stages.ExpanderStage; import com.dfsek.terra.addons.biome.pipeline.stage.expander.FractalExpander;
public class ExpanderStageTemplate extends StageTemplate { public class ExpanderStageTemplate extends StageTemplate {
@Override @Override
public Stage get() { public Expander get() {
return new ExpanderStage(new FractalExpander(noise)); return new FractalExpander(noise);
} }
} }
@@ -11,17 +11,16 @@ import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.mutator.BorderListMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.BorderListStage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BorderListMutatorTemplate extends StageTemplate { public class BorderListStageTemplate extends StageTemplate {
@Value("from") @Value("from")
private @Meta String from; private @Meta String from;
@@ -29,14 +28,14 @@ public class BorderListMutatorTemplate extends StageTemplate {
private @Meta String defaultReplace; private @Meta String defaultReplace;
@Value("default-to") @Value("default-to")
private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo; private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
@Value("replace") @Value("replace")
private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace; private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
@Override @Override
public Stage get() { public Stage get() {
return new MutatorStage(new BorderListMutator(replace, from, defaultReplace, noise, defaultTo)); return new BorderListStage(replace, from, defaultReplace, noise, defaultTo);
} }
} }
@@ -9,17 +9,16 @@ package com.dfsek.terra.addons.biome.pipeline.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.mutator.BorderMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.BorderStage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BorderMutatorTemplate extends StageTemplate { public class BorderStageTemplate extends StageTemplate {
@Value("from") @Value("from")
private @Meta String from; private @Meta String from;
@@ -27,10 +26,10 @@ public class BorderMutatorTemplate extends StageTemplate {
private @Meta String replace; private @Meta String replace;
@Value("to") @Value("to")
private @Meta ProbabilityCollection<@Meta BiomeDelegate> to; private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
@Override @Override
public Stage get() { public Stage get() {
return new MutatorStage(new BorderMutator(from, replace, noise, to)); return new BorderStage(from, replace, noise, to);
} }
} }
@@ -11,28 +11,27 @@ import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceListMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.ReplaceListStage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ReplaceListMutatorTemplate extends StageTemplate { public class ReplaceListStageTemplate extends StageTemplate {
@Value("default-from") @Value("default-from")
private @Meta String defaultFrom; private @Meta String defaultFrom;
@Value("default-to") @Value("default-to")
private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo; private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
@Value("to") @Value("to")
private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace; private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
@Override @Override
public Stage get() { public Stage get() {
return new MutatorStage(new ReplaceListMutator(replace, defaultFrom, defaultTo, noise)); return new ReplaceListStage(replace, defaultFrom, defaultTo, noise);
} }
} }
@@ -9,25 +9,24 @@ package com.dfsek.terra.addons.biome.pipeline.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.ReplaceStage;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ReplaceMutatorTemplate extends StageTemplate { public class ReplaceStageTemplate extends StageTemplate {
@Value("from") @Value("from")
private @Meta String from; private @Meta String from;
@Value("to") @Value("to")
private @Meta ProbabilityCollection<@Meta BiomeDelegate> to; private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
@Override @Override
public Stage get() { public Stage get() {
return new MutatorStage(new ReplaceMutator(from, to, noise)); return new ReplaceStage(from, to, noise);
} }
} }
@@ -7,15 +7,14 @@
package com.dfsek.terra.addons.biome.pipeline.config.stage.mutator; 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.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.mutator.SmoothMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage; import com.dfsek.terra.addons.biome.pipeline.stage.mutators.SmoothStage;
public class SmoothMutatorTemplate extends StageTemplate { public class SmoothStageTemplate extends StageTemplate {
@Override @Override
public Stage get() { public Stage get() {
return new MutatorStage(new SmoothMutator(noise)); return new SmoothStage(noise);
} }
} }
@@ -1,27 +0,0 @@
/*
* 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)];
}
}
@@ -1,67 +0,0 @@
/*
* 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<BiomeDelegate> replaceDefault;
private final String defaultReplace;
private final Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace;
public BorderListMutator(Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace, String border, String defaultReplace,
NoiseSampler noiseSampler, ProbabilityCollection<BiomeDelegate> 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
Set<BiomeDelegate> 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;
}
}
@@ -1,66 +0,0 @@
/*
* 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<BiomeDelegate> replace;
private final String replaceTag;
public BorderMutator(String border, String replaceTag, NoiseSampler noiseSampler, ProbabilityCollection<BiomeDelegate> 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
Set<BiomeDelegate> biomeSet = new HashSet<>();
biomes.forEach(biomeSet::add);
biomeSet.addAll(
replace
.getContents()
.stream()
.filter(
Predicate.not(BiomeDelegate::isSelf)
)
.toList()
);
return biomeSet;
}
}
@@ -1,46 +0,0 @@
/*
* 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);
}
}
@@ -0,0 +1,218 @@
package com.dfsek.terra.addons.biome.pipeline.pipeline;
import net.jafama.FastMath;
import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
public class BiomeChunkImpl implements BiomeChunk {
private PipelineBiome[][] biomes;
private final SeededVector worldOrigin;
private final int chunkOriginArrayIndex;
private final int worldCoordinateScale;
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) {
this.worldOrigin = worldOrigin;
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex();
this.worldCoordinateScale = pipeline.getResolution();
int size = pipeline.getArraySize();
int expanderCount = pipeline.getExpanderCount();
int expansionsApplied = 0;
// Allocate working arrays
this.biomes = new PipelineBiome[size][size];
PipelineBiome[][] lookupArray = new PipelineBiome[size][size];
// A second lookup array is required such that stage application doesn't affect lookups, otherwise application may cascade
// Construct working grid
int gridOrigin = 0;
int gridInterval = calculateGridInterval(expanderCount, expansionsApplied);
int gridSize = (size / gridInterval);
gridSize += expanderCount > 0 ? 1 : 0; // Add an extra border if expansion occurs
// Fill working grid with initial cells
for(int gridX = 0; gridX < gridSize; gridX++) {
for(int gridZ = 0; gridZ < gridSize; gridZ++) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[xIndex][zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex), zIndexToWorldCoordinate(zIndex));
}
}
for(Stage stage : pipeline.getStages()) {
if(stage instanceof Expander) {
// Shrink working grid size, the expander will fill in null cells (as a result of shrinking the grid) during mutation
expansionsApplied++;
gridInterval = calculateGridInterval(expanderCount, expansionsApplied);
gridSize = expandSize(gridSize);
}
int stageReadDistance = stage.maxRelativeReadDistance();
if(stageReadDistance > 0) {
// Discard edges such that adjacent lookups are only ran on valid cells
gridSize = contractBordersFromSize(gridSize, stageReadDistance);
gridOrigin += stageReadDistance * gridInterval;
}
// Cycle arrays, the previously populated array is swapped to be used for lookups, and the result of the stage application
// overwrites the previous lookup array. This saves having to allocate a new array copy each time
PipelineBiome[][] tempArray = biomes;
biomes = lookupArray;
lookupArray = tempArray;
// Apply stage to working grid
for(int gridZ = 0; gridZ < gridSize; gridZ = gridZ + 1) {
for(int gridX = 0; gridX < gridSize; gridX = gridX + 1) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[xIndex][zIndex] = stage.apply(new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray));
}
}
}
}
@Override
public PipelineBiome get(int xInChunk, int zInChunk) {
int xIndex = xInChunk + chunkOriginArrayIndex;
int zIndex = zInChunk + chunkOriginArrayIndex;
return biomes[xIndex][zIndex];
}
private int xIndexToWorldCoordinate(int xIndex) {
return (worldOrigin.x() + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private int zIndexToWorldCoordinate(int zIndex) {
return (worldOrigin.z() + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
protected static int initialSizeToArraySize(int expanderCount, int initialSize) {
int size = initialSize;
for(int i = 0; i < expanderCount; i++) {
size = expandSize(size);
}
return size;
}
protected static int calculateChunkOriginArrayIndex(int totalExpanderCount, List<Stage> stages) {
int finalGridOrigin = calculateFinalGridOrigin(totalExpanderCount, stages);
int initialGridInterval = calculateGridInterval(totalExpanderCount, 0);
// Round the final grid origin up to the nearest multiple of initialGridInterval, such that each
// chunk samples points on the same overall grid.
// Without this, shared chunk borders (required because of adjacent cell reads) will not be identical
// because points would be sampled on grids at different offsets, resulting in artifacts at borders.
return FastMath.ceilToInt((double) finalGridOrigin / initialGridInterval) * initialGridInterval;
}
private static int calculateFinalGridOrigin(int totalExpanderCount, List<Stage> stages) {
int gridOrigin = 0;
int expansionsApplied = 0;
int gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied);
for (Stage stage : stages) {
if (stage instanceof Expander) {
expansionsApplied++;
gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied);
}
gridOrigin += stage.maxRelativeReadDistance() * gridInterval;
}
return gridOrigin;
}
protected static int calculateChunkSize(int arraySize, int chunkOriginArrayIndex, int totalExpanderCount) {
return contractBordersFromSize(arraySize, chunkOriginArrayIndex) - (totalExpanderCount > 0 ? 1 : 0);
}
private static int expandSize(int size) {
return size * 2 - 1;
}
private static int contractBordersFromSize(int size, int border) {
return size - border * 2;
}
private static int calculateGridInterval(int totalExpansions, int expansionsApplied) {
return 1 << (totalExpansions - expansionsApplied);
}
private SeededVector getOrigin() {
return worldOrigin;
}
/**
* Represents a point on the operating grid within the biomes array
*/
public static class ViewPoint {
private final BiomeChunkImpl chunk;
private final PipelineBiome biome;
private final int gridInterval;
private final int gridX;
private final int gridZ;
private final int xIndex;
private final int zIndex;
private final PipelineBiome[][] lookupArray;
private ViewPoint(BiomeChunkImpl chunk, int gridInterval, int gridX, int gridZ, int xIndex, int zIndex, PipelineBiome[][] lookupArray) {
this.chunk = chunk;
this.gridInterval = gridInterval;
this.gridX = gridX;
this.gridZ = gridZ;
this.xIndex = xIndex;
this.zIndex = zIndex;
this.lookupArray = lookupArray;
this.biome = lookupArray[xIndex][zIndex];
}
public PipelineBiome getRelativeBiome(int x, int z) {
int lookupXIndex = this.xIndex + x * gridInterval;
int lookupZIndex = this.zIndex + z * gridInterval;
return lookupArray[lookupXIndex][lookupZIndex];
}
public PipelineBiome getBiome() {
return biome;
}
/**
* @return X position of the point relative to the operating grid
*/
public int gridX() {
return gridX;
}
/**
* @return Z position of the point relative to the operating grid
*/
public int gridZ() {
return gridZ;
}
/**
* @return X position of the point in the world
*/
public int worldX() {
return chunk.xIndexToWorldCoordinate(xIndex);
}
/**
* @return Z position of the point in the world
*/
public int worldZ() {
return chunk.zIndexToWorldCoordinate(zIndex);
}
public long worldSeed() {
return chunk.getOrigin().seed();
}
}
}
@@ -0,0 +1,92 @@
package com.dfsek.terra.addons.biome.pipeline.pipeline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
public class PipelineImpl implements Pipeline {
private static final Logger logger = LoggerFactory.getLogger(PipelineImpl.class);
private final Source source;
private final List<Stage> stages;
private final int chunkSize;
private final int expanderCount;
private final int arraySize;
private final int chunkOriginArrayIndex;
private final int resolution;
public PipelineImpl(Source source, List<Stage> stages, int resolution, int idealChunkArraySize) {
this.source = source;
this.stages = stages;
this.resolution = resolution;
this.expanderCount = (int) stages.stream().filter(s -> s instanceof Expander).count();
// Optimize for the ideal array size
int arraySize;
int chunkOriginArrayIndex;
int chunkSize;
int initialSize = 1;
while (true) {
arraySize = BiomeChunkImpl.initialSizeToArraySize(expanderCount, initialSize);
chunkOriginArrayIndex = BiomeChunkImpl.calculateChunkOriginArrayIndex(expanderCount, stages);
chunkSize = BiomeChunkImpl.calculateChunkSize(arraySize, chunkOriginArrayIndex, expanderCount);
if (chunkSize > 1 && arraySize >= idealChunkArraySize) break;
initialSize++;
}
this.arraySize = arraySize;
this.chunkOriginArrayIndex = chunkOriginArrayIndex;
this.chunkSize = chunkSize;
logger.debug("Initialized a new biome pipeline:");
logger.debug("Array size: {} (Target: {})", arraySize, idealChunkArraySize);
logger.debug("Internal array origin: {}", chunkOriginArrayIndex);
logger.debug("Chunk size: {}", chunkSize);
}
@Override
public BiomeChunk generateChunk(SeededVector worldCoordinates) {
return new BiomeChunkImpl(worldCoordinates, this);
}
@Override
public int getChunkSize() {
return chunkSize;
}
@Override
public Source getSource() {
return source;
}
@Override
public List<Stage> getStages() {
return stages;
}
protected int getExpanderCount() {
return expanderCount;
}
protected int getArraySize() {
return arraySize;
}
protected int getChunkOriginArrayIndex() {
return chunkOriginArrayIndex;
}
protected int getResolution() {
return resolution;
}
}
@@ -1,17 +0,0 @@
/*
* 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<BiomeDelegate> getBiomes();
}
@@ -1,33 +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.source; package com.dfsek.terra.addons.biome.pipeline.source;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class SamplerSource implements BiomeSource { public class SamplerSource implements Source {
private final ProbabilityCollection<BiomeDelegate> biomes; private final ProbabilityCollection<PipelineBiome> biomes;
private final NoiseSampler sampler; private final NoiseSampler sampler;
public SamplerSource(ProbabilityCollection<BiomeDelegate> biomes, NoiseSampler sampler) { public SamplerSource(ProbabilityCollection<PipelineBiome> biomes, NoiseSampler sampler) {
this.biomes = biomes; this.biomes = biomes;
this.sampler = sampler; this.sampler = sampler;
} }
@Override @Override
public BiomeDelegate getBiome(double x, double z, long seed) { public PipelineBiome get(long seed, int x, int z) {
return biomes.get(sampler, x, z, seed); return biomes.get(sampler, x, z, seed);
} }
@Override @Override
public Iterable<BiomeDelegate> getBiomes() { public Iterable<PipelineBiome> getBiomes() {
return biomes.getContents(); return biomes.getContents();
} }
} }
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.biome.pipeline.source;
import java.util.Collections;
import java.util.Set;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.api.Source;
public class SingleSource implements Source {
private final PipelineBiome biome;
public SingleSource(PipelineBiome biome) {
this.biome = biome;
}
@Override
public PipelineBiome get(long seed, int x, int z) {
return biome;
}
@Override
public Set<PipelineBiome> getBiomes() {
return Collections.singleton(biome);
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.addons.biome.pipeline.stage.expander;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
public class FractalExpander implements Expander {
private final NoiseSampler sampler;
public FractalExpander(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public PipelineBiome fillBiome(ViewPoint viewPoint) {
int xMod2 = viewPoint.gridX() % 2;
int zMod2 = viewPoint.gridZ() % 2;
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ());
if (xMod2 == 1 && zMod2 == 0) { // Pick one of 2 neighbors on X axis randomly
return roll > 0 ? viewPoint.getRelativeBiome(-1, 0) : viewPoint.getRelativeBiome(1, 0);
} else if (xMod2 == 0 && zMod2 == 1) { // Pick one of 2 neighbors on Z axis randomly
return roll > 0 ? viewPoint.getRelativeBiome(0, -1) : viewPoint.getRelativeBiome(0, 1);
} else { // Pick one of 4 corners randomly
return roll > 0 ?
roll > 0.25 ? viewPoint.getRelativeBiome(-1, 1) : viewPoint.getRelativeBiome(1, 1) :
roll > -0.25 ? viewPoint.getRelativeBiome(-1, -1) : viewPoint.getRelativeBiome(1, -1);
}
}
}
@@ -0,0 +1,86 @@
/*
* 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.stage.mutators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.vector.Vector2Int;
public class BorderListStage implements Stage {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<PipelineBiome> replaceDefault;
private final String defaultReplace;
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
private final Vector2Int[] borderPoints;
public BorderListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String border, String defaultReplace,
NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replaceDefault) {
this.border = border;
this.noiseSampler = noiseSampler;
this.replaceDefault = replaceDefault;
this.defaultReplace = defaultReplace;
this.replace = replace;
List<Vector2Int> points = new ArrayList<>();
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
if(x == 0 && z == 0) continue;
points.add(Vector2Int.of(x, z));
}
}
this.borderPoints = points.toArray(new Vector2Int[0]);
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
biomes.forEach(biomeSet::add);
biomeSet.addAll(replaceDefault.getContents().stream().filter(Predicate.not(PipelineBiome::isSelf)).toList());
replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents()));
return biomeSet;
}
@Override
public PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
if(center.getTags().contains(defaultReplace)) {
for(Vector2Int point : borderPoints) {
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ());
if(current != null && current.getTags().contains(border)) {
if(replace.containsKey(center)) {
PipelineBiome replacement = replace.get(center).get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(),
viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
PipelineBiome replacement = replaceDefault.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
}
}
return center;
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -0,0 +1,81 @@
/*
* 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.stage.mutators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.vector.Vector2Int;
public class BorderStage implements Stage {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<PipelineBiome> replace;
private final String replaceTag;
private final Vector2Int[] borderPoints;
public BorderStage(String border, String replaceTag, NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replace) {
this.border = border;
this.noiseSampler = noiseSampler;
this.replace = replace;
this.replaceTag = replaceTag;
List<Vector2Int> points = new ArrayList<>();
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
if(x == 0 && z == 0) continue;
points.add(Vector2Int.of(x, z));
}
}
this.borderPoints = points.toArray(new Vector2Int[0]);
}
@Override
public PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
if(center.getTags().contains(replaceTag)) {
for(Vector2Int point : borderPoints) {
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ());
if(current != null && current.getTags().contains(border)) {
PipelineBiome replacement = replace.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
}
}
return center;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
biomes.forEach(biomeSet::add);
biomeSet.addAll(
replace
.getContents()
.stream()
.filter(
Predicate.not(PipelineBiome::isSelf)
)
.toList()
);
return biomeSet;
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -5,27 +5,28 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.biome.pipeline.mutator; package com.dfsek.terra.addons.biome.pipeline.stage.mutators;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class ReplaceListMutator implements BiomeMutator { public class ReplaceListStage implements Stage {
private final Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace; private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
private final NoiseSampler sampler; private final NoiseSampler sampler;
private final ProbabilityCollection<BiomeDelegate> replaceDefault; private final ProbabilityCollection<PipelineBiome> replaceDefault;
private final String defaultTag; private final String defaultTag;
public ReplaceListMutator(Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace, String defaultTag, public ReplaceListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String defaultTag,
ProbabilityCollection<BiomeDelegate> replaceDefault, NoiseSampler sampler) { ProbabilityCollection<PipelineBiome> replaceDefault, NoiseSampler sampler) {
this.replace = replace; this.replace = replace;
this.sampler = sampler; this.sampler = sampler;
this.defaultTag = defaultTag; this.defaultTag = defaultTag;
@@ -33,24 +34,29 @@ public class ReplaceListMutator implements BiomeMutator {
} }
@Override @Override
public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { public PipelineBiome apply(ViewPoint viewPoint) {
BiomeDelegate center = viewPoint.getBiome(0, 0); PipelineBiome center = viewPoint.getBiome();
if(replace.containsKey(center)) { if(replace.containsKey(center)) {
BiomeDelegate biome = replace.get(center).get(sampler, x, z, seed); PipelineBiome biome = replace.get(center).get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; return biome.isSelf() ? viewPoint.getBiome() : biome;
} }
if(viewPoint.getBiome(0, 0).getTags().contains(defaultTag)) { if(viewPoint.getBiome().getTags().contains(defaultTag)) {
BiomeDelegate biome = replaceDefault.get(sampler, x, z, seed); PipelineBiome biome = replaceDefault.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; return biome.isSelf() ? viewPoint.getBiome() : biome;
} }
return center; return center;
} }
@Override @Override
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) { public int maxRelativeReadDistance() {
Set<BiomeDelegate> biomeSet = new HashSet<>(); return 0;
}
Set<BiomeDelegate> reject = new HashSet<>(); @Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
biomes.forEach(biome -> { biomes.forEach(biome -> {
if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) { if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) {
@@ -5,42 +5,48 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.biome.pipeline.mutator; package com.dfsek.terra.addons.biome.pipeline.stage.mutators;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate; import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator; import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection; import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class ReplaceMutator implements BiomeMutator { public class ReplaceStage implements Stage {
private final String replaceableTag; private final String replaceableTag;
private final ProbabilityCollection<BiomeDelegate> replace; private final ProbabilityCollection<PipelineBiome> replace;
private final NoiseSampler sampler; private final NoiseSampler sampler;
public ReplaceMutator(String replaceable, ProbabilityCollection<BiomeDelegate> replace, NoiseSampler sampler) { public ReplaceStage(String replaceable, ProbabilityCollection<PipelineBiome> replace, NoiseSampler sampler) {
this.replaceableTag = replaceable; this.replaceableTag = replaceable;
this.replace = replace; this.replace = replace;
this.sampler = sampler; this.sampler = sampler;
} }
@Override @Override
public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) { public PipelineBiome apply(ViewPoint viewPoint) {
if(viewPoint.getBiome(0, 0).getTags().contains(replaceableTag)) { if(viewPoint.getBiome().getTags().contains(replaceableTag)) {
BiomeDelegate biome = replace.get(sampler, x, z, seed); PipelineBiome biome = replace.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome; return biome.isSelf() ? viewPoint.getBiome() : biome;
} }
return viewPoint.getBiome(0, 0); return viewPoint.getBiome();
} }
@Override @Override
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) { public int maxRelativeReadDistance() {
Set<BiomeDelegate> biomeSet = new HashSet<>(); return 0;
Set<BiomeDelegate> reject = new HashSet<>(); }
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
biomes.forEach(biome -> { biomes.forEach(biome -> {
if(!biome.getTags().contains(replaceableTag)) { if(!biome.getTags().contains(replaceableTag)) {
biomeSet.add(biome); biomeSet.add(biome);
@@ -0,0 +1,56 @@
/*
* 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.stage.mutators;
import java.util.Objects;
import com.dfsek.terra.addons.biome.pipeline.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
public class SmoothStage implements Stage {
private final NoiseSampler sampler;
public SmoothStage(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome top = viewPoint.getRelativeBiome(1, 0);
PipelineBiome bottom = viewPoint.getRelativeBiome(-1, 0);
PipelineBiome left = viewPoint.getRelativeBiome(0, 1);
PipelineBiome right = viewPoint.getRelativeBiome(0, -1);
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ());
boolean vert = Objects.equals(top, bottom);
boolean horiz = Objects.equals(left, right);
if(vert && horiz) {
return roll > 0 ?
roll > 0.25 ? left : right :
roll > -0.25 ? top : bottom;
}
if(vert) {
return roll > 0 ? top : bottom;
}
if(horiz) {
return roll > 0 ? left : right;
}
return viewPoint.getBiome();
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -1,37 +0,0 @@
/*
* 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
return biomes;
}
}
@@ -1,46 +0,0 @@
/*
* 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<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
return mutator.getBiomes(biomes);
}
public enum Type {
REPLACE,
REPLACE_LIST,
BORDER,
BORDER_LIST,
SMOOTH
}
}
@@ -2,11 +2,4 @@ version = version("1.1.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.chunkgenerator.lib.jafama")
} }
@@ -7,6 +7,9 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation;
import com.dfsek.terra.api.util.MathUtil;
/** /**
* Class for bilinear interpolation of values arranged on a unit square. * Class for bilinear interpolation of values arranged on a unit square.
*/ */
@@ -28,19 +31,6 @@ public class Interpolator {
this.v3 = v3; this.v3 = v3;
} }
/**
* 1D Linear interpolation between 2 points 1 unit apart.
*
* @param t - Distance from v0. Total distance between v0 and v1 is 1 unit.
* @param v0 - Value at v0.
* @param v1 - Value at v1.
*
* @return double - The interpolated value.
*/
public static double lerp(double t, double v0, double v1) {
return v0 + t * (v1 - v0);
}
/** /**
* 2D Bilinear interpolation between 4 points on a unit square. * 2D Bilinear interpolation between 4 points on a unit square.
* *
@@ -50,8 +40,8 @@ public class Interpolator {
* @return double - The interpolated value. * @return double - The interpolated value.
*/ */
public double bilerp(double s, double t) { public double bilerp(double s, double t) {
double v01 = lerp(s, v0, v1); double v01 = MathUtil.lerp(s, v0, v1);
double v23 = lerp(s, v2, v3); double v23 = MathUtil.lerp(s, v2, v3);
return lerp(t, v01, v23); return MathUtil.lerp(t, v01, v23);
} }
} }
@@ -7,6 +7,9 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation;
import com.dfsek.terra.api.util.MathUtil;
/** /**
* Class for bilinear interpolation of values arranged on a unit square. * Class for bilinear interpolation of values arranged on a unit square.
*/ */
@@ -34,6 +37,6 @@ public class Interpolator3 {
} }
public double trilerp(double x, double y, double z) { public double trilerp(double x, double y, double z) {
return Interpolator.lerp(x, top.bilerp(y, z), bottom.bilerp(y, z)); return MathUtil.lerp(x, top.bilerp(y, z), bottom.bilerp(y, z));
} }
} }
@@ -1,13 +1,13 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation; package com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation;
import com.dfsek.terra.api.util.MathUtil;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties; import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.api.properties.PropertyKey; import com.dfsek.terra.api.properties.PropertyKey;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import static com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.Interpolator.lerp;
public class LazilyEvaluatedInterpolator { public class LazilyEvaluatedInterpolator {
private final Double[] samples; // private final Double[] samples; //
@@ -84,10 +84,10 @@ public class LazilyEvaluatedInterpolator {
double xFrac = (double) (x % horizontalRes) / horizontalRes; double xFrac = (double) (x % horizontalRes) / horizontalRes;
double zFrac = (double) (z % horizontalRes) / horizontalRes; double zFrac = (double) (z % horizontalRes) / horizontalRes;
double lerp_bottom_0 = lerp(zFrac, sample_0_0_0, sample_0_0_1); double lerp_bottom_0 = MathUtil.lerp(zFrac, sample_0_0_0, sample_0_0_1);
double lerp_bottom_1 = lerp(zFrac, sample_1_0_0, sample_1_0_1); double lerp_bottom_1 = MathUtil.lerp(zFrac, sample_1_0_0, sample_1_0_1);
double lerp_bottom = lerp(xFrac, lerp_bottom_0, lerp_bottom_1); double lerp_bottom = MathUtil.lerp(xFrac, lerp_bottom_0, lerp_bottom_1);
if(yRange) { // we can do bilerp if(yRange) { // we can do bilerp
return lerp_bottom; return lerp_bottom;
@@ -103,11 +103,11 @@ public class LazilyEvaluatedInterpolator {
double sample_1_1_0 = sample(xIndex + 1, yIndex + 1, zIndex, x + horizontalRes, y + verticalRes, z); double sample_1_1_0 = sample(xIndex + 1, yIndex + 1, zIndex, x + horizontalRes, y + verticalRes, z);
double sample_1_1_1 = sample(xIndex + 1, yIndex + 1, zIndex + 1, x + horizontalRes, y + verticalRes, z + horizontalRes); double sample_1_1_1 = sample(xIndex + 1, yIndex + 1, zIndex + 1, x + horizontalRes, y + verticalRes, z + horizontalRes);
double lerp_top_0 = lerp(zFrac, sample_0_1_0, sample_0_1_1); double lerp_top_0 = MathUtil.lerp(zFrac, sample_0_1_0, sample_0_1_1);
double lerp_top_1 = lerp(zFrac, sample_1_1_0, sample_1_1_1); double lerp_top_1 = MathUtil.lerp(zFrac, sample_1_1_0, sample_1_1_1);
double lerp_top = lerp(xFrac, lerp_top_0, lerp_top_1); double lerp_top = MathUtil.lerp(xFrac, lerp_top_0, lerp_top_1);
return lerp(yFrac, lerp_bottom, lerp_top); return MathUtil.lerp(yFrac, lerp_bottom, lerp_top);
} }
} }
@@ -6,21 +6,18 @@ import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.LongArgument; import cloud.commandframework.arguments.standard.LongArgument;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import java.util.Random; import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.manifest.api.MonadAddonInitializer; import com.dfsek.terra.addons.manifest.api.MonadAddonInitializer;
import com.dfsek.terra.addons.manifest.api.monad.Do; import com.dfsek.terra.addons.manifest.api.monad.Do;
import com.dfsek.terra.addons.manifest.api.monad.Get; import com.dfsek.terra.addons.manifest.api.monad.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init; import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.command.CommandSender; import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.command.arguments.RegistryArgument; import com.dfsek.terra.api.command.arguments.RegistryArgument;
import com.dfsek.terra.api.entity.Entity; import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent; import com.dfsek.terra.api.event.events.platform.CommandRegistrationEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.structure.Structure; import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.Rotation; import com.dfsek.terra.api.util.Rotation;
@@ -59,9 +56,7 @@ public class StructureCommandAddon implements MonadAddonInitializer {
structure.generate( structure.generate(
sender.position().toInt(), sender.position().toInt(),
sender.world(), sender.world(),
((Long) context.get("seed") == 0) ? new Random() : new Random(context.get("seed")), context.get("rotation"));
context.get("rotation")
);
}) })
.permission("terra.structures.generate") .permission("terra.structures.generate")
); );
@@ -2,11 +2,4 @@ version = version("1.0.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.feature.distributor.lib.jafama")
} }
@@ -14,7 +14,7 @@ import java.util.function.Supplier;
import com.dfsek.terra.addons.feature.distributor.config.AndDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.AndDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.NoDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.NoDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.OrDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.OrDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.PaddedGridDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.PaddedGridSamplerDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.PointSetDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.PointSetDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.SamplerDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.SamplerDistributorTemplate;
import com.dfsek.terra.addons.feature.distributor.config.XorDistributorTemplate; import com.dfsek.terra.addons.feature.distributor.config.XorDistributorTemplate;
@@ -27,33 +27,31 @@ import com.dfsek.terra.addons.manifest.api.monad.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init; import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.feature.Distributor; import com.dfsek.terra.api.structure.feature.Distributor;
import com.dfsek.terra.api.util.function.monad.Monad; import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.util.reflection.TypeKey; import com.dfsek.terra.api.util.reflection.TypeKey;
import org.jetbrains.annotations.NotNull;
public class DistributorAddon implements MonadAddonInitializer { public class DistributorAddon implements MonadAddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<Distributor>>> DISTRIBUTOR_TOKEN = new TypeKey<>() { public static final TypeKey<Supplier<ObjectTemplate<Distributor>>> DISTRIBUTOR_TOKEN = new TypeKey<>() {
}; };
@Override @Override
public @NotNull Monad<?, Init<?>> initialize() { public Monad<?, Init<?>> initialize() {
return Do.with( return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)), Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(), Get.addon(),
((functionalEventHandler, base) -> Init.ofPure( ((functionalEventHandler, base) -> Init.ofPure(
functionalEventHandler.register(base, ConfigPackPreLoadEvent.class) functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<Distributor>>> distributorRegistry = event CheckedRegistry<Supplier<ObjectTemplate<Distributor>>> distributorRegistry = event
.getPack() .getPack()
.createRegistry(DISTRIBUTOR_TOKEN); .getOrCreateRegistry(DISTRIBUTOR_TOKEN);
distributorRegistry.register(base.key("SAMPLER"), SamplerDistributorTemplate::new); distributorRegistry.register(base.key("SAMPLER"), SamplerDistributorTemplate::new);
distributorRegistry.register(base.key("POINTS"), PointSetDistributorTemplate::new); distributorRegistry.register(base.key("POINTS"), PointSetDistributorTemplate::new);
distributorRegistry.register(base.key("PADDED_GRID"), PaddedGridDistributorTemplate::new); distributorRegistry.register(base.key("PADDED_GRID_SAMPLER"), PaddedGridSamplerDistributorTemplate::new);
distributorRegistry.register(base.key("AND"), AndDistributorTemplate::new); distributorRegistry.register(base.key("AND"), AndDistributorTemplate::new);
distributorRegistry.register(base.key("OR"), OrDistributorTemplate::new); distributorRegistry.register(base.key("OR"), OrDistributorTemplate::new);
distributorRegistry.register(base.key("XOR"), XorDistributorTemplate::new); distributorRegistry.register(base.key("XOR"), XorDistributorTemplate::new);
@@ -3,23 +3,24 @@ package com.dfsek.terra.addons.feature.distributor.config;
import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.feature.distributor.distributors.PaddedGridDistributor; import com.dfsek.terra.addons.feature.distributor.distributors.PaddedGridSamplerDistributor;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.structure.feature.Distributor; import com.dfsek.terra.api.structure.feature.Distributor;
public class PaddedGridDistributorTemplate implements ObjectTemplate<Distributor> { public class PaddedGridSamplerDistributorTemplate implements ObjectTemplate<Distributor> {
@Value("width") @Value("width")
private @Meta int width; private @Meta int width;
@Value("padding") @Value("padding")
private @Meta int padding; private @Meta int padding;
@Value("salt") @Value("sampler")
private @Meta int salt; private @Meta NoiseSampler noise;
@Override @Override
public Distributor get() { public Distributor get() {
return new PaddedGridDistributor(width, padding, salt); return new PaddedGridSamplerDistributor(noise, width, padding);
} }
} }
@@ -1,45 +0,0 @@
package com.dfsek.terra.addons.feature.distributor.distributors;
import net.jafama.FastMath;
import java.util.Random;
import com.dfsek.terra.api.structure.feature.Distributor;
import com.dfsek.terra.api.util.MathUtil;
public class PaddedGridDistributor implements Distributor {
private final int width;
private final int cellWidth;
private final int salt;
public PaddedGridDistributor(int width, int padding, int salt) {
this.width = width;
this.salt = salt;
this.cellWidth = width + padding;
}
private static long murmur64(long h) {
h ^= h >>> 33;
h *= 0xff51afd7ed558ccdL;
h ^= h >>> 33;
h *= 0xc4ceb9fe1a85ec53L;
h ^= h >>> 33;
return h;
}
@Override
public boolean matches(int x, int z, long seed) {
int cellX = FastMath.floorDiv(x, cellWidth);
int cellZ = FastMath.floorDiv(z, cellWidth);
Random random = new Random((murmur64(MathUtil.squash(cellX, cellZ)) ^ seed) + salt);
int pointX = random.nextInt(width) + cellX * cellWidth;
int pointZ = random.nextInt(width) + cellZ * cellWidth;
return x == pointX && z == pointZ;
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.addons.feature.distributor.distributors;
import com.dfsek.terra.api.noise.NoiseSampler;
import net.jafama.FastMath;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.structure.feature.Distributor;
import com.dfsek.terra.api.util.MathUtil;
public class PaddedGridSamplerDistributor implements Distributor {
private final NoiseSampler sampler;
private final int width;
private final int cellWidth;
public PaddedGridSamplerDistributor(NoiseSampler sampler, int width, int padding) {
this.sampler = sampler;
this.width = width;
this.cellWidth = width + padding;
}
@Override
public boolean matches(int x, int z, long seed) {
int cellX = FastMath.floorDiv(x, cellWidth);
int cellZ = FastMath.floorDiv(z, cellWidth);
int pointX = (int) (FastMath.round(MathUtil.lerp(MathUtil.inverseLerp(sampler.noise(x, z, seed), -1, 1), 0, width)) + cellX * cellWidth);
int pointZ = (int) (FastMath.round(MathUtil.lerp(MathUtil.inverseLerp(sampler.noise(x, z, seed + 1), -1, 1), 0, width)) + cellZ * cellWidth);
return x == pointX && z == pointZ;
}
}
@@ -2,10 +2,4 @@ version = version("1.0.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.flora.lib.jafama")
} }
@@ -12,7 +12,8 @@ import net.jafama.FastMath;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.properties.enums.Direction; import com.dfsek.terra.api.block.state.properties.enums.Direction;
@@ -73,7 +74,7 @@ public class TerraFlora implements Structure {
} }
@Override @Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation) {
boolean doRotation = testRotation.size() > 0; boolean doRotation = testRotation.size() > 0;
int size = layers.size(); int size = layers.size();
int c = ceiling ? -1 : 1; int c = ceiling ? -1 : 1;
@@ -86,10 +87,7 @@ public class TerraFlora implements Structure {
int lvl = (FastMath.abs(i)); int lvl = (FastMath.abs(i));
BlockState data = getStateCollection((ceiling ? lvl : size - lvl - 1)).get(distribution, location.getX(), location.getY(), BlockState data = getStateCollection((ceiling ? lvl : size - lvl - 1)).get(distribution, location.getX(), location.getY(),
location.getZ(), world.getSeed()); location.getZ(), world.getSeed());
if(doRotation) {
Direction oneFace = new ArrayList<>(faces).get(
new Random(location.getX() ^ location.getZ()).nextInt(faces.size())); // Get random face.
}
world.setBlockState(location.mutable().add(0, i + c, 0).immutable(), data, physics); world.setBlockState(location.mutable().add(0, i + c, 0).immutable(), data, physics);
} }
return true; return true;
@@ -2,11 +2,4 @@ version = version("1.1.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.feature.locator.lib.jafama")
} }
@@ -13,10 +13,8 @@ import java.util.function.Supplier;
import com.dfsek.terra.addons.feature.locator.config.AdjacentPatternLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.AdjacentPatternLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.AndLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.AndLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.GaussianRandomLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.OrLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.OrLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.PatternLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.PatternLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.RandomLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.Sampler3DLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.Sampler3DLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.SamplerLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.SamplerLocatorTemplate;
import com.dfsek.terra.addons.feature.locator.config.SurfaceLocatorTemplate; import com.dfsek.terra.addons.feature.locator.config.SurfaceLocatorTemplate;
@@ -37,13 +35,11 @@ import com.dfsek.terra.addons.manifest.api.monad.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init; import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.structure.feature.Locator; import com.dfsek.terra.api.structure.feature.Locator;
import com.dfsek.terra.api.util.function.monad.Monad; import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.util.reflection.TypeKey; import com.dfsek.terra.api.util.reflection.TypeKey;
import org.jetbrains.annotations.NotNull;
public class LocatorAddon implements MonadAddonInitializer { public class LocatorAddon implements MonadAddonInitializer {
@@ -53,7 +49,7 @@ public class LocatorAddon implements MonadAddonInitializer {
}; };
@Override @Override
public @NotNull Monad<?, Init<?>> initialize() { public Monad<?, Init<?>> initialize() {
return Do.with( return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)), Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(), Get.addon(),
@@ -61,13 +57,10 @@ public class LocatorAddon implements MonadAddonInitializer {
((functionalEventHandler, base, platform) -> Init.ofPure( ((functionalEventHandler, base, platform) -> Init.ofPure(
functionalEventHandler.register(base, ConfigPackPreLoadEvent.class) functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<Locator>>> locatorRegistry = event.getPack().createRegistry(LOCATOR_TOKEN); CheckedRegistry<Supplier<ObjectTemplate<Locator>>> locatorRegistry = event.getPack().getOrCreateRegistry(LOCATOR_TOKEN);
locatorRegistry.register(base.key("SURFACE"), SurfaceLocatorTemplate::new); locatorRegistry.register(base.key("SURFACE"), SurfaceLocatorTemplate::new);
locatorRegistry.register(base.key("TOP"), TopLocatorTemplate::new); locatorRegistry.register(base.key("TOP"), TopLocatorTemplate::new);
locatorRegistry.register(base.key("RANDOM"), RandomLocatorTemplate::new);
locatorRegistry.register(base.key("GAUSSIAN_RANDOM"), GaussianRandomLocatorTemplate::new);
locatorRegistry.register(base.key("PATTERN"), PatternLocatorTemplate::new); locatorRegistry.register(base.key("PATTERN"), PatternLocatorTemplate::new);
locatorRegistry.register(base.key("ADJACENT_PATTERN"), AdjacentPatternLocatorTemplate::new); locatorRegistry.register(base.key("ADJACENT_PATTERN"), AdjacentPatternLocatorTemplate::new);
@@ -79,7 +72,7 @@ public class LocatorAddon implements MonadAddonInitializer {
locatorRegistry.register(base.key("XOR"), XorLocatorTemplate::new); locatorRegistry.register(base.key("XOR"), XorLocatorTemplate::new);
}) })
.then(event -> { .then(event -> {
Registry<Supplier<ObjectTemplate<Pattern>>> patternRegistry = event.getPack().createRegistry(PATTERN_TOKEN); CheckedRegistry<Supplier<ObjectTemplate<Pattern>>> patternRegistry = event.getPack().getOrCreateRegistry(PATTERN_TOKEN);
patternRegistry.register(base.key("MATCH_AIR"), AirMatchPatternTemplate::new); patternRegistry.register(base.key("MATCH_AIR"), AirMatchPatternTemplate::new);
patternRegistry.register(base.key("MATCH_SOLID"), SolidMatchPatternTemplate::new); patternRegistry.register(base.key("MATCH_SOLID"), SolidMatchPatternTemplate::new);
patternRegistry.register(base.key("MATCH"), SingleBlockMatchPatternTemplate::new); patternRegistry.register(base.key("MATCH"), SingleBlockMatchPatternTemplate::new);
@@ -1,39 +0,0 @@
/*
* 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.feature.locator.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.feature.locator.locators.GaussianRandomLocator;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.structure.feature.Locator;
import com.dfsek.terra.api.util.Range;
@SuppressWarnings("FieldMayBeFinal")
public class GaussianRandomLocatorTemplate implements ObjectTemplate<Locator> {
@Value("height")
private @Meta Range height;
@Value("amount")
private @Meta Range amount;
@Value("standard-deviation")
private @Meta double standardDeviation;
@Value("salt")
@Default
private @Meta int salt = 0;
@Override
public Locator get() {
return new GaussianRandomLocator(height, amount, standardDeviation, salt);
}
}
@@ -1,36 +0,0 @@
/*
* 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.feature.locator.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.feature.locator.locators.RandomLocator;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.structure.feature.Locator;
import com.dfsek.terra.api.util.Range;
@SuppressWarnings("FieldMayBeFinal")
public class RandomLocatorTemplate implements ObjectTemplate<Locator> {
@Value("height")
private @Meta Range height;
@Value("amount")
private @Meta Range amount;
@Value("salt")
@Default
private @Meta int salt = 0;
@Override
public Locator get() {
return new RandomLocator(height, amount, salt);
}
}
@@ -1,57 +0,0 @@
/*
* 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.feature.locator.locators;
import java.util.Random;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
import com.dfsek.terra.api.util.Range;
import com.dfsek.terra.api.world.chunk.generation.util.Column;
import com.dfsek.terra.api.world.chunk.generation.util.Column.BinaryColumnBuilder;
public class GaussianRandomLocator implements Locator {
private final double mean;
private final Range points;
private final double standardDeviation;
private final int salt;
public GaussianRandomLocator(Range height, Range points, double standardDeviation, int salt) {
this.mean = (height.getMax() + height.getMin()) / 2.0;
this.points = points;
this.standardDeviation = standardDeviation;
this.salt = salt;
}
@Override
public BinaryColumn getSuitableCoordinates(Column<?> column) {
long seed = column.getWorld().getSeed();
seed = 31 * seed + column.getX();
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
int size = points.get(r);
BinaryColumnBuilder results = column.newBinaryColumn();
for(int i = 0; i < size; i++) {
int h = (int) r.nextGaussian(mean, standardDeviation);
if(h >= column.getMaxY() || h < column.getMinY()) continue;
results.set(h);
}
return results.build();
}
}
@@ -1,52 +0,0 @@
/*
* 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.feature.locator.locators;
import java.util.Random;
import com.dfsek.terra.api.structure.feature.BinaryColumn;
import com.dfsek.terra.api.structure.feature.Locator;
import com.dfsek.terra.api.util.Range;
import com.dfsek.terra.api.world.chunk.generation.util.Column;
import com.dfsek.terra.api.world.chunk.generation.util.Column.BinaryColumnBuilder;
public class RandomLocator implements Locator {
private final Range height;
private final Range points;
private final int salt;
public RandomLocator(Range height, Range points, int salt) {
this.height = height;
this.points = points;
this.salt = salt;
}
@Override
public BinaryColumn getSuitableCoordinates(Column<?> column) {
long seed = column.getWorld().getSeed();
seed = 31 * seed + column.getX();
seed = 31 * seed + column.getZ();
seed += salt;
Random r = new Random(seed);
int size = points.get(r);
BinaryColumnBuilder results = column.newBinaryColumn();
for(int i = 0; i < size; i++) {
int h = height.get(r);
if(h >= column.getMaxY() || h < column.getMinY()) continue;
results.set(h);
}
return results.build();
}
}
@@ -4,13 +4,4 @@ version = version("1.0.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) compileOnlyApi(project(":common:addons:manifest-addon-loader"))
api("com.dfsek", "paralithic", Versions.Libraries.paralithic)
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<ShadowJar>("shadowJar") {
relocate("com.dfsek.paralithic", "com.dfsek.terra.addons.noise.lib.paralithic")
relocate("net.jafama", "com.dfsek.terra.addons.noise.lib.jafama")
} }
+1 -6
View File
@@ -2,10 +2,5 @@ version = version("1.0.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama) compileOnlyApi(project(":common:addons:config-noise-function"))
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")
} }
@@ -7,6 +7,7 @@
package com.dfsek.terra.addons.ore; package com.dfsek.terra.addons.ore;
import com.dfsek.terra.addons.noise.samplers.noise.random.WhiteNoiseSampler;
import com.dfsek.terra.addons.ore.ores.VanillaOre; import com.dfsek.terra.addons.ore.ores.VanillaOre;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
@@ -18,6 +19,6 @@ public class OreFactory implements ConfigFactory<OreTemplate, Structure> {
public VanillaOre build(OreTemplate config, Platform platform) { public VanillaOre build(OreTemplate config, Platform platform) {
BlockState m = config.getMaterial(); BlockState m = config.getMaterial();
return new VanillaOre(m, config.getSize(), config.getReplaceable(), config.doPhysics(), config.isExposed(), return new VanillaOre(m, config.getSize(), config.getReplaceable(), config.doPhysics(), config.isExposed(),
config.getMaterialOverrides()); config.getMaterialOverrides(), new WhiteNoiseSampler());
} }
} }
@@ -7,10 +7,13 @@
package com.dfsek.terra.addons.ore.ores; package com.dfsek.terra.addons.ore.ores;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
import net.jafama.FastMath; import net.jafama.FastMath;
import java.util.Map; import java.util.Map;
import java.util.Random;
import com.dfsek.terra.api.block.BlockType; import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
@@ -30,38 +33,40 @@ public class VanillaOre implements Structure {
private final boolean applyGravity; private final boolean applyGravity;
private final double exposed; private final double exposed;
private final Map<BlockType, BlockState> materials; private final Map<BlockType, BlockState> materials;
private final NoiseSampler sampler;
public VanillaOre(BlockState material, double size, MaterialSet replaceable, boolean applyGravity, public VanillaOre(BlockState material, double size, MaterialSet replaceable, boolean applyGravity,
double exposed, Map<BlockType, BlockState> materials) { double exposed, Map<BlockType, BlockState> materials, NoiseSampler sampler) {
this.material = material; this.material = material;
this.size = size; this.size = size;
this.replaceable = replaceable; this.replaceable = replaceable;
this.applyGravity = applyGravity; this.applyGravity = applyGravity;
this.exposed = exposed; this.exposed = exposed;
this.materials = materials; this.materials = materials;
this.sampler = sampler;
} }
@Override @Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation){
int centerX = location.getX(); int centerX = location.getX();
int centerZ = location.getZ(); int centerZ = location.getZ();
int centerY = location.getY(); int centerY = location.getY();
float f = random.nextFloat() * (float) Math.PI; double f = sampler.noise(centerX, centerY, centerZ, world.getSeed()) * (float) Math.PI;
double d1 = centerX + 8 + FastMath.sin(f) * size / 8.0F; double d1 = centerX + 8 + FastMath.sin(f) * size / 8.0F;
double d2 = centerX + 8 - FastMath.sin(f) * size / 8.0F; double d2 = centerX + 8 - FastMath.sin(f) * size / 8.0F;
double d3 = centerZ + 8 + FastMath.cos(f) * size / 8.0F; double d3 = centerZ + 8 + FastMath.cos(f) * size / 8.0F;
double d4 = centerZ + 8 - FastMath.cos(f) * size / 8.0F; double d4 = centerZ + 8 - FastMath.cos(f) * size / 8.0F;
double d5 = centerY + random.nextInt(3) - 2D; double d5 = centerY + (Math.round(sampler.noise(centerX, centerY, centerZ, world.getSeed() + 1) + 1)) - 2D;
double d6 = centerY + random.nextInt(3) - 2D; double d6 = centerY + (Math.round(sampler.noise(centerX, centerY, centerZ, world.getSeed() + 2) + 1)) - 2D;
for(int i = 0; i < size; i++) { for(int i = 0; i < size; i++) {
float iFactor = (float) i / (float) size; float iFactor = (float) i / (float) size;
double d10 = random.nextDouble() * size / 16.0D; double d10 = MathUtil.inverseLerp(sampler.noise(centerX, centerY, centerZ, world.getSeed() + 2 + (i * 2 - 1)), -1, 1) * size / 16.0D;
double d11 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0; double d11 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0;
double d12 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0; double d12 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0;
@@ -85,7 +90,7 @@ public class VanillaOre implements Structure {
if(y >= world.getMaxHeight() || y < world.getMinHeight()) continue; if(y >= world.getMaxHeight() || y < world.getMinHeight()) continue;
BlockType block = world.getBlockState(x, y, z).getBlockType(); BlockType block = world.getBlockState(x, y, z).getBlockType();
if((d13 * d13 + d14 * d14 + d15 * d15 < 1.0D) && getReplaceable().contains(block)) { if((d13 * d13 + d14 * d14 + d15 * d15 < 1.0D) && getReplaceable().contains(block)) {
if(exposed > random.nextDouble() || !(world.getBlockState(x, y, z - 1).isAir() || if(exposed > MathUtil.inverseLerp(sampler.noise(centerX, centerY, centerZ, world.getSeed() + 2 + (i * 2)), -1, 1) || !(world.getBlockState(x, y, z - 1).isAir() ||
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, y + 1, z).isAir() || world.getBlockState(x, y + 1, z).isAir() ||
@@ -10,3 +10,5 @@ website:
source: https://github.com/PolyhedralDev/Terra source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org docs: https://terra.polydev.org
license: MIT License license: MIT License
depends:
config-noise-function: "1.+"
@@ -1,13 +1,5 @@
version = version("1.0.0") version = version("1.0.0")
dependencies { dependencies {
api("com.googlecode.json-simple:json-simple:1.1.1")
compileOnlyApi(project(":common:addons:manifest-addon-loader")) 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.structure.lib.jafama")
} }
@@ -1,109 +0,0 @@
/*
* 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.structure.structures.loot;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.dfsek.terra.addons.structure.structures.loot.functions.AmountFunction;
import com.dfsek.terra.addons.structure.structures.loot.functions.DamageFunction;
import com.dfsek.terra.addons.structure.structures.loot.functions.EnchantFunction;
import com.dfsek.terra.addons.structure.structures.loot.functions.LootFunction;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.ItemStack;
/**
* Representation of a single item entry within a Loot Table pool.
*/
public class Entry {
private final Item item;
private final long weight;
private final List<LootFunction> functions = new ArrayList<>();
/**
* Instantiates an Entry from a JSON representation.
*
* @param entry The JSON Object to instantiate from.
*/
public Entry(JSONObject entry, Platform platform) {
String id = entry.get("name").toString();
this.item = platform.getItemHandle().createItem(id);
long weight1;
try {
weight1 = (long) entry.get("weight");
} catch(NullPointerException e) {
weight1 = 1;
}
this.weight = weight1;
if(entry.containsKey("functions")) {
for(Object function : (JSONArray) entry.get("functions")) {
switch(((String) ((JSONObject) function).get("function"))) {
case "minecraft:set_count", "set_count" -> {
Object loot = ((JSONObject) function).get("count");
long max, min;
if(loot instanceof Long) {
max = (Long) loot;
min = (Long) loot;
} else {
max = (long) ((JSONObject) loot).get("max");
min = (long) ((JSONObject) loot).get("min");
}
functions.add(new AmountFunction(FastMath.toIntExact(min), FastMath.toIntExact(max)));
}
case "minecraft:set_damage", "set_damage" -> {
long maxDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("max");
long minDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("min");
functions.add(new DamageFunction(FastMath.toIntExact(minDamage), FastMath.toIntExact(maxDamage)));
}
case "minecraft:enchant_with_levels", "enchant_with_levels" -> {
long maxEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("max");
long minEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("min");
JSONArray disabled = null;
if(((JSONObject) function).containsKey("disabled_enchants"))
disabled = (JSONArray) ((JSONObject) function).get("disabled_enchants");
functions.add(
new EnchantFunction(FastMath.toIntExact(minEnchant), FastMath.toIntExact(maxEnchant), disabled, platform));
}
}
}
}
}
/**
* Fetches a single ItemStack from the Entry, applying all functions to it.
*
* @param r The Random instance to apply functions with
*
* @return ItemStack - The ItemStack with all functions applied.
*/
public ItemStack getItem(Random r) {
ItemStack item = this.item.newItemStack(1);
for(LootFunction f : functions) {
item = f.apply(item, r);
}
return item;
}
/**
* Gets the weight attribute of the Entry.
*
* @return long - The weight of the Entry.
*/
public long getWeight() {
return this.weight;
}
}
@@ -1,80 +0,0 @@
/*
* 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.structure.structures.loot;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.Inventory;
import com.dfsek.terra.api.inventory.ItemStack;
/**
* Class representation of a Loot Table to populate chest loot.
*/
public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
private final List<Pool> pools = new ArrayList<>();
/**
* Instantiates a LootTable from a JSON String.
*
* @param json The JSON String representing the loot table.
*
* @throws ParseException if malformed JSON is passed.
*/
public LootTableImpl(String json, Platform platform) throws ParseException {
JSONParser jsonParser = new JSONParser();
Object tableJSON = jsonParser.parse(json);
JSONArray poolArray = (JSONArray) ((JSONObject) tableJSON).get("pools");
for(Object pool : poolArray) {
pools.add(new Pool((JSONObject) pool, platform));
}
}
@Override
public void fillInventory(Inventory i, Random r) {
List<ItemStack> loot = getLoot(r);
for(ItemStack stack : loot) {
int attempts = 0;
while(stack.getAmount() != 0 && attempts < 10) {
ItemStack newStack = stack.getType().newItemStack(stack.getAmount());
newStack.setItemMeta(stack.getItemMeta());
newStack.setAmount(1);
int slot = r.nextInt(i.getSize());
ItemStack slotItem = i.getItem(slot);
if(slotItem == null) {
i.setItem(slot, newStack);
stack.setAmount(stack.getAmount() - 1);
} else if(slotItem.getType().equals(newStack.getType())) {
ItemStack dep = newStack.getType().newItemStack(newStack.getAmount());
dep.setItemMeta(newStack.getItemMeta());
dep.setAmount(newStack.getAmount() + slotItem.getAmount());
i.setItem(slot, dep);
stack.setAmount(stack.getAmount() - 1);
}
attempts++;
}
}
}
@Override
public List<ItemStack> getLoot(Random r) {
List<ItemStack> itemList = new ArrayList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
}
return itemList;
}
}
@@ -1,69 +0,0 @@
/*
* 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.structure.structures.loot;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
/**
* Representation of a Loot Table pool, or a set of items to be fetched independently.
*/
public class Pool {
private final int max;
private final int min;
private final ProbabilityCollection<Entry> entries;
/**
* Instantiates a Pool from a JSON representation.
*
* @param pool The JSON Object to instantiate from.
*/
public Pool(JSONObject pool, Platform platform) {
entries = new ProbabilityCollection<>();
Object amount = pool.get("rolls");
if(amount instanceof Long) {
max = FastMath.toIntExact((Long) amount);
min = FastMath.toIntExact((Long) amount);
} else {
max = FastMath.toIntExact((Long) ((JSONObject) amount).get("max"));
min = FastMath.toIntExact((Long) ((JSONObject) amount).get("min"));
}
for(Object entryJSON : (JSONArray) pool.get("entries")) {
Entry entry = new Entry((JSONObject) entryJSON, platform);
entries.add(entry, FastMath.toIntExact(entry.getWeight()));
}
}
/**
* Fetches a list of items from the pool using the provided Random instance.
*
* @param r The Random instance to use.
*
* @return List&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new ArrayList<>();
for(int i = 0; i < rolls; i++) {
items.add(entries.get(r).getItem(r));
}
return items;
}
}
@@ -1,47 +0,0 @@
/*
* 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.structure.structures.loot.functions;
import java.util.Random;
import com.dfsek.terra.api.inventory.ItemStack;
/**
* Loot LootFunction fot setting the amount of an item.
*/
public class AmountFunction implements LootFunction {
private final int max;
private final int min;
/**
* Instantiates an AmountFunction.
*
* @param min Minimum amount.
* @param max Maximum amount.
*/
public AmountFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
original.setAmount(r.nextInt(max - min + 1) + min);
return original;
}
}
@@ -1,54 +0,0 @@
/*
* 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.structure.structures.loot.functions;
import java.util.Random;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Damageable;
import com.dfsek.terra.api.inventory.item.ItemMeta;
/**
* Loot LootFunction for setting the damage on items in Loot Tables
*/
public class DamageFunction implements LootFunction {
private final int max;
private final int min;
/**
* Instantiates a DamageFunction.
*
* @param min Minimum amount of damage (percentage, out of 100)
* @param max Maximum amount of damage (percentage, out of 100)
*/
public DamageFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
if(original == null) return null;
if(!original.isDamageable()) return original;
ItemMeta meta = original.getItemMeta();
double itemDurability = (r.nextDouble() * (max - min)) + min;
Damageable damage = (Damageable) meta;
damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability()));
original.setItemMeta((ItemMeta) damage);
return original;
}
}
@@ -1,82 +0,0 @@
/*
* 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.structure.structures.loot.functions;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Enchantment;
import com.dfsek.terra.api.inventory.item.ItemMeta;
public class EnchantFunction implements LootFunction {
private static final Logger LOGGER = LoggerFactory.getLogger(EnchantFunction.class);
private final int min;
private final int max;
private final JSONArray disabled;
private final Platform platform;
public EnchantFunction(int min, int max, JSONArray disabled, Platform platform) {
this.max = max;
this.min = min;
this.disabled = disabled;
this.platform = platform;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
if(original.getItemMeta() == null) return original;
double enchant = (r.nextDouble() * (max - min)) + min;
List<Enchantment> possible = new ArrayList<>();
for(Enchantment ench : platform.getItemHandle().getEnchantments()) {
if(ench.canEnchantItem(original) && (disabled == null || !this.disabled.contains(ench.getID()))) {
possible.add(ench);
}
}
int numEnchant = (r.nextInt((int) FastMath.abs(enchant)) / 10 + 1);
Collections.shuffle(possible);
ItemMeta meta = original.getItemMeta();
iter:
for(int i = 0; i < numEnchant && i < possible.size(); i++) {
Enchantment chosen = possible.get(i);
for(Enchantment ench : meta.getEnchantments().keySet()) {
if(chosen.conflictsWith(ench)) continue iter;
}
int lvl = r.nextInt(1 + (int) (((enchant / 40 > 1) ? 1 : enchant / 40) * (chosen.getMaxLevel())));
try {
meta.addEnchantment(chosen, FastMath.max(lvl, 1));
} catch(IllegalArgumentException e) {
LOGGER.warn(
"Attempted to enchant {} with {} at level {}, but an unexpected exception occurred! Usually this is caused by a " +
"misbehaving enchantment plugin.",
original.getType(), chosen, FastMath.max(lvl, 1));
}
}
original.setItemMeta(meta);
return original;
}
}
@@ -1,29 +0,0 @@
/*
* 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.structure.structures.loot.functions;
import java.util.Random;
import com.dfsek.terra.api.inventory.ItemStack;
/**
* Interface for mutating items in Loot Tables.
*/
public interface LootFunction {
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
*
* @return - ItemStack - The mutated ItemStack.
*/
ItemStack apply(ItemStack original, Random r);
}
@@ -8,7 +8,8 @@
package com.dfsek.terra.addons.generation.feature; package com.dfsek.terra.addons.generation.feature;
import java.util.Collections; import java.util.Collections;
import java.util.Random; import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures; import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -58,7 +59,6 @@ public class FeatureGenerationStage implements GenerationStage, StringIdentifiab
for(int subChunkZ = 0; subChunkZ < resolution; subChunkZ++) { for(int subChunkZ = 0; subChunkZ < resolution; subChunkZ++) {
int x = subChunkX + tx; int x = subChunkX + tx;
int z = subChunkZ + tz; int z = subChunkZ + tz;
long coordinateSeed = (seed * 31 + x) * 31 + z;
Column<WritableWorld> column = world.column(x, z); Column<WritableWorld> column = world.column(x, z);
biome.getContext() biome.getContext()
.get(biomeFeaturesKey) .get(biomeFeaturesKey)
@@ -72,7 +72,6 @@ public class FeatureGenerationStage implements GenerationStage, StringIdentifiab
.forEach(y -> feature.getStructure(world, x, y, z) .forEach(y -> feature.getStructure(world, x, y, z)
.generate(Vector3Int.of(x, y, z), .generate(Vector3Int.of(x, y, z),
world, world,
new Random(coordinateSeed * 31 + y),
Rotation.NONE) Rotation.NONE)
); );
} }
+1 -1
View File
@@ -1,6 +1,6 @@
version = version("1.0.0") version = version("1.0.0")
dependencies { dependencies {
implementation("com.dfsek.tectonic:yaml:${Versions.Libraries.tectonic}") implementation(libs.libraries.tectonic.yaml)
compileOnlyApi(project(":common:addons:manifest-addon-loader")) compileOnlyApi(project(":common:addons:manifest-addon-loader"))
} }
@@ -1,8 +1,8 @@
version = version("1.0.0") version = version("1.0.0")
dependencies { dependencies {
api("commons-io:commons-io:2.7") api(libs.libraries.internal.apache.io)
implementation("com.dfsek.tectonic:yaml:${Versions.Libraries.tectonic}") implementation(libs.libraries.tectonic.yaml)
} }
tasks.withType<Jar> { tasks.withType<Jar> {
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.palette.shortcut.block; package com.dfsek.terra.addons.palette.shortcut.block;
import java.util.Random; import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.structure.Structure; import com.dfsek.terra.api.structure.Structure;
@@ -17,7 +17,7 @@ public class SingletonStructure implements Structure {
} }
@Override @Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation) {
world.setBlockState(location, blockState); world.setBlockState(location, blockState);
return true; return true;
} }
@@ -1,6 +1,6 @@
package com.dfsek.terra.addons.structure.mutator; package com.dfsek.terra.addons.structure.mutator;
import java.util.Random; import java.util.random.RandomGenerator;
import com.dfsek.terra.api.registry.key.Keyed; import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.RegistryKey; import com.dfsek.terra.api.registry.key.RegistryKey;
@@ -32,13 +32,12 @@ public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
} }
@Override @Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation) {
return base.generate(location, return base.generate(location,
world world
.buffer() .buffer()
.read(readInterceptor) .read(readInterceptor)
.write(writeInterceptor) .write(writeInterceptor)
.build(), .build(), rotation);
random, rotation);
} }
} }
@@ -7,7 +7,7 @@
package com.dfsek.terra.addons.sponge; package com.dfsek.terra.addons.sponge;
import java.util.Random; import java.util.random.RandomGenerator;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.registry.key.Keyed; import com.dfsek.terra.api.registry.key.Keyed;
@@ -31,7 +31,7 @@ public class SpongeStructure implements Structure, Keyed<SpongeStructure> {
} }
@Override @Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation) {
int bX = location.getX(); int bX = location.getX();
int bY = location.getY(); int bY = location.getY();
int bZ = location.getZ(); int bZ = location.getZ();
@@ -1,15 +1,6 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
version = version("1.1.0") version = version("1.1.0")
dependencies { dependencies {
api("commons-io:commons-io:2.7")
compileOnlyApi(project(":common:addons:manifest-addon-loader")) compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama) api(libs.libraries.internal.apache.io)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<ShadowJar>("shadowJar") {
relocate("org.apache.commons", "com.dfsek.terra.addons.terrascript.lib.commons")
relocate("net.jafama", "com.dfsek.terra.addons.terrascript.lib.jafama")
} }
@@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Random; import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.Parser; import com.dfsek.terra.addons.terrascript.parser.Parser;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable; import com.dfsek.terra.addons.terrascript.parser.lang.Executable;
@@ -29,7 +29,6 @@ import com.dfsek.terra.addons.terrascript.script.builders.EntityFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.GetMarkFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.GetMarkFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.LootFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.LootFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.PullFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.PullFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.RandomFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.RecursionsFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.RecursionsFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.SetMarkFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.SetMarkFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.StateFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.StateFunctionBuilder;
@@ -77,7 +76,6 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("block", new BlockFunctionBuilder(platform)) .registerFunction("block", new BlockFunctionBuilder(platform))
.registerFunction("debugBlock", new BlockFunctionBuilder(platform)) .registerFunction("debugBlock", new BlockFunctionBuilder(platform))
.registerFunction("structure", new StructureFunctionBuilder(registry, platform)) .registerFunction("structure", new StructureFunctionBuilder(registry, platform))
.registerFunction("randomInt", new RandomFunctionBuilder())
.registerFunction("recursions", new RecursionsFunctionBuilder()) .registerFunction("recursions", new RecursionsFunctionBuilder())
.registerFunction("setMark", new SetMarkFunctionBuilder()) .registerFunction("setMark", new SetMarkFunctionBuilder())
.registerFunction("getMark", new GetMarkFunctionBuilder()) .registerFunction("getMark", new GetMarkFunctionBuilder())
@@ -130,16 +128,16 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
@Override @Override
@SuppressWarnings("try") @SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation) {
platform.getProfiler().push(profile); platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0)); boolean result = applyBlock(new TerraImplementationArguments(location, rotation, world, 0));
platform.getProfiler().pop(profile); platform.getProfiler().pop(profile);
return result; return result;
} }
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) { public boolean generate(Vector3Int location, WritableWorld world, Rotation rotation, int recursions) {
platform.getProfiler().push(profile); platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions)); boolean result = applyBlock(new TerraImplementationArguments(location, rotation, world, recursions));
platform.getProfiler().pop(profile); platform.getProfiler().pop(profile);
return result; return result;
} }
@@ -9,7 +9,7 @@ package com.dfsek.terra.addons.terrascript.script;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.random.RandomGenerator;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.api.util.Rotation; import com.dfsek.terra.api.util.Rotation;
@@ -20,16 +20,14 @@ import com.dfsek.terra.api.world.WritableWorld;
public class TerraImplementationArguments implements ImplementationArguments { public class TerraImplementationArguments implements ImplementationArguments {
private final Rotation rotation; private final Rotation rotation;
private final Random random;
private final WritableWorld world; private final WritableWorld world;
private final Map<Vector3, String> marks = new HashMap<>(); private final Map<Vector3, String> marks = new HashMap<>();
private final int recursions; private final int recursions;
private final Vector3Int origin; private final Vector3Int origin;
private boolean waterlog = false; private boolean waterlog = false;
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, Random random, WritableWorld world, int recursions) { public TerraImplementationArguments(Vector3Int origin, Rotation rotation, WritableWorld world, int recursions) {
this.rotation = rotation; this.rotation = rotation;
this.random = random;
this.world = world; this.world = world;
this.recursions = recursions; this.recursions = recursions;
this.origin = origin; this.origin = origin;
@@ -39,10 +37,6 @@ public class TerraImplementationArguments implements ImplementationArguments {
return recursions; return recursions;
} }
public Random getRandom() {
return random;
}
public Rotation getRotation() { public Rotation getRotation() {
return rotation; return rotation;
} }
@@ -1,35 +0,0 @@
/*
* 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.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.functions.RandomFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
@SuppressWarnings("unchecked")
@Override
public RandomFunction build(List<Returnable<?>> argumentList, Position position) {
return new RandomFunction((Returnable<Number>) argumentList.get(0), position);
}
@Override
public int argNumber() {
return 1;
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.NUMBER;
return null;
}
}
@@ -11,8 +11,6 @@ import net.jafama.FastMath;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Random;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
@@ -30,6 +28,9 @@ import com.dfsek.terra.api.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2; import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3; import com.dfsek.terra.api.util.vector.Vector3;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
public class LootFunction implements Function<Void> { public class LootFunction implements Function<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(LootFunction.class); private static final Logger LOGGER = LoggerFactory.getLogger(LootFunction.class);
@@ -83,8 +84,7 @@ public class LootFunction implements Function<Void> {
platform.getEventManager().callEvent(event); platform.getEventManager().callEvent(event);
if(event.isCancelled()) return; if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(), event.getTable().fillInventory(container.getInventory());
new Random(apply.hashCode()));
data.update(false); data.update(false);
} catch(Exception e) { } catch(Exception e) {
LOGGER.error("Could not apply loot at {}", apply, e); LOGGER.error("Could not apply loot at {}", apply, e);
@@ -1,43 +0,0 @@
/*
* 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.terrascript.script.functions;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunction implements Function<Integer> {
private final Returnable<Number> numberReturnable;
private final Position position;
public RandomFunction(Returnable<Number> numberReturnable, Position position) {
this.numberReturnable = numberReturnable;
this.position = position;
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@Override
public Integer apply(ImplementationArguments implementationArguments, Scope scope) {
return ((TerraImplementationArguments) implementationArguments).getRandom().nextInt(
numberReturnable.apply(implementationArguments, scope).intValue());
}
@Override
public Position getPosition() {
return position;
}
}
@@ -67,31 +67,20 @@ public class StructureFunction implements Function<Boolean> {
String app = id.apply(implementationArguments, scope); String app = id.apply(implementationArguments, scope);
return registry.getByID(app).map(script -> { return registry.getByID(app).map(script -> {
Rotation rotation1;
String rotString = rotations.get(arguments.getRandom().nextInt(rotations.size())).apply(implementationArguments, scope);
try {
rotation1 = Rotation.valueOf(rotString);
} catch(IllegalArgumentException e) {
LOGGER.warn("Invalid rotation {}", rotString);
return false;
}
if(script instanceof StructureScript structureScript) { if(script instanceof StructureScript structureScript) {
return structureScript.generate(arguments.getOrigin(), return structureScript.generate(arguments.getOrigin(),
arguments.getWorld() arguments.getWorld()
.buffer(FastMath.roundToInt(xz.getX()), .buffer(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(), y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())), FastMath.roundToInt(xz.getZ())),
arguments.getRandom(), arguments.getRotation(), arguments.getRecursions() + 1);
arguments.getRotation().rotate(rotation1), arguments.getRecursions() + 1);
} }
return script.generate(arguments.getOrigin(), return script.generate(arguments.getOrigin(),
arguments.getWorld() arguments.getWorld()
.buffer(FastMath.roundToInt(xz.getX()), .buffer(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(), y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())), FastMath.roundToInt(xz.getZ())),
arguments.getRandom(), arguments.getRotation());
arguments.getRotation().rotate(rotation1));
}).orElseGet(() -> { }).orElseGet(() -> {
LOGGER.error("No such structure {}", app); LOGGER.error("No such structure {}", app);
return false; return false;
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff. Show more