Compare commits

..

2 Commits

Author SHA1 Message Date
dfsek 3f48b616fe clean up CellularSampler 2022-04-24 23:14:17 -07:00
dfsek cef185b07d create noise JMH testing stuff 2022-04-24 22:51:55 -07:00
630 changed files with 9519 additions and 12282 deletions
+2 -2
View File
@@ -21,8 +21,8 @@ assignees: ""
- You must be on the LATEST version of Terra to receive any support. There is no support for older versions of Terra.
- Make sure that this is not a *specific* compatibility issue with another terrain generation mod.
Do not request *specific* compatibility with mods or plugins (e.g. "Compatibility with TechCraft v7").
That should be implemented in an addon, **not** in the main project.
*General* compatibility (e.g. "Ability to pull Vanilla/Modded features from parent biomes") will be considered in the main project.
That should be implemented in an addon, **not** in the platform project.
*General* compatibility (e.g. "Ability to pull Vanilla/Modded features from parent biomes") will be considered in the platform project.
- Make sure that there are no already existing issues open with your problem. If you open a duplicate, it will be closed as such.
- Make sure that it is actually Terra causing the issue, and not another mod/plugin.
You can do this by testing to see if you can recreate the issue without Terra installed.
+9 -5
View File
@@ -72,15 +72,19 @@
- [ ] Bug Fix <!-- Anything which fixes an issue in Terra. -->
- [ ] Build system <!-- Anything which pretain to the build system. -->
- [ ] Documentation <!-- Anything which adds or improves documentation for existing features. -->
- [ ]
Documentation <!-- Anything which adds or improves documentation for existing features. -->
- [ ] New Feature <!-- Anything which adds new functionality to Terra. -->
- [ ] Performance <!-- Anything which is imrpoves the performance of Terra. -->
- [ ] Refactoring <!-- Anything which does not add any new code, only moves code around. -->
- [ ] Repository <!-- Anything which affects the repository. Eg. changes to the `README.md` file. -->
- [ ]
Refactoring <!-- Anything which does not add any new code, only moves code around. -->
- [ ]
Repository <!-- Anything which affects the repository. Eg. changes to the `README.md` file. -->
- [ ] Revert <!-- Anything which reverts previous commits. -->
- [ ] Style <!-- Anything which updates style. -->
- [ ] Tests <!-- Anything which adds or updates tests. -->
- [ ] Translation <!-- Anything which is internationalizing the Terra program to other languages. -->
- [ ]
Translation <!-- Anything which is internationalizing the Terra program to other languages. -->
#### Compatiblity
@@ -115,4 +119,4 @@
<!--
If it only introduces small changes, you don't need to add tests.
But if you add big changes, you should probably at least write *some* testing, where applicable.
-->
-->
+1 -4
View File
@@ -245,7 +245,4 @@ nbdist/
/run/
**/testDir/
platforms/**/run/**
**/testDir/
+2 -5
View File
@@ -63,8 +63,7 @@ to [Terra global moderation team](CODE_OF_CONDUCT.md#Reporting).
## I don't want to read this whole thing I just have a question!!!
> **Note:** Please don't file an issue to ask a question. You'll get faster
> results by using the resources below.
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below.
We have an official discord server where you can request help from various users
@@ -104,9 +103,7 @@ you don't need to create one. When you are creating a bug report,
please [include as many details as possible](#how-do-i-submit-a-good-bug-report)
.
> **Note:** If you find a **Closed** issue that seems like it is the same thing
> that you're experiencing, open a new issue and include a link to the original
> issue in the body of your new one.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before Submitting A Bug Report
+6 -10
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.2.0"))
versionProjects(":common:implementation", version("6.2.0"))
versionProjects(":platforms", version("6.2.0"))
versionProjects(":common:api", version("6.0.0"))
versionProjects(":common:implementation", version("6.0.0"))
versionProjects(":platforms", version("6.0.0"))
allprojects {
@@ -39,18 +39,14 @@ allprojects {
}
afterEvaluate {
forImmediateSubProjects(":platforms") {
forSubProjects(":platforms") {
configureDistribution()
}
forSubProjects(":common:addons") {
apply(plugin = "com.github.johnrengelman.shadow")
tasks.named("build") {
finalizedBy(tasks.named("shadowJar"))
}
dependencies {
"compileOnly"(project(":common:api"))
"shadedImplementation"("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
"testImplementation"("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
"testImplementation"(project(":common:api"))
}
}
+6 -10
View File
@@ -6,17 +6,13 @@ plugins {
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://repo.codemc.org/repository/maven-public") {
name = "CodeMC"
}
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
}
dependencies {
implementation(libs.libraries.internal.shadow)
implementation(libs.libraries.internal.asm)
implementation(libs.libraries.internal.asm.tree)
implementation(libs.libraries.tectonic)
implementation(libs.libraries.snakeyaml)
implementation("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:+")
implementation("org.ow2.asm:asm:9.2")
implementation("org.ow2.asm:asm-tree:9.2")
implementation("com.dfsek.tectonic:common:4.2.0")
implementation("org.yaml:snakeyaml:1.27")
}
-7
View File
@@ -1,7 +0,0 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
+24 -25
View File
@@ -3,6 +3,7 @@ import java.io.File
import java.util.function.Predicate
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.extra
import kotlin.streams.asStream
@@ -11,33 +12,31 @@ import kotlin.streams.asStream
* Configures a directory where addons will be put.
*/
fun Project.addonDir(dir: File, task: Task) {
val moveAddons = tasks.register("moveAddons" + task.name) {
dependsOn("compileAddons")
doLast {
dir.parentFile.mkdirs()
matchingAddons(dir) {
it.name.startsWith("Terra-") // Assume everything that starts with Terra- is a core addon.
}.forEach {
println("Deleting old addon: " + it.absolutePath)
it.delete()
}
forSubProjects(":common:addons") {
val jar = tasks.named("shadowJar").get() as ShadowJar
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val target = File(dir, boot + jar.archiveFileName.get())
val base = "${jar.archiveBaseName.get()}-${version}"
println("Copying addon ${jar.archiveFileName.get()} to ${target.absolutePath}. Base name: $base")
jar.archiveFile.orNull?.asFile?.copyTo(target)
}
task.doFirst {
dir.parentFile.mkdirs()
matchingAddons(dir) {
it.name.startsWith("Terra-") // Assume everything that starts with Terra- is a core addon.
}.forEach {
println("Deleting old addon: " + it.absolutePath)
it.delete()
}
forSubProjects(":common:addons") {
val jar = if (tasks.findByName("shadowJar") != null) {
(tasks.named("shadowJar").get() as ShadowJar)
} else {
(tasks.named("jar").get() as Jar)
}
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val target = File(dir, boot + jar.archiveFileName.get())
val base = "${jar.archiveBaseName.get()}-${version}"
println("Copying addon ${jar.archiveFileName.get()} to ${target.absolutePath}. Base name: $base")
jar.archiveFile.orNull?.asFile?.copyTo(target)
}
}
task.dependsOn(moveAddons)
}
fun matchingAddons(dir: File, matcher: Predicate<File>): Set<File> {
+11 -35
View File
@@ -3,22 +3,16 @@ import org.gradle.kotlin.dsl.creating
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.getting
import org.gradle.kotlin.dsl.maven
import org.gradle.kotlin.dsl.repositories
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.kotlin.dsl.apply
fun Project.configureDependencies() {
val api by configurations.getting
val implementation by configurations.getting
val testImplementation by configurations.getting
val compileOnly by configurations.getting
val api by configurations.getting
val implementation by configurations.getting
val shaded by configurations.creating
val libs = rootProject.project.versionCatalogs.libs
@Suppress("UNUSED_VARIABLE")
val shadedApi by configurations.creating {
shaded.extendsFrom(this)
@@ -34,35 +28,17 @@ fun Project.configureDependencies() {
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.fabricmc.net/") {
name = "FabricMC"
}
maven("https://repo.codemc.org/repository/maven-public") {
name = "CodeMC"
}
maven("https://papermc.io/repo/repository/maven-public/") {
name = "PaperMC"
}
maven("https://files.minecraftforge.net/maven/") {
name = "Forge"
}
maven("https://maven.quiltmc.org/repository/release/") {
name = "Quilt"
}
maven("https://jitpack.io") {
name = "JitPack"
}
maven("https://api.modrinth.com/maven") {
name = "Modrinth"
}
maven { url = uri("https://maven.fabricmc.net/") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
}
dependencies {
testImplementation(libs.findLibrary("libraries.internal.junit.jupiter.api").get())
testImplementation(libs.findLibrary("libraries.internal.junit.jupiter.engine").get())
compileOnly(libs.findLibrary("libraries.internal.jetbrains.annotations").get())
compileOnly(libs.findLibrary("libraries.guava").get())
testImplementation(libs.findLibrary("libraries.guava").get())
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.7.0")
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("com.google.guava:guava:30.0-jre")
testImplementation("com.google.guava:guava:30.0-jre")
}
}
+44 -41
View File
@@ -1,13 +1,15 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileWriter
import java.net.URL
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.plugins.BasePluginExtension
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.extra
@@ -16,7 +18,6 @@ import org.gradle.kotlin.dsl.named
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.Yaml
fun Project.configureDistribution() {
apply(plugin = "com.github.johnrengelman.shadow")
@@ -29,44 +30,50 @@ fun Project.configureDistribution() {
}
}
val compileAddons = tasks.create("compileAddons") {
val installAddons = tasks.create("installAddons") {
group = "terra"
forSubProjects(":common:addons") {
afterEvaluate {
dependsOn(getJarTask())
}
}
}
val installAddons = tasks.create("installAddons") {
group = "terra"
dependsOn(compileAddons)
doLast {
// The addons are copied into a JAR because of a ShadowJar bug
// which expands *all* JARs, even resource ones, into the fat JAR.
// To get around this, we copy all addon JARs into a *new* JAR,
// then, ShadowJar expands the newly created JAR, putting the original
// JARs where they should go.
//
// https://github.com/johnrengelman/shadow/issues/111
val dest = tasks.named<ShadowJar>("shadowJar").get().archiveFile.get().asFile.toPath()
val dest = File(buildDir, "/resources/main/addons.jar")
dest.parentFile.mkdirs()
FileSystems.newFileSystem(dest, mapOf("create" to "false"), null).use { fs ->
forSubProjects(":common:addons") {
val jar = getJarTask()
println("Packaging addon ${jar.archiveFileName.get()} to $dest. size: ${jar.archiveFile.get().asFile.length() / 1024}KB")
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val addonPath = fs.getPath("/addons/$boot${jar.archiveFileName.get()}")
if (!Files.exists(addonPath)) {
Files.createDirectories(addonPath.parent)
Files.createFile(addonPath)
Files.copy(jar.archiveFile.get().asFile.toPath(), addonPath, StandardCopyOption.REPLACE_EXISTING)
}
val zip = ZipOutputStream(FileOutputStream(dest))
forSubProjects(":common:addons") {
val jar = getJarTask()
println("Packaging addon ${jar.archiveFileName.get()} to ${dest.absolutePath}. size: ${jar.archiveFile.get().asFile.length() / 1024}KB")
val boot = if (extra.has("bootstrap") && extra.get("bootstrap") as Boolean) "bootstrap/" else ""
val entry = ZipEntry("addons/$boot${jar.archiveFileName.get()}")
zip.putNextEntry(entry)
FileInputStream(jar.archiveFile.get().asFile).run {
copyTo(zip)
close()
}
zip.closeEntry()
}
zip.close()
}
}
val generateResourceManifest = tasks.create("generateResourceManifest") {
group = "terra"
dependsOn(downloadDefaultPacks)
dependsOn(installAddons)
doLast {
val resources = HashMap<String, MutableList<String>>()
val packsDir = File("${project.buildDir}/resources/main/packs/")
@@ -104,35 +111,25 @@ fun Project.configureDistribution() {
val manifest = File("${project.buildDir}/resources/main/resources.yml")
if (manifest.exists()) manifest.delete()
manifest.parentFile.mkdirs()
manifest.createNewFile()
FileWriter(manifest).use {
yaml.dump(resources, it)
}
yaml.dump(resources, FileWriter(manifest))
}
}
tasks.named("processResources") {
generateResourceManifest.mustRunAfter(downloadDefaultPacks)
finalizedBy(downloadDefaultPacks)
finalizedBy(generateResourceManifest)
}
tasks["processResources"].dependsOn(generateResourceManifest)
tasks.named<ShadowJar>("shadowJar") {
// Tell shadow to download the packs
dependsOn(downloadDefaultPacks)
configurations = listOf(project.configurations["shaded"])
archiveClassifier.set("shaded")
setVersion(project.version)
relocate("org.apache.commons", "com.dfsek.terra.lib.commons")
relocate("org.objectweb.asm", "com.dfsek.terra.lib.asm")
relocate("com.dfsek.paralithic", "com.dfsek.terra.lib.paralithic")
relocate("org.json", "com.dfsek.terra.lib.json")
relocate("org.yaml", "com.dfsek.terra.lib.yaml")
finalizedBy(installAddons)
}
configure<BasePluginExtension> {
@@ -151,4 +148,10 @@ fun downloadPack(packUrl: URL, project: Project) {
file.outputStream().write(packUrl.readBytes())
}
fun Project.getJarTask() = tasks.named("shadowJar").get() as ShadowJar
fun Project.getJarTask(): Jar {
return if (tasks.findByName("shadowJar") != null) {
(tasks.named("shadowJar").get() as ShadowJar)
} else {
(tasks.named("jar").get() as Jar)
}
}
-12
View File
@@ -1,12 +0,0 @@
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")
-6
View File
@@ -29,12 +29,6 @@ fun Project.forSubProjects(project: String, action: Action<Project>) {
}
}
fun Project.forImmediateSubProjects(project: String, action: Action<Project>) {
project(project).childProjects.forEach {
action.execute(it.value)
}
}
fun preRelease(preRelease: Boolean) {
isPrerelease = preRelease
}
+39
View File
@@ -0,0 +1,39 @@
object Versions {
object Libraries {
const val tectonic = "4.2.0"
const val paralithic = "0.6.0"
const val strata = "1.1.1"
const val cloud = "1.6.2"
const val slf4j = "1.7.36"
const val log4j_slf4j_impl = "2.14.1"
object Internal {
const val apacheText = "1.9"
const val jafama = "2.3.2"
const val apacheIO = "2.6"
const val asm = "9.2"
const val fastutil = "8.5.6"
}
}
object Fabric {
const val fabricLoader = "0.14.2"
const val fabricAPI = "0.51.1+1.18.2"
const val minecraft = "1.18.2"
const val yarn = "$minecraft+build.3"
const val permissionsAPI = "0.1-SNAPSHOT"
}
object Bukkit {
const val paper = "1.18-R0.1-SNAPSHOT"
const val paperLib = "1.0.5"
}
object Sponge {
const val sponge = "9.0.0-SNAPSHOT"
const val mixin = "0.8.2"
const val minecraft = "1.17.1"
}
}
@@ -1,26 +0,0 @@
package com.dfsek.terra.addon.loader;
import ca.solostudios.strata.version.Version;
import com.dfsek.terra.api.addon.BaseAddon;
public class ApiAddon implements BaseAddon {
private final Version version;
private final String id;
public ApiAddon(Version version, String id) {
this.version = version;
this.id = id;
}
@Override
public Version getVersion() {
return version;
}
@Override
public String getID() {
return id;
}
}
@@ -1,22 +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.addon.loader;
import java.net.URL;
import java.net.URLClassLoader;
public class ApiAddonClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public ApiAddonClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
}
@@ -14,7 +14,6 @@ import java.nio.file.Path;
import java.util.Collections;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.addon.bootstrap.BootstrapAddonClassLoader;
import com.dfsek.terra.api.addon.bootstrap.BootstrapBaseAddon;
@@ -22,8 +21,7 @@ public class ApiAddonLoader implements BootstrapBaseAddon<BaseAddon> {
private static final Version VERSION = Versions.getVersion(1, 0, 0);
@Override
public Iterable<BaseAddon> loadAddons(Path addonsFolder, BootstrapAddonClassLoader parent) {
public Iterable<BaseAddon> loadAddons(Path addonsFolder, ClassLoader parent) {
return Collections.emptySet();
}
@@ -1,6 +0,0 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
compileOnlyApi(project(":common:addons:biome-query-api"))
}
@@ -1,51 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.world.biome.Biome;
class BaseBiomeColumn implements Column<Biome> {
private final BiomeExtrusionProvider biomeProvider;
private final Biome base;
private final int min;
private final int max;
private final int x;
private final int z;
private final long seed;
protected BaseBiomeColumn(BiomeExtrusionProvider biomeProvider, Biome base, int min, int max, int x, int z, long seed) {
this.biomeProvider = biomeProvider;
this.base = base;
this.min = min;
this.max = max;
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public int getMinY() {
return min;
}
@Override
public int getMaxY() {
return max;
}
@Override
public int getX() {
return x;
}
@Override
public int getZ() {
return z;
}
@Override
public Biome get(int y) {
return biomeProvider.extrude(base, x, y, z, seed);
}
}
@@ -1,69 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.addons.biome.extrusion.config.BiomeExtrusionTemplate;
import com.dfsek.terra.addons.biome.extrusion.config.ReplaceableBiomeLoader;
import com.dfsek.terra.addons.biome.extrusion.config.extrusions.ReplaceExtrusionTemplate;
import com.dfsek.terra.addons.biome.extrusion.config.extrusions.SetExtrusionTemplate;
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.Get;
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.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.util.generic.Construct;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import org.jetbrains.annotations.NotNull;
public class BiomeExtrusionAddon implements MonadAddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<Extrusion>>> EXTRUSION_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@Override
public @NotNull Monad<?, Init<?>> initialize() {
return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(),
Get.platform(),
((functionalEventHandler, base, platform) -> Init.ofPure(Construct.construct(() -> {
functionalEventHandler.register(base, ConfigPackPostLoadEvent.class)
.then(event -> {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(ReplaceableBiome.class,
new ReplaceableBiomeLoader(biomeRegistry));
});
return functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> {
Registry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry =
event.getPack()
.createRegistry(PROVIDER_REGISTRY_KEY);
providerRegistry.register(base.key("EXTRUSION"), BiomeExtrusionTemplate::new);
})
.then(event -> {
Registry<Supplier<ObjectTemplate<Extrusion>>> extrusionRegistry =
event.getPack().createRegistry(
EXTRUSION_REGISTRY_KEY);
extrusionRegistry.register(base.key("SET"), SetExtrusionTemplate::new);
extrusionRegistry.register(base.key("REPLACE"), ReplaceExtrusionTemplate::new);
})
.failThrough();
}))
));
}
}
@@ -1,67 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomeExtrusionProvider implements BiomeProvider {
private final BiomeProvider delegate;
private final Set<Biome> biomes;
private final List<Extrusion> extrusions;
private final int resolution;
public BiomeExtrusionProvider(BiomeProvider delegate, List<Extrusion> extrusions, int resolution) {
this.delegate = delegate;
this.biomes = delegate.stream().collect(Collectors.toSet());
extrusions.forEach(e -> biomes.addAll(e.getBiomes()));
this.extrusions = extrusions;
this.resolution = resolution;
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
Biome delegated = delegate.getBiome(x, y, z, seed);
return extrude(delegated, x, y, z, seed);
}
public Biome extrude(Biome original, int x, int y, int z, long seed) {
for(Extrusion extrusion : extrusions) {
original = extrusion.extrude(original, x, y, z, seed);
}
return original;
}
@Override
public Column<Biome> getColumn(int x, int z, long seed, int min, int max) {
return delegate.getBaseBiome(x, z, seed)
.map(base -> (Column<Biome>) new BaseBiomeColumn(this, base, min, max, x, z, seed))
.orElseGet(() -> BiomeProvider.super.getColumn(x, z, seed, min, max));
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return delegate.getBaseBiome(x, z, seed);
}
@Override
public Iterable<Biome> getBiomes() {
return biomes;
}
@Override
public int resolution() {
return resolution;
}
public BiomeProvider getDelegate() {
return delegate;
}
}
@@ -1,12 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.api;
import java.util.Collection;
import com.dfsek.terra.api.world.biome.Biome;
public interface Extrusion {
Biome extrude(Biome original, int x, int y, int z, long seed);
Collection<Biome> getBiomes();
}
@@ -1,23 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.api;
import com.dfsek.terra.api.world.biome.Biome;
final class PresentBiome implements ReplaceableBiome {
private final Biome biome;
PresentBiome(Biome biome) {
this.biome = biome;
}
@Override
public Biome get(Biome existing) {
return biome;
}
@Override
public boolean isSelf() {
return false;
}
}
@@ -1,31 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.api;
import java.util.Optional;
import com.dfsek.terra.api.world.biome.Biome;
/**
* Basically just a specialised implementation of {@link Optional} for biomes where a biome may be a "self" reference.
*/
public sealed interface ReplaceableBiome permits PresentBiome, SelfBiome {
static ReplaceableBiome of(Biome biome) {
return new PresentBiome(biome);
}
static ReplaceableBiome self() {
return SelfBiome.INSTANCE;
}
Biome get(Biome existing);
default Biome get() {
if(isSelf()) {
throw new IllegalStateException("Cannot get() self biome!");
}
return get(null);
}
boolean isSelf();
}
@@ -1,21 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.api;
import java.util.Objects;
import com.dfsek.terra.api.world.biome.Biome;
final class SelfBiome implements ReplaceableBiome {
public static final SelfBiome INSTANCE = new SelfBiome();
@Override
public Biome get(Biome existing) {
return Objects.requireNonNull(existing);
}
@Override
public boolean isSelf() {
return true;
}
}
@@ -1,30 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.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 java.util.List;
import com.dfsek.terra.addons.biome.extrusion.BiomeExtrusionProvider;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomeExtrusionTemplate implements ObjectTemplate<BiomeProvider> {
@Value("provider")
private @Meta BiomeProvider provider;
@Value("resolution")
@Default
private @Meta int resolution = 4;
@Value("extrusions")
private @Meta List<@Meta Extrusion> extrusions;
@Override
public BiomeProvider get() {
return new BiomeExtrusionProvider(provider, extrusions, resolution);
}
}
@@ -1,32 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.config;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.world.biome.Biome;
public class ReplaceableBiomeLoader implements TypeLoader<ReplaceableBiome> {
private final Registry<Biome> biomeRegistry;
public ReplaceableBiomeLoader(Registry<Biome> biomeRegistry) {
this.biomeRegistry = biomeRegistry;
}
@Override
public ReplaceableBiome load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
throws LoadException {
if(c.equals("SELF")) return ReplaceableBiome.self();
return biomeRegistry
.getByID((String) c)
.map(ReplaceableBiome::of)
.orElseThrow(() -> new LoadException("No such biome: " + c, depthTracker));
}
}
@@ -1,23 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.config.extrusions;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.addons.biome.extrusion.extrusions.ReplaceExtrusion;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class ReplaceExtrusionTemplate extends SamplerExtrusionTemplate {
@Value("to")
private @Meta ProbabilityCollection<@Meta ReplaceableBiome> biomes;
@Value("from")
private @Meta String fromTag;
@Override
public Extrusion get() {
return new ReplaceExtrusion(sampler, range, biomes, fromTag);
}
}
@@ -1,18 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.config.extrusions;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.Range;
public abstract class SamplerExtrusionTemplate implements ObjectTemplate<Extrusion> {
@Value("sampler")
protected @Meta NoiseSampler sampler;
@Value("range")
protected @Meta Range range;
}
@@ -1,20 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.config.extrusions;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.addons.biome.extrusion.extrusions.SetExtrusion;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class SetExtrusionTemplate extends SamplerExtrusionTemplate {
@Value("to")
private @Meta ProbabilityCollection<@Meta ReplaceableBiome> biomes;
@Override
public Extrusion get() {
return new SetExtrusion(sampler, range, biomes);
}
}
@@ -1,52 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.extrusions;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.addons.biome.query.api.BiomeQueries;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.Range;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.world.biome.Biome;
/**
* Sets biomes at locations based on a sampler.
*/
public class ReplaceExtrusion implements Extrusion {
private final NoiseSampler sampler;
private final Range range;
private final ProbabilityCollection<ReplaceableBiome> biomes;
private final Predicate<Biome> hasTag;
public ReplaceExtrusion(NoiseSampler sampler, Range range, ProbabilityCollection<ReplaceableBiome> biomes, String tag) {
this.sampler = sampler;
this.range = range;
this.biomes = biomes;
this.hasTag = BiomeQueries.has(tag);
}
@Override
public Biome extrude(Biome original, int x, int y, int z, long seed) {
if(hasTag.test(original)) {
return range.ifInRange(y, () -> biomes.get(sampler, x, y, z, seed).get(original), original);
}
return original;
}
@Override
public Collection<Biome> getBiomes() {
return biomes
.getContents()
.stream()
.filter(Predicate.not(ReplaceableBiome::isSelf))
.map(ReplaceableBiome::get)
.collect(Collectors.toSet());
}
}
@@ -1,45 +0,0 @@
package com.dfsek.terra.addons.biome.extrusion.extrusions;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.dfsek.terra.addons.biome.extrusion.api.Extrusion;
import com.dfsek.terra.addons.biome.extrusion.api.ReplaceableBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.Range;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.world.biome.Biome;
/**
* Sets biomes at locations based on a sampler.
*/
public class SetExtrusion implements Extrusion {
private final NoiseSampler sampler;
private final Range range;
private final ProbabilityCollection<ReplaceableBiome> biomes;
public SetExtrusion(NoiseSampler sampler, Range range, ProbabilityCollection<ReplaceableBiome> biomes) {
this.sampler = sampler;
this.range = range;
this.biomes = biomes;
}
@Override
public Biome extrude(Biome original, int x, int y, int z, long seed) {
return range.ifInRange(y, () -> biomes.get(sampler, x, y, z, seed).get(original), original);
}
@Override
public Collection<Biome> getBiomes() {
return biomes
.getContents()
.stream()
.filter(Predicate.not(ReplaceableBiome::isSelf))
.map(ReplaceableBiome::get)
.collect(Collectors.toSet());
}
}
@@ -1,14 +0,0 @@
schema-version: 1
contributors:
- Terra contributors
id: biome-provider-extrusion
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.extrusion.BiomeExtrusionAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
depends:
biome-query-api: "1.+"
@@ -1,5 +1,5 @@
version = version("1.0.0")
version = version("0.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
}
shadedApi(project(":common:addons:manifest-addon-loader"))
}
@@ -13,7 +13,6 @@ import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
@@ -38,11 +37,7 @@ public class ImageBiomeProvider implements BiomeProvider {
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return getBiome(x, z);
}
public Biome getBiome(int x, int z) {
public Biome getBiome(int x, int z, long seed) {
x /= resolution;
z /= resolution;
Color color = align.getColor(image, x, z);
@@ -56,11 +51,6 @@ public class ImageBiomeProvider implements BiomeProvider {
}));
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return Optional.of(getBiome(x, z));
}
@Override
public Iterable<Biome> getBiomes() {
return colorBiomeMap.values();
@@ -11,41 +11,39 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
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.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import org.jetbrains.annotations.NotNull;
public class ImageBiomeProviderAddon implements MonadAddonInitializer {
public class ImageBiomeProviderAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@Override
public @NotNull Monad<?, Init<?>> initialize() {
return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(),
Get.platform(),
((functionalEventHandler, base, platform) -> Init.ofPure(
functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> {
Registry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().createRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(base.key("IMAGE"),
() -> new ImageProviderTemplate(event.getPack().getRegistry(Biome.class)));
})
.failThrough()))
);
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(addon.key("IMAGE"),
() -> new ImageProviderTemplate(event.getPack().getRegistry(Biome.class)));
})
.failThrough();
}
}
@@ -6,7 +6,7 @@ version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.image.ImageBiomeProviderAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
issues: https://github.com/PolyhedralDev/Terra-biome-provider-image/issues
source: https://github.com/PolyhedralDev/Terra-biome-provider-image
docs: https://github.com/PolyhedralDev/Terra/wiki
license: GNU LGPL v3.0
@@ -1,5 +1,5 @@
version = version("1.0.1")
version = version("0.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
}
shadedApi(project(":common:addons:manifest-addon-loader"))
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
import com.dfsek.terra.api.util.vector.Vector2;
public class BiomeHolderImpl implements BiomeHolder {
private final Vector2.Mutable origin;
private final int width;
private final int offset;
private BiomeDelegate[][] biomes;
public BiomeHolderImpl(int width, Vector2.Mutable origin) {
width += 4;
this.width = width;
biomes = new BiomeDelegate[width][width];
this.origin = origin;
this.offset = 2;
}
private BiomeHolderImpl(BiomeDelegate[][] biomes, Vector2.Mutable origin, int width, int offset) {
this.biomes = biomes;
this.origin = origin;
this.width = width;
this.offset = 2 * offset;
}
@Override
public BiomeHolder expand(BiomeExpander expander, long seed) {
BiomeDelegate[][] old = biomes;
int newWidth = width * 2 - 1;
biomes = new BiomeDelegate[newWidth][newWidth];
for(int x = 0; x < width; x++) {
for(int z = 0; z < width; z++) {
biomes[x * 2][z * 2] = old[x][z];
if(z != width - 1)
biomes[x * 2][z * 2 + 1] = expander.getBetween(x + origin.getX(), z + 1 + origin.getZ(), seed, old[x][z],
old[x][z + 1]);
if(x != width - 1)
biomes[x * 2 + 1][z * 2] = expander.getBetween(x + 1 + origin.getX(), z + origin.getZ(), seed, old[x][z],
old[x + 1][z]);
if(x != width - 1 && z != width - 1)
biomes[x * 2 + 1][z * 2 + 1] = expander.getBetween(x + 1 + origin.getX(), z + 1 + origin.getZ(), seed, old[x][z],
old[x + 1][z + 1], old[x][z + 1], old[x + 1][z]);
}
}
return new BiomeHolderImpl(biomes, origin.setX(origin.getX() * 2 - 1).setZ(origin.getZ() * 2 - 1), newWidth, offset);
}
@Override
public void mutate(BiomeMutator mutator, long seed) {
for(int x = 0; x < width; x++) {
for(int z = 0; z < width; z++) {
BiomeMutator.ViewPoint viewPoint = new BiomeMutator.ViewPoint(this, x, z);
biomes[x][z] = mutator.mutate(viewPoint, x + origin.getX(), z + origin.getZ(), seed);
}
}
}
@Override
public void fill(BiomeSource source, long seed) {
for(int x = 0; x < width; x++) {
for(int z = 0; z < width; z++) {
biomes[x][z] = source.getBiome(origin.getX() + x, origin.getZ() + z, seed);
}
}
}
@Override
public BiomeDelegate getBiome(int x, int z) {
x += offset;
z += offset;
return getBiomeRaw(x, z);
}
@Override
public BiomeDelegate getBiomeRaw(int x, int z) {
if(x >= width || z >= width || x < 0 || z < 0) return null;
return biomes[x][z];
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
import com.dfsek.terra.api.util.vector.Vector2;
public class BiomePipeline {
private final BiomeSource source;
private final List<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,84 +11,79 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.config.BiomeDelegateLoader;
import com.dfsek.terra.addons.biome.pipeline.config.BiomePipelineTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.PipelineBiomeLoader;
import com.dfsek.terra.addons.biome.pipeline.config.source.SamplerSourceTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.SamplerSourceTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.expander.ExpanderStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.SmoothStageTemplate;
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.monad.Do;
import com.dfsek.terra.addons.manifest.api.monad.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderListMutatorTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.BorderMutatorTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceListMutatorTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.ReplaceMutatorTemplate;
import com.dfsek.terra.addons.biome.pipeline.config.stage.mutator.SmoothMutatorTemplate;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.util.generic.Construct;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineAddon implements MonadAddonInitializer {
public class BiomePipelineAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<Source>>> SOURCE_REGISTRY_KEY = new TypeKey<>() {
public static final TypeKey<Supplier<ObjectTemplate<BiomeSource>>> SOURCE_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<Stage>>> STAGE_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public Monad<?, Init<?>> initialize() {
return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(),
((functionalEventHandler, base) -> Init.ofPure(Construct.construct(() -> {
functionalEventHandler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry =
event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(base.key("PIPELINE"), BiomePipelineTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<Source>>> sourceRegistry =
event.getPack().getOrCreateRegistry(
SOURCE_REGISTRY_KEY);
sourceRegistry.register(base.key("SAMPLER"), SamplerSourceTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<Stage>>> stageRegistry =
event.getPack().getOrCreateRegistry(
STAGE_REGISTRY_KEY);
stageRegistry.register(base.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new);
stageRegistry.register(base.key("SMOOTH"), SmoothStageTemplate::new);
stageRegistry.register(base.key("REPLACE"), ReplaceStageTemplate::new);
stageRegistry.register(base.key("REPLACE_LIST"), ReplaceListStageTemplate::new);
stageRegistry.register(base.key("BORDER"), BorderStageTemplate::new);
stageRegistry.register(base.key("BORDER_LIST"), BorderListStageTemplate::new);
})
.failThrough();
return functionalEventHandler.register(base, ConfigPackPostLoadEvent.class)
.then(event -> {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(PipelineBiome.class,
new PipelineBiomeLoader(biomeRegistry));
});
})))
);
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(addon.key("PIPELINE"), BiomePipelineTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeSource>>> sourceRegistry = event.getPack().getOrCreateRegistry(
SOURCE_REGISTRY_KEY);
sourceRegistry.register(addon.key("SAMPLER"), SamplerSourceTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<Stage>>> stageRegistry = event.getPack().getOrCreateRegistry(
STAGE_REGISTRY_KEY);
stageRegistry.register(addon.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new);
stageRegistry.register(addon.key("SMOOTH"), SmoothMutatorTemplate::new);
stageRegistry.register(addon.key("REPLACE"), ReplaceMutatorTemplate::new);
stageRegistry.register(addon.key("REPLACE_LIST"), ReplaceListMutatorTemplate::new);
stageRegistry.register(addon.key("BORDER"), BorderMutatorTemplate::new);
stageRegistry.register(addon.key("BORDER_LIST"), BorderListMutatorTemplate::new);
})
.failThrough();
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPostLoadEvent.class)
.then(event -> {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(BiomeDelegate.class, new BiomeDelegateLoader(biomeRegistry));
});
}
}
@@ -1,71 +0,0 @@
package com.dfsek.terra.addons.biome.pipeline;
import java.util.function.Consumer;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.util.function.IntIntObjConsumer;
import com.dfsek.terra.api.util.function.IntObjConsumer;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineColumn implements Column<Biome> {
private final int min;
private final int max;
private final int x;
private final int z;
private final Biome biome;
protected BiomePipelineColumn(BiomeProvider biomeProvider, int min, int max, int x, int z, long seed) {
this.min = min;
this.max = max;
this.x = x;
this.z = z;
this.biome = biomeProvider.getBiome(x, 0, z, seed);
}
@Override
public int getMinY() {
return min;
}
@Override
public int getMaxY() {
return max;
}
@Override
public int getX() {
return x;
}
@Override
public int getZ() {
return z;
}
@Override
public Biome get(int y) {
return biome;
}
@Override
public void forRanges(int resolution, IntIntObjConsumer<Biome> consumer) {
consumer.accept(min, max, biome);
}
@Override
public void forEach(Consumer<Biome> consumer) {
for(int y = min; y < max; y++) {
consumer.accept(biome);
}
}
@Override
public void forEach(IntObjConsumer<Biome> consumer) {
for(int y = min; y < max; y++) {
consumer.accept(y, biome);
}
}
}
@@ -0,0 +1,106 @@
/*
* 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.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.StreamSupport;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeHolder> holderCache;
private final BiomePipeline pipeline;
private final int resolution;
private final NoiseSampler mutator;
private final double noiseAmp;
private final Set<Biome> biomes;
public BiomePipelineProvider(BiomePipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) {
this.resolution = resolution;
this.mutator = mutator;
this.noiseAmp = noiseAmp;
holderCache = CacheBuilder.newBuilder()
.maximumSize(1024)
.build(
new CacheLoader<>() {
@Override
public BiomeHolder load(@NotNull SeededVector key) {
return pipeline.getBiomes(key.x, key.z, key.seed);
}
}
);
this.pipeline = pipeline;
Set<BiomeDelegate> biomeSet = new HashSet<>();
pipeline.getSource().getBiomes().forEach(biomeSet::add);
Iterable<BiomeDelegate> result = biomeSet;
for(Stage stage : pipeline.getStages()) {
result = stage.getBiomes(result); // pass through all stages
}
this.biomes = new HashSet<>();
Iterable<BiomeDelegate> finalResult = result;
result.forEach(biomeDelegate -> {
if(biomeDelegate.isEphemeral()) {
StringBuilder biomeList = new StringBuilder("\n");
StreamSupport.stream(finalResult.spliterator(), false)
.sorted(Comparator.comparing(StringIdentifiable::getID))
.forEach(delegate -> biomeList
.append(" - ")
.append(delegate.getID())
.append(':')
.append(delegate.getClass().getCanonicalName())
.append('\n'));
throw new IllegalArgumentException("Biome Pipeline leaks ephemeral biome \"" + biomeDelegate.getID() +
"\". Ensure there is a stage to guarantee replacement of the ephemeral biome. Biomes: " +
biomeList);
}
this.biomes.add(biomeDelegate.getBiome());
});
}
@Override
public Biome getBiome(int x, int z, long seed) {
x += mutator.noise(seed + 1, x, z) * noiseAmp;
z += mutator.noise(seed + 2, x, z) * noiseAmp;
x = FastMath.floorToInt(FastMath.floorDiv(x, resolution));
z = FastMath.floorToInt(FastMath.floorDiv(z, resolution));
int fdX = FastMath.floorDiv(x, pipeline.getSize());
int fdZ = FastMath.floorDiv(z, pipeline.getSize());
return holderCache.getUnchecked(new SeededVector(fdX, fdZ, seed)).getBiome(x - fdX * pipeline.getSize(),
z - fdZ * pipeline.getSize()).getBiome();
}
@Override
public Iterable<Biome> getBiomes() {
return biomes;
}
private record SeededVector(int x, int z, long seed) {
}
}
@@ -1,109 +0,0 @@
package com.dfsek.terra.addons.biome.pipeline;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import net.jafama.FastMath;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.StreamSupport;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeChunk;
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.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class PipelineBiomeProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache;
private final int chunkSize;
private final int resolution;
private final NoiseSampler mutator;
private final double noiseAmp;
private final Set<Biome> biomes;
public PipelineBiomeProvider(Pipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) {
this.resolution = resolution;
this.mutator = mutator;
this.noiseAmp = noiseAmp;
this.chunkSize = pipeline.getChunkSize();
this.biomeChunkCache = Caffeine.newBuilder()
.maximumSize(1024)
.build(pipeline::generateChunk);
Set<PipelineBiome> biomeSet = new HashSet<>();
pipeline.getSource().getBiomes().forEach(biomeSet::add);
Iterable<PipelineBiome> result = biomeSet;
for(Stage stage : pipeline.getStages()) {
result = stage.getBiomes(result);
}
this.biomes = new HashSet<>();
Iterable<PipelineBiome> finalResult = result;
result.forEach(pipelineBiome -> {
if(pipelineBiome.isPlaceholder()) {
StringBuilder biomeList = new StringBuilder("\n");
StreamSupport.stream(finalResult.spliterator(), false)
.sorted(Comparator.comparing(StringIdentifiable::getID))
.forEach(delegate -> biomeList
.append(" - ")
.append(delegate.getID())
.append(':')
.append(delegate.getClass().getCanonicalName())
.append('\n'));
throw new IllegalArgumentException("Biome Pipeline leaks placeholder biome \"" + pipelineBiome.getID() +
"\". Ensure there is a stage to guarantee replacement of the placeholder biome. Biomes: " +
biomeList);
}
this.biomes.add(pipelineBiome.getBiome());
});
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return getBiome(x, z, seed);
}
public Biome getBiome(int x, int z, long seed) {
x += mutator.noise(seed + 1, x, z) * noiseAmp;
z += mutator.noise(seed + 2, x, z) * noiseAmp;
x /= resolution;
z /= resolution;
int chunkX = FastMath.floorDiv(x, chunkSize);
int chunkZ = FastMath.floorDiv(z, chunkSize);
int chunkWorldX = chunkX * chunkSize;
int chunkWorldZ = chunkZ * chunkSize;
int xInChunk = x - chunkWorldX;
int zInChunk = z - chunkWorldZ;
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome();
}
@Override
public Iterable<Biome> getBiomes() {
return biomes;
}
@Override
public Column<Biome> getColumn(int x, int z, long seed, int min, int max) {
return new BiomePipelineColumn(this, min, max, x, z, seed);
}
@Override
public int resolution() {
return resolution;
}
}
@@ -1,10 +0,0 @@
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);
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.api;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
public interface BiomeHolder {
BiomeHolder expand(BiomeExpander expander, long seed);
void mutate(BiomeMutator mutator, long seed);
void fill(BiomeSource source, long seed);
BiomeDelegate getBiome(int x, int z);
BiomeDelegate getBiomeRaw(int x, int z);
}
@@ -1,29 +0,0 @@
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;
}
}
}
@@ -1,14 +0,0 @@
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();
}
@@ -1,19 +0,0 @@
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)));
}
}
@@ -1,11 +0,0 @@
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();
}
@@ -1,15 +0,0 @@
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,35 +0,0 @@
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;
}
}
@@ -0,0 +1,35 @@
package com.dfsek.terra.addons.biome.pipeline.api.delegate;
import java.util.Set;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.world.biome.Biome;
public interface BiomeDelegate extends StringIdentifiable {
static BiomeDelegate ephemeral(String id) {
return new EphemeralBiomeDelegate(id);
}
static BiomeDelegate from(Biome biome) {
return new DelegatedBiome(biome);
}
static BiomeDelegate self() {
return SelfDelegate.INSTANCE;
}
Biome getBiome();
Set<String> getTags();
default boolean isEphemeral() {
return false;
}
default boolean isSelf() {
return false;
}
}
@@ -1,14 +1,14 @@
package com.dfsek.terra.addons.biome.pipeline.api.biome;
package com.dfsek.terra.addons.biome.pipeline.api.delegate;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
public final class DelegatedPipelineBiome implements PipelineBiome {
final class DelegatedBiome implements BiomeDelegate {
private final Biome biome;
public DelegatedPipelineBiome(Biome biome) {
public DelegatedBiome(Biome biome) {
this.biome = biome;
}
@@ -24,7 +24,7 @@ public final class DelegatedPipelineBiome implements PipelineBiome {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof DelegatedPipelineBiome that)) return false;
if(!(obj instanceof DelegatedBiome that)) return false;
return that.biome.equals(this.biome);
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.addons.biome.pipeline.api.biome;
package com.dfsek.terra.addons.biome.pipeline.api.delegate;
import java.util.HashSet;
import java.util.Set;
@@ -6,11 +6,11 @@ import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
final class PlaceholderPipelineBiome implements PipelineBiome {
final class EphemeralBiomeDelegate implements BiomeDelegate {
private final Set<String> tags;
private final String id;
public PlaceholderPipelineBiome(String id) {
public EphemeralBiomeDelegate(String id) {
this.id = id;
tags = new HashSet<>();
tags.add(id);
@@ -19,7 +19,7 @@ final class PlaceholderPipelineBiome implements PipelineBiome {
@Override
public Biome getBiome() {
throw new UnsupportedOperationException("Cannot get raw biome from placeholder pipeline biome");
throw new UnsupportedOperationException("Cannot get biome from ephemeral delegate");
}
@Override
@@ -33,7 +33,7 @@ final class PlaceholderPipelineBiome implements PipelineBiome {
}
@Override
public boolean isPlaceholder() {
public boolean isEphemeral() {
return true;
}
@@ -44,7 +44,7 @@ final class PlaceholderPipelineBiome implements PipelineBiome {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof PlaceholderPipelineBiome that)) return false;
if(!(obj instanceof EphemeralBiomeDelegate that)) return false;
return this.id.equals(that.id);
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.addons.biome.pipeline.api.biome;
package com.dfsek.terra.addons.biome.pipeline.api.delegate;
import java.util.Collections;
import java.util.Set;
@@ -6,11 +6,11 @@ import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
final class SelfPipelineBiome implements PipelineBiome {
public static final SelfPipelineBiome INSTANCE = new SelfPipelineBiome();
final class SelfDelegate implements BiomeDelegate {
public static final SelfDelegate INSTANCE = new SelfDelegate();
private SelfDelegate() {
private SelfPipelineBiome() {
}
@Override
@@ -24,7 +24,7 @@ final class SelfPipelineBiome implements PipelineBiome {
}
@Override
public boolean isPlaceholder() {
public boolean isEphemeral() {
return true;
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.api.stage;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
public interface Stage {
BiomeHolder apply(BiomeHolder in, long seed);
boolean isExpansion();
Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes);
}
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.api.stage.type;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
public interface BiomeExpander {
BiomeDelegate getBetween(double x, double z, long seed, BiomeDelegate... others);
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.api.stage.type;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
public interface BiomeMutator {
BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed);
default Iterable<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);
}
}
}
@@ -8,25 +8,25 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.world.biome.Biome;
public class PipelineBiomeLoader implements TypeLoader<PipelineBiome> {
public class BiomeDelegateLoader implements TypeLoader<BiomeDelegate> {
private final Registry<Biome> biomeRegistry;
public PipelineBiomeLoader(Registry<Biome> biomeRegistry) {
public BiomeDelegateLoader(Registry<Biome> biomeRegistry) {
this.biomeRegistry = biomeRegistry;
}
@Override
public PipelineBiome load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
public BiomeDelegate load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
throws LoadException {
if(c.equals("SELF")) return PipelineBiome.self();
if(c.equals("SELF")) return BiomeDelegate.self();
return biomeRegistry
.getByID((String) c)
.map(PipelineBiome::from)
.orElseGet(() -> PipelineBiome.placeholder((String) c));
.map(BiomeDelegate::from)
.orElseGet(() -> BiomeDelegate.ephemeral((String) c));
}
}
@@ -10,50 +10,45 @@ 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 java.util.List;
import com.dfsek.terra.addons.biome.pipeline.PipelineBiomeProvider;
import com.dfsek.terra.addons.biome.pipeline.pipeline.PipelineImpl;
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.BiomePipeline;
import com.dfsek.terra.addons.biome.pipeline.BiomePipelineProvider;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
@SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> {
@Value("resolution")
public class BiomePipelineTemplate extends BiomeProviderTemplate {
@Value("pipeline.initial-size")
@Default
@Description("""
The resolution at which to sample biomes.
The initial size of biome chunks. This value must be at least 2.
<b>This is not the final size of biome chunks. Final chunks will be much larger</b>.
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;
It is recommended to keep biome chunks' final size in the range of [50, 300]
to prevent performance issues. To calculate the size of biome chunks, simply
take initial-size and for each expand stage, multiply the running value by 2
and subtract 1. (The size is also printed to the server console if you
have debug mode enabled)""")
private @Meta int initialSize = 2;
@Value("pipeline.source")
@Description("The Biome Source to use for initial population of biomes.")
private @Meta Source source;
private @Meta BiomeSource source;
@Value("pipeline.stages")
@Description("A list of pipeline stages to apply to the result of #source")
private @Meta List<@Meta Stage> stages;
@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
public BiomeProvider get() {
return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 500), resolution, blendSampler, blendAmplitude);
BiomePipeline.BiomePipelineBuilder biomePipelineBuilder = new BiomePipeline.BiomePipelineBuilder(initialSize);
stages.forEach(biomePipelineBuilder::addStage);
BiomePipeline pipeline = biomePipelineBuilder.build(source);
return new BiomePipelineProvider(pipeline, resolution, blend, blendAmp);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public abstract class BiomeProviderTemplate implements ObjectTemplate<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;
}
@@ -5,13 +5,13 @@
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.config.source;
package com.dfsek.terra.addons.biome.pipeline.config;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
import com.dfsek.terra.addons.biome.pipeline.source.SamplerSource;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -25,10 +25,10 @@ public class SamplerSourceTemplate extends SourceTemplate {
@Value("biomes")
@Description("The biomes to be distributed.")
private @Meta ProbabilityCollection<@Meta PipelineBiome> biomes;
private @Meta ProbabilityCollection<@Meta BiomeDelegate> biomes;
@Override
public Source get() {
public BiomeSource get() {
return new SamplerSource(biomes, noise);
}
}
@@ -5,13 +5,13 @@
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.config.source;
package com.dfsek.terra.addons.biome.pipeline.config;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.source.BiomeSource;
public abstract class SourceTemplate implements ObjectTemplate<Source> {
public abstract class SourceTemplate implements ObjectTemplate<BiomeSource> {
}
@@ -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.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -7,14 +7,15 @@
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.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.stage.expander.FractalExpander;
import com.dfsek.terra.addons.biome.pipeline.expand.FractalExpander;
import com.dfsek.terra.addons.biome.pipeline.stages.ExpanderStage;
public class ExpanderStageTemplate extends StageTemplate {
@Override
public Expander get() {
return new FractalExpander(noise);
public Stage get() {
return new ExpanderStage(new FractalExpander(noise));
}
}
@@ -11,16 +11,17 @@ import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.BorderListStage;
import com.dfsek.terra.addons.biome.pipeline.mutator.BorderListMutator;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class BorderListStageTemplate extends StageTemplate {
public class BorderListMutatorTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@@ -28,14 +29,14 @@ public class BorderListStageTemplate extends StageTemplate {
private @Meta String defaultReplace;
@Value("default-to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo;
@Value("replace")
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace;
@Override
public Stage get() {
return new BorderListStage(replace, from, defaultReplace, noise, defaultTo);
return new MutatorStage(new BorderListMutator(replace, from, defaultReplace, noise, defaultTo));
}
}
@@ -9,16 +9,17 @@ package com.dfsek.terra.addons.biome.pipeline.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.BorderStage;
import com.dfsek.terra.addons.biome.pipeline.mutator.BorderMutator;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class BorderStageTemplate extends StageTemplate {
public class BorderMutatorTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@@ -26,10 +27,10 @@ public class BorderStageTemplate extends StageTemplate {
private @Meta String replace;
@Value("to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
private @Meta ProbabilityCollection<@Meta BiomeDelegate> to;
@Override
public Stage get() {
return new BorderStage(from, replace, noise, to);
return new MutatorStage(new BorderMutator(from, replace, noise, to));
}
}
@@ -11,27 +11,28 @@ import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.ReplaceListStage;
import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceListMutator;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class ReplaceListStageTemplate extends StageTemplate {
public class ReplaceListMutatorTemplate extends StageTemplate {
@Value("default-from")
private @Meta String defaultFrom;
@Value("default-to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
private @Meta ProbabilityCollection<@Meta BiomeDelegate> defaultTo;
@Value("to")
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
private @Meta Map<@Meta BiomeDelegate, @Meta ProbabilityCollection<@Meta BiomeDelegate>> replace;
@Override
public Stage get() {
return new ReplaceListStage(replace, defaultFrom, defaultTo, noise);
return new MutatorStage(new ReplaceListMutator(replace, defaultFrom, defaultTo, noise));
}
}
@@ -9,24 +9,25 @@ package com.dfsek.terra.addons.biome.pipeline.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.ReplaceStage;
import com.dfsek.terra.addons.biome.pipeline.mutator.ReplaceMutator;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class ReplaceStageTemplate extends StageTemplate {
public class ReplaceMutatorTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@Value("to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
private @Meta ProbabilityCollection<@Meta BiomeDelegate> to;
@Override
public Stage get() {
return new ReplaceStage(from, to, noise);
return new MutatorStage(new ReplaceMutator(from, to, noise));
}
}
@@ -7,14 +7,15 @@
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.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.stage.mutators.SmoothStage;
import com.dfsek.terra.addons.biome.pipeline.mutator.SmoothMutator;
import com.dfsek.terra.addons.biome.pipeline.stages.MutatorStage;
public class SmoothStageTemplate extends StageTemplate {
public class SmoothMutatorTemplate extends StageTemplate {
@Override
public Stage get() {
return new SmoothStage(noise);
return new MutatorStage(new SmoothMutator(noise));
}
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.expand;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
public class FractalExpander implements BiomeExpander {
private final NoiseSampler sampler;
public FractalExpander(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public BiomeDelegate getBetween(double x, double z, long seed, BiomeDelegate... others) {
return others[MathUtil.normalizeIndex(sampler.noise(seed, x, z), others.length)];
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.mutator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class BorderListMutator implements BiomeMutator {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<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;
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.mutator;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class BorderMutator implements BiomeMutator {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<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;
}
}
@@ -5,28 +5,27 @@
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.stage.mutators;
package com.dfsek.terra.addons.biome.pipeline.mutator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.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.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 ReplaceListStage implements Stage {
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
public class ReplaceListMutator implements BiomeMutator {
private final Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace;
private final NoiseSampler sampler;
private final ProbabilityCollection<PipelineBiome> replaceDefault;
private final ProbabilityCollection<BiomeDelegate> replaceDefault;
private final String defaultTag;
public ReplaceListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String defaultTag,
ProbabilityCollection<PipelineBiome> replaceDefault, NoiseSampler sampler) {
public ReplaceListMutator(Map<BiomeDelegate, ProbabilityCollection<BiomeDelegate>> replace, String defaultTag,
ProbabilityCollection<BiomeDelegate> replaceDefault, NoiseSampler sampler) {
this.replace = replace;
this.sampler = sampler;
this.defaultTag = defaultTag;
@@ -34,29 +33,24 @@ public class ReplaceListStage implements Stage {
}
@Override
public PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) {
BiomeDelegate center = viewPoint.getBiome(0, 0);
if(replace.containsKey(center)) {
PipelineBiome biome = replace.get(center).get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
BiomeDelegate biome = replace.get(center).get(sampler, x, z, seed);
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome;
}
if(viewPoint.getBiome().getTags().contains(defaultTag)) {
PipelineBiome biome = replaceDefault.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
if(viewPoint.getBiome(0, 0).getTags().contains(defaultTag)) {
BiomeDelegate biome = replaceDefault.get(sampler, x, z, seed);
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome;
}
return center;
}
@Override
public int maxRelativeReadDistance() {
return 0;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
Set<BiomeDelegate> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
Set<BiomeDelegate> reject = new HashSet<>();
biomes.forEach(biome -> {
if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) {
@@ -5,48 +5,42 @@
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.stage.mutators;
package com.dfsek.terra.addons.biome.pipeline.mutator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.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.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 ReplaceStage implements Stage {
public class ReplaceMutator implements BiomeMutator {
private final String replaceableTag;
private final ProbabilityCollection<PipelineBiome> replace;
private final ProbabilityCollection<BiomeDelegate> replace;
private final NoiseSampler sampler;
public ReplaceStage(String replaceable, ProbabilityCollection<PipelineBiome> replace, NoiseSampler sampler) {
public ReplaceMutator(String replaceable, ProbabilityCollection<BiomeDelegate> replace, NoiseSampler sampler) {
this.replaceableTag = replaceable;
this.replace = replace;
this.sampler = sampler;
}
@Override
public PipelineBiome apply(ViewPoint viewPoint) {
if(viewPoint.getBiome().getTags().contains(replaceableTag)) {
PipelineBiome biome = replace.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) {
if(viewPoint.getBiome(0, 0).getTags().contains(replaceableTag)) {
BiomeDelegate biome = replace.get(sampler, x, z, seed);
return biome.isSelf() ? viewPoint.getBiome(0, 0) : biome;
}
return viewPoint.getBiome();
return viewPoint.getBiome(0, 0);
}
@Override
public int maxRelativeReadDistance() {
return 0;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
Set<BiomeDelegate> biomeSet = new HashSet<>();
Set<BiomeDelegate> reject = new HashSet<>();
biomes.forEach(biome -> {
if(!biome.getTags().contains(replaceableTag)) {
biomeSet.add(biome);
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.mutator;
import java.util.Objects;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
public class SmoothMutator implements BiomeMutator {
private final NoiseSampler sampler;
public SmoothMutator(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public BiomeDelegate mutate(ViewPoint viewPoint, double x, double z, long seed) {
BiomeDelegate top = viewPoint.getBiome(1, 0);
BiomeDelegate bottom = viewPoint.getBiome(-1, 0);
BiomeDelegate left = viewPoint.getBiome(0, 1);
BiomeDelegate right = viewPoint.getBiome(0, -1);
boolean vert = Objects.equals(top, bottom) && top != null;
boolean horiz = Objects.equals(left, right) && left != null;
if(vert && horiz) {
return MathUtil.normalizeIndex(sampler.noise(seed, x, z), 2) == 0 ? left : top;
}
if(vert) return top;
if(horiz) return left;
return viewPoint.getBiome(0, 0);
}
}
@@ -1,218 +0,0 @@
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();
}
}
}
@@ -1,92 +0,0 @@
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;
}
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.source;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
public interface BiomeSource {
BiomeDelegate getBiome(double x, double z, long seed);
Iterable<BiomeDelegate> getBiomes();
}
@@ -1,27 +1,33 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.source;
import com.dfsek.terra.addons.biome.pipeline.api.Source;
import com.dfsek.terra.addons.biome.pipeline.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class SamplerSource implements Source {
private final ProbabilityCollection<PipelineBiome> biomes;
public class SamplerSource implements BiomeSource {
private final ProbabilityCollection<BiomeDelegate> biomes;
private final NoiseSampler sampler;
public SamplerSource(ProbabilityCollection<PipelineBiome> biomes, NoiseSampler sampler) {
public SamplerSource(ProbabilityCollection<BiomeDelegate> biomes, NoiseSampler sampler) {
this.biomes = biomes;
this.sampler = sampler;
}
@Override
public PipelineBiome get(long seed, int x, int z) {
public BiomeDelegate getBiome(double x, double z, long seed) {
return biomes.get(sampler, x, z, seed);
}
@Override
public Iterable<PipelineBiome> getBiomes() {
public Iterable<BiomeDelegate> getBiomes() {
return biomes.getContents();
}
}
@@ -1,27 +0,0 @@
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);
}
}
@@ -1,37 +0,0 @@
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);
}
}
}
@@ -1,86 +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.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;
}
}
@@ -1,81 +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.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;
}
}
@@ -1,56 +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.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;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.stages;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeExpander;
public class ExpanderStage implements Stage {
private final BiomeExpander expander;
public ExpanderStage(BiomeExpander expander) {
this.expander = expander;
}
@Override
public BiomeHolder apply(BiomeHolder in, long seed) {
return in.expand(expander, seed);
}
@Override
public boolean isExpansion() {
return true;
}
@Override
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
return biomes;
}
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.stages;
import com.dfsek.terra.addons.biome.pipeline.api.BiomeHolder;
import com.dfsek.terra.addons.biome.pipeline.api.delegate.BiomeDelegate;
import com.dfsek.terra.addons.biome.pipeline.api.stage.Stage;
import com.dfsek.terra.addons.biome.pipeline.api.stage.type.BiomeMutator;
public class MutatorStage implements Stage {
private final BiomeMutator mutator;
public MutatorStage(BiomeMutator mutator) {
this.mutator = mutator;
}
@Override
public BiomeHolder apply(BiomeHolder in, long seed) {
in.mutate(mutator, seed);
return in;
}
@Override
public boolean isExpansion() {
return false;
}
@Override
public Iterable<BiomeDelegate> getBiomes(Iterable<BiomeDelegate> biomes) {
return mutator.getBiomes(biomes);
}
public enum Type {
REPLACE,
REPLACE_LIST,
BORDER,
BORDER_LIST,
SMOOTH
}
}
@@ -6,7 +6,7 @@ version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.pipeline.BiomePipelineAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
issues: https://github.com/PolyhedralDev/Terra-biome-provider-pipeline/issues
source: https://github.com/PolyhedralDev/Terra-biome-provider-pipeline
docs: https://github.com/PolyhedralDev/Terra/wiki
license: GNU LGPL v3.0
@@ -1,5 +1,5 @@
version = version("1.0.0")
version = version("0.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
shadedApi(project(":common:addons:manifest-addon-loader"))
}
@@ -8,7 +8,6 @@
package com.dfsek.terra.addons.biome.single;
import java.util.Collections;
import java.util.Optional;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
@@ -22,15 +21,10 @@ public class SingleBiomeProvider implements BiomeProvider {
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
public Biome getBiome(int x, int z, long seed) {
return biome;
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return Optional.of(biome);
}
@Override
public Iterable<Biome> getBiomes() {
return Collections.singleton(biome);
@@ -11,38 +11,37 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
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.Get;
import com.dfsek.terra.addons.manifest.api.monad.Init;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import org.jetbrains.annotations.NotNull;
public class SingleBiomeProviderAddon implements MonadAddonInitializer {
public class SingleBiomeProviderAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public @NotNull Monad<?, Init<?>> initialize() {
return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(),
Get.platform(),
((handler, base, platform) -> Init.ofPure(
handler.register(base, ConfigPackPreLoadEvent.class)
.then(event -> {
Registry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().createRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(base.key("SINGLE"), SingleBiomeProviderTemplate::new);
})
.failThrough()))
);
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(addon.key("SINGLE"), SingleBiomeProviderTemplate::new);
})
.failThrough();
}
}
@@ -6,7 +6,7 @@ version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.single.SingleBiomeProviderAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
issues: https://github.com/PolyhedralDev/Terra-biome-provider-single/issues
source: https://github.com/PolyhedralDev/Terra-biome-provider-single
docs: https://github.com/PolyhedralDev/Terra/wiki
license: GNU LGPL v3.0
-4
View File
@@ -1,4 +0,0 @@
# Biome Query API
This addon contains an API to allow other addons to quickly query
Biome data, by baking queries and using Contexts on biomes.
@@ -1,5 +0,0 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
}
@@ -1,48 +0,0 @@
package com.dfsek.terra.addons.biome.query;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import com.dfsek.terra.addons.biome.query.impl.BiomeTagFlattener;
import com.dfsek.terra.addons.biome.query.impl.BiomeTagHolder;
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.Get;
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.functional.FunctionalEventHandler;
import com.dfsek.terra.api.properties.Context;
import com.dfsek.terra.api.properties.PropertyKey;
import com.dfsek.terra.api.util.function.monad.Monad;
import com.dfsek.terra.api.world.biome.Biome;
public class BiomeQueryAPIAddon implements MonadAddonInitializer {
public static PropertyKey<BiomeTagHolder> BIOME_TAG_KEY = Context.create(BiomeTagHolder.class);
@Override
public @NotNull Monad<?, Init<?>> initialize() {
return Do.with(
Get.eventManager().map(eventManager -> eventManager.getHandler(FunctionalEventHandler.class)),
Get.addon(),
((functionalEventHandler, base) -> Init.ofPure(
functionalEventHandler.register(base, ConfigPackPostLoadEvent.class)
.then(event -> {
Collection<Biome> biomes = event
.getPack()
.getRegistry(Biome.class)
.entries();
BiomeTagFlattener flattener = new BiomeTagFlattener(
biomes.stream()
.flatMap(biome -> biome.getTags().stream())
.toList());
biomes.forEach(biome -> biome.getContext()
.put(BIOME_TAG_KEY, new BiomeTagHolder(biome, flattener)));
})
.global()
)));
}
}
@@ -1,17 +0,0 @@
package com.dfsek.terra.addons.biome.query.api;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.query.impl.SingleTagQuery;
import com.dfsek.terra.api.world.biome.Biome;
public final class BiomeQueries {
private BiomeQueries() {
}
public static Predicate<Biome> has(String tag) {
return new SingleTagQuery(tag);
}
}
@@ -1,20 +0,0 @@
package com.dfsek.terra.addons.biome.query.impl;
import java.util.List;
public class BiomeTagFlattener {
private final List<String> tags;
public BiomeTagFlattener(List<String> tags) {
this.tags = tags;
}
public int index(String tag) {
return tags.indexOf(tag);
}
public int size() {
return tags.size();
}
}
@@ -1,26 +0,0 @@
package com.dfsek.terra.addons.biome.query.impl;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.world.biome.Biome;
public class BiomeTagHolder implements Properties {
private final boolean[] tags;
private final BiomeTagFlattener flattener;
public BiomeTagHolder(Biome biome, BiomeTagFlattener flattener) {
this.tags = new boolean[flattener.size()];
this.flattener = flattener;
for(String tag : biome.getTags()) {
tags[flattener.index(tag)] = true;
}
}
boolean get(int index) {
return tags[index];
}
public BiomeTagFlattener getFlattener() {
return flattener;
}
}
@@ -1,31 +0,0 @@
package com.dfsek.terra.addons.biome.query.impl;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.query.BiomeQueryAPIAddon;
import com.dfsek.terra.api.world.biome.Biome;
public class SingleTagQuery implements Predicate<Biome> {
private final String tag;
private int tagIndex = -1;
public SingleTagQuery(String tag) {
this.tag = tag;
}
@Override
public boolean test(Biome biome) {
if(tagIndex < 0) {
tagIndex = biome
.getContext()
.get(BiomeQueryAPIAddon.BIOME_TAG_KEY)
.getFlattener()
.index(tag);
}
return biome
.getContext()
.get(BiomeQueryAPIAddon.BIOME_TAG_KEY)
.get(tagIndex);
}
}
@@ -1,12 +0,0 @@
schema-version: 1
contributors:
- Terra contributors
id: biome-query-api
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.query.BiomeQueryAPIAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License

Some files were not shown because too many files have changed in this diff Show More