Merge pull request #43 from PolyhedralDev/agnostic

Platform-agnostic engine & TerraScript structures
This commit is contained in:
dfsek
2021-01-09 11:30:34 -07:00
committed by GitHub
568 changed files with 21651 additions and 4872 deletions

6
.gitignore vendored
View File

@@ -137,3 +137,9 @@ build
!lib/*.jar
.idea/Terra.iml
/run/
.idea/**.iml
/lang/
/packs/
/config.yml
/region/

View File

@@ -1,192 +1,11 @@
//import java.util.zip.ZipFile
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import java.io.ByteArrayOutputStream
import java.net.URL
import java.nio.channels.Channels
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import com.dfsek.terra.getGitHash
plugins {
java
maven
id("com.github.johnrengelman.shadow").version("6.1.0")
val versionObj = Version("3", "0", "0", true)
allprojects {
version = versionObj
group = "com.dfsek.terra"
}
repositories {
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("http://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
val versionObj = Version("2", "1", "1", true)
version = versionObj
dependencies {
val gaeaVersion = "1.15.0"
compileOnly("org.polydev.gaea:Gaea:${gaeaVersion}")
testImplementation("org.polydev.gaea:Gaea:${gaeaVersion}")
compileOnly("org.jetbrains:annotations:20.1.0")
implementation("commons-io:commons-io:2.4")
implementation("org.apache.commons:commons-imaging:1.0-alpha2")
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT")
implementation("org.bstats:bstats-bukkit:1.7")
compileOnly("com.googlecode.json-simple:json-simple:1.1")
implementation("com.scireum:parsii:1.2.1")
compileOnly("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT")
implementation("io.papermc:paperlib:1.0.5")
implementation("net.jafama:jafama:2.3.2")
implementation("com.dfsek:Tectonic:1.0.3")
// JUnit.
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0")
}
val compileJava: JavaCompile by tasks
val mainSourceSet: SourceSet = sourceSets["main"]
tasks.withType<ProcessResources> {
include("**/*.yml")
filter<org.apache.tools.ant.filters.ReplaceTokens>(
"tokens" to mapOf(
"VERSION" to project.version.toString()
)
)
}
compileJava.apply {
options.encoding = "UTF-8"
doFirst {
options.compilerArgs = mutableListOf("-Xlint:all")
}
}
tasks.test {
useJUnitPlatform()
maxHeapSize = "4G"
ignoreFailures = false
failFast = true
maxParallelForks = 12
}
tasks.named<ShadowJar>("shadowJar") {
// Tell shadow to download the packs
dependsOn(downloadDefaultPacks)
archiveClassifier.set("")
archiveBaseName.set("Terra")
setVersion(project.version)
relocate("org.apache.commons", "com.dfsek.terra.lib.commons")
relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats")
relocate("parsii", "com.dfsek.terra.lib.parsii")
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
relocate("com.dfsek.tectonic", "com.dfsek.terra.lib.tectonic")
minimize()
}
tasks.build {
dependsOn(tasks.shadowJar)
// dependsOn(testWithPaper)
// testWithPaper.mustRunAfter(tasks.shadowJar)
}
val testDir = "target/server/"
val setupServer = tasks.create("setupServer") {
dependsOn(tasks.shadowJar)
doFirst {
// clean
file("${testDir}/").deleteRecursively()
file("${testDir}/plugins").mkdirs()
// Downloading latest paper jar.
val paperUrl = URL("https://papermc.io/api/v1/paper/1.16.4/latest/download")
val paperReadableByteChannel = Channels.newChannel(paperUrl.openStream())
val paperFile = file("${testDir}/paper.jar")
val paperFileOutputStream = paperFile.outputStream()
val paperFileChannel = paperFileOutputStream.channel
paperFileChannel.transferFrom(paperReadableByteChannel, 0, Long.MAX_VALUE)
// Cloning test setup.
gitClone("https://github.com/PolyhedralDev/WorldGenTestServer")
// Copying plugins
Files.move(Paths.get("WorldGenTestServer/plugins"),
Paths.get("$testDir/plugins"),
StandardCopyOption.REPLACE_EXISTING)
// Copying config
val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText()
file("${testDir}/server.properties").writeText(serverText)
val bukkitText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/bukkit.yml").readText()
file("${testDir}/bukkit.yml").writeText(bukkitText.replace("\${world}", "world").replace("\${gen}", "Terra:DEFAULT"))
File("${testDir}/eula.txt").writeText("eula=true")
// clean up
file("WorldGenTestServer").deleteRecursively()
}
}
val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
doFirst {
// Downloading latest paper jar.
// if (file("${buildDir}/resources/main/packs/default").exists() && file("${buildDir}/resources/main/packs/nether").exists())
// return@doFirst
// else
file("${buildDir}/resources/main/packs/").deleteRecursively()
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraDefaultConfig/releases/download/latest/default.zip")
downloadPack(defaultPackUrl)
val netherPackUrl = URL("https://github.com/PolyhedralDev/TerraDefaultConfig/releases/download/latest/nether.zip")
downloadPack(netherPackUrl)
}
}
val testWithPaper = task<JavaExec>(name = "testWithPaper") {
standardInput = System.`in`
dependsOn(tasks.shadowJar)
// Copy Terra into dir
doFirst {
copy {
from("${buildDir}/libs/Terra-${versionObj}.jar")
into("${testDir}/plugins/")
}
}
main = "io.papermc.paperclip.Paperclip"
jvmArgs = listOf("-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200",
"-XX:+UnlockExperimentalVMOptions", "-XX:+DisableExplicitGC", "-XX:+AlwaysPreTouch",
"-XX:G1NewSizePercent=30", "-XX:G1MaxNewSizePercent=40", "-XX:G1HeapRegionSize=8M",
"-XX:G1ReservePercent=20", "-XX:G1HeapWastePercent=5", "-XX:G1MixedGCCountTarget=4",
"-XX:InitiatingHeapOccupancyPercent=15", "-XX:G1MixedGCLiveThresholdPercent=90",
"-XX:G1RSetUpdatingPauseTimePercent=5", "-XX:SurvivorRatio=32", "-XX:+PerfDisableSharedMem",
"-XX:MaxTenuringThreshold=1", "-Dusing.aikars.flags=https://mcflags.emc.gs",
"-Daikars.new.flags=true", "-DIReallyKnowWhatIAmDoingISwear")
maxHeapSize = "2G"
args = listOf("nogui")
workingDir = file("${testDir}/")
classpath = files("${testDir}/paper.jar")
}
/**
* Version class that does version stuff.
*/
@@ -199,28 +18,4 @@ class Version(val major: String, val minor: String, val revision: String, val pr
else //Only use git hash if it's a prerelease.
"$major.$minor.$revision-BETA+${getGitHash()}"
}
}
fun getGitHash(): String {
val stdout = ByteArrayOutputStream()
exec {
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout
}
return stdout.toString().trim()
}
fun gitClone(name: String) {
val stdout = ByteArrayOutputStream()
exec {
commandLine = mutableListOf("git", "clone", name)
standardOutput = stdout
}
}
fun downloadPack(packUrl: URL) {
val fileName = packUrl.file.substring(packUrl.file.lastIndexOf("/"))
val file = file("${buildDir}/resources/main/packs/${fileName}")
file.parentFile.mkdirs()
file.outputStream().write(packUrl.readBytes())
}

13
buildSrc/build.gradle.kts Normal file
View File

@@ -0,0 +1,13 @@
plugins {
`kotlin-dsl`
kotlin("jvm") version embeddedKotlinVersion
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
"implementation"("com.github.jengelman.gradle.plugins:shadow:+")
}

View File

@@ -0,0 +1,47 @@
package com.dfsek.terra
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.withType
import java.io.ByteArrayOutputStream
fun Project.configureCommon() {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "idea")
configureDependencies()
configureCompilation()
configureDistribution()
version = rootProject.version
tasks.withType<Test>().configureEach {
useJUnitPlatform()
maxHeapSize = "2G"
ignoreFailures = false
failFast = true
maxParallelForks = 12
}
}
fun Project.getGitHash(): String {
val stdout = java.io.ByteArrayOutputStream()
exec {
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout
}
return stdout.toString().trim()
}
fun Project.gitClone(name: String) {
val stdout = ByteArrayOutputStream()
exec {
commandLine = mutableListOf("git", "clone", name)
standardOutput = stdout
}
}

View File

@@ -0,0 +1,27 @@
package com.dfsek.terra
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
fun Project.configureCompilation() {
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
doFirst {
options.compilerArgs = mutableListOf("-Xlint:all")
}
}
tasks.withType<Javadoc> {
options.encoding = "UTF-8"
}
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.repositories
fun Project.configureDependencies() {
repositories {
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("http://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://maven.fabricmc.net/") }
gradlePluginPortal()
jcenter()
mavenCentral()
}
dependencies {
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
"compileOnly"("org.jetbrains:annotations:20.1.0")
}
}

View File

@@ -0,0 +1,87 @@
package com.dfsek.terra
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.plugins.BasePluginConvention
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.kotlin.dsl.*
import java.io.File
import java.net.URL
fun Project.configureDistribution() {
apply(plugin = "java-library")
apply(plugin = "com.github.johnrengelman.shadow")
// configurations.create("shaded")
configurations {
val shaded = create("shaded")
getByName("compile").extendsFrom(shaded)
// shaded.extendsFrom(getByName("compile"))
val shadedApi = create("shadedApi")
shaded.extendsFrom(shadedApi)
getByName("api").extendsFrom(shadedApi)
val shadedImplementation = create("shadedImplementation")
shaded.extendsFrom(shadedImplementation)
getByName("implementation").extendsFrom(shadedImplementation)
}
// tasks.withType<JavaCompile> {
// classpath +=
// }
val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively()
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraDefaultConfig/releases/download/latest/default.zip")
downloadPack(defaultPackUrl, project)
val netherPackUrl = URL("https://github.com/PolyhedralDev/TerraDefaultConfig/releases/download/latest/nether.zip")
downloadPack(netherPackUrl, project)
}
}
tasks["processResources"].dependsOn(downloadDefaultPacks)
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
}
tasks.withType<Jar> {
from("../LICENSE", "../../LICENSE")
}
tasks.register<Jar>("javadocJar") {
dependsOn("javadoc")
archiveClassifier.set("javadoc")
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
}
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("parsii", "com.dfsek.terra.lib.parsii")
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
minimize()
}
convention.getPlugin<BasePluginConvention>().archivesBaseName = project.name
tasks.named<DefaultTask>("build") {
dependsOn(tasks["shadowJar"])
}
}
fun downloadPack(packUrl: URL, project: Project) {
val fileName = packUrl.file.substring(packUrl.file.lastIndexOf("/"))
val file = File("${project.buildDir}/resources/main/packs/${fileName}")
file.parentFile.mkdirs()
file.outputStream().write(packUrl.readBytes())
}

23
common/build.gradle.kts Normal file
View File

@@ -0,0 +1,23 @@
import com.dfsek.terra.configureCommon
plugins {
`java-library`
}
configureCommon()
group = "com.dfsek.terra.common"
dependencies {
"shadedApi"("org.apache.commons:commons-rng-core:1.3")
"shadedApi"("commons-io:commons-io:2.4")
"shadedApi"("com.scireum:parsii:1.2.1")
"shadedApi"("com.dfsek:Tectonic:1.1.0")
"shadedApi"("net.jafama:jafama:2.3.2")
"shadedApi"("org.yaml:snakeyaml:1.27")
"compileOnly"("com.googlecode.json-simple:json-simple:1.1")
"shadedApi"("com.google.guava:guava:30.0-jre")
}

View File

@@ -1,9 +1,9 @@
package com.dfsek.terra;
import org.bukkit.World;
import org.polydev.gaea.profiler.DataType;
import org.polydev.gaea.profiler.Measurement;
import org.polydev.gaea.profiler.WorldProfiler;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.profiler.DataType;
import com.dfsek.terra.api.profiler.Measurement;
import com.dfsek.terra.api.profiler.WorldProfiler;
public class TerraProfiler extends WorldProfiler {
public TerraProfiler(World w) {
@@ -13,7 +13,6 @@ public class TerraProfiler extends WorldProfiler {
.addMeasurement(new Measurement(10000000, DataType.PERIOD_MILLISECONDS), "TreeTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime")
.addMeasurement(new Measurement(5000000, DataType.PERIOD_MILLISECONDS), "CaveTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "ElevationTime");
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime");
}
}

View File

@@ -0,0 +1,55 @@
package com.dfsek.terra;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.generator.GeneratorWrapper;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.biome.BiomeZone;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.config.base.ConfigPack;
public class TerraWorld {
private final TerraBiomeGrid grid;
private final BiomeZone zone;
private final ConfigPack config;
private final boolean safe;
private final TerraProfiler profiler;
private final World world;
public TerraWorld(World w, ConfigPack c, TerraPlugin main) {
config = c;
profiler = new TerraProfiler(w);
this.grid = new TerraBiomeGrid.TerraBiomeGridBuilder(w.getSeed(), c, main).build();
this.zone = grid.getZone();
this.world = w;
safe = true;
}
public World getWorld() {
return world;
}
public static boolean isTerraWorld(World w) {
return w.getGenerator().getHandle() instanceof GeneratorWrapper;
}
public TerraBiomeGrid getGrid() {
return grid;
}
public ConfigPack getConfig() {
return config;
}
public BiomeZone getZone() {
return zone;
}
public boolean isSafe() {
return safe;
}
public TerraProfiler getProfiler() {
return profiler;
}
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.Handle;
public interface Entity extends Handle {
Location getLocation();
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api;
import com.dfsek.terra.api.platform.world.World;
import java.io.File;
public class Gaea {
private static boolean debug;
public static File getGaeaFolder(World w) {
File f = new File(w.getWorldFolder(), "gaea");
f.mkdirs();
return f;
}
public static boolean isDebug() {
return debug;
}
}

View File

@@ -0,0 +1,63 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.math.GridSpawn;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.palette.PaletteHolder;
import com.dfsek.terra.biome.palette.PaletteLayer;
import com.dfsek.terra.carving.CarverPalette;
import com.dfsek.terra.config.loaders.ImageLoaderLoader;
import com.dfsek.terra.config.loaders.MaterialSetLoader;
import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader;
import com.dfsek.terra.config.loaders.RangeLoader;
import com.dfsek.terra.config.loaders.config.FloraLayerLoader;
import com.dfsek.terra.config.loaders.config.GridSpawnLoader;
import com.dfsek.terra.config.loaders.config.NoiseBuilderLoader;
import com.dfsek.terra.config.loaders.config.OreConfigLoader;
import com.dfsek.terra.config.loaders.config.OreHolderLoader;
import com.dfsek.terra.config.loaders.config.TreeLayerLoader;
import com.dfsek.terra.config.loaders.palette.CarverPaletteLoader;
import com.dfsek.terra.config.loaders.palette.PaletteHolderLoader;
import com.dfsek.terra.config.loaders.palette.PaletteLayerLoader;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.image.ImageLoader;
import com.dfsek.terra.population.items.flora.FloraLayer;
import com.dfsek.terra.population.items.flora.TerraFlora;
import com.dfsek.terra.population.items.ores.Ore;
import com.dfsek.terra.population.items.ores.OreConfig;
import com.dfsek.terra.population.items.ores.OreHolder;
import com.dfsek.terra.population.items.tree.TreeLayer;
import com.dfsek.terra.util.MaterialSet;
public class GenericLoaders implements LoaderRegistrar {
private final TerraPlugin main;
public GenericLoaders(TerraPlugin main) {
this.main = main;
}
@Override
public void register(TypeRegistry registry) {
registry.registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader())
.registerLoader(Range.class, new RangeLoader())
.registerLoader(CarverPalette.class, new CarverPaletteLoader())
.registerLoader(GridSpawn.class, new GridSpawnLoader())
.registerLoader(PaletteHolder.class, new PaletteHolderLoader())
.registerLoader(PaletteLayer.class, new PaletteLayerLoader())
.registerLoader(FloraLayer.class, new FloraLayerLoader())
.registerLoader(Ore.Type.class, (t, o, l) -> Ore.Type.valueOf((String) o))
.registerLoader(OreConfig.class, new OreConfigLoader())
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(TreeLayer.class, new TreeLayerLoader())
.registerLoader(MaterialSet.class, new MaterialSetLoader())
.registerLoader(OreHolder.class, new OreHolderLoader())
.registerLoader(ImageLoader.class, new ImageLoaderLoader())
.registerLoader(TerraBiomeGrid.Type.class, (t, o, l) -> TerraBiomeGrid.Type.valueOf((String) o))
.registerLoader(ImageLoader.Channel.class, (t, o, l) -> ImageLoader.Channel.valueOf((String) o))
.registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf((String) o))
.registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf((String) o));
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
public interface LoaderRegistrar {
void register(TypeRegistry registry);
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api;
public interface Player extends Entity {
}

View File

@@ -0,0 +1,40 @@
package com.dfsek.terra.api.language;
import com.dfsek.tectonic.config.Configuration;
import com.dfsek.terra.api.platform.CommandSender;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Language {
private final Configuration configuration;
public Language(File file) throws IOException {
configuration = new Configuration(new FileInputStream(file));
}
@SuppressWarnings("unchecked")
public Message getMessage(String id) {
Message temp = null;
if(configuration.contains(id)) {
Object m = configuration.get(id);
if(m instanceof List) {
temp = new MultiLineMessage((List<String>) m);
} else if(m instanceof String) {
temp = new SingleLineMessage((String) m);
} else return new SingleLineMessage("message:" + id + ":translation_undefined");
}
if(temp == null || temp.isEmpty()) return new SingleLineMessage("message:" + id + ":translation_undefined");
return temp;
}
public void log(String messageID, Level level, Logger logger, String... args) {
getMessage(messageID).log(logger, level, args);
}
public void send(String messageID, CommandSender sender, String... args) {
getMessage(messageID).send(sender, args);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.logging.Level;
import java.util.logging.Logger;
public interface Message {
void log(Logger logger, Level level, String... args);
void send(CommandSender sender, String... args);
boolean isEmpty();
}

View File

@@ -0,0 +1,33 @@
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MultiLineMessage implements Message {
private final List<String> message;
public MultiLineMessage(List<String> message) {
this.message = message;
}
@Override
public void log(Logger logger, Level level, String... args) {
for(String line: message) {
logger.log(level, String.format(line, Arrays.asList(args).toArray()));
}
}
@Override
public void send(CommandSender sender, String... args) {
for(String line: message) {
sender.sendMessage(String.format(line, Arrays.asList(args).toArray()));
}
}
@Override
public boolean isEmpty() {
return message == null || message.isEmpty();
}
}

View File

@@ -0,0 +1,29 @@
package com.dfsek.terra.api.language;
import com.dfsek.terra.api.platform.CommandSender;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SingleLineMessage implements Message {
private final String message;
public SingleLineMessage(String message) {
this.message = message;
}
@Override
public void log(Logger logger, Level level, String... args) {
logger.log(level, String.format(message, Arrays.asList(args).toArray()));
}
@Override
public void send(CommandSender sender, String... args) {
sender.sendMessage(String.format(message, Arrays.asList(args).toArray()));
}
@Override
public boolean isEmpty() {
return message == null || message.equals("");
}
}

View File

@@ -1,9 +1,8 @@
package com.dfsek.terra.procgen;
package com.dfsek.terra.api.math;
import org.bukkit.util.Vector;
import org.polydev.gaea.math.MathUtil;
import org.polydev.gaea.util.FastRandom;
import org.polydev.gaea.util.GlueList;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
import java.util.Random;
@@ -14,12 +13,12 @@ import java.util.Random;
public class GridSpawn {
private final int separation;
private final int width;
private final int seedOffset;
private final int salt;
public GridSpawn(int width, int separation, int seedOffset) {
public GridSpawn(int width, int separation, int salt) {
this.separation = separation;
this.width = width;
this.seedOffset = seedOffset;
this.salt = salt;
}
/**
@@ -30,18 +29,18 @@ public class GridSpawn {
* @param seed Seed for RNG
* @return Vector representing nearest spawnpoint
*/
public Vector getNearestSpawn(int x, int z, long seed) {
public Vector3 getNearestSpawn(int x, int z, long seed) {
int structureChunkX = x / (width + 2 * separation);
int structureChunkZ = z / (width + 2 * separation);
List<Vector> zones = new GlueList<>();
List<Vector3> zones = new GlueList<>();
for(int xi = structureChunkX - 1; xi <= structureChunkX + 1; xi++) {
for(int zi = structureChunkZ - 1; zi <= structureChunkZ + 1; zi++) {
zones.add(getChunkSpawn(xi, zi, seed + seedOffset));
zones.add(getChunkSpawn(xi, zi, seed));
}
}
Vector shortest = zones.get(0);
Vector compare = new Vector(x, 0, z);
for(Vector v : zones) {
Vector3 shortest = zones.get(0);
Vector3 compare = new Vector3(x, 0, z);
for(Vector3 v : zones) {
if(compare.distanceSquared(shortest) > compare.distanceSquared(v)) shortest = v.clone();
}
return shortest;
@@ -55,13 +54,13 @@ public class GridSpawn {
* @param seed Seed for RNG
* @return Vector representing spawnpoint
*/
public Vector getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) {
Random r = new FastRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed + seedOffset));
public Vector3 getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) {
Random r = new FastRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed + salt));
int offsetX = r.nextInt(width);
int offsetZ = r.nextInt(width);
int sx = structureChunkX * (width + 2 * separation) + offsetX;
int sz = structureChunkZ * (width + 2 * separation) + offsetZ;
return new Vector(sx, 0, sz);
return new Vector3(sx, 0, sz);
}
public int getWidth() {

View File

@@ -0,0 +1,97 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.generation.math.Sampler;
import net.jafama.FastMath;
import java.util.List;
import java.util.Random;
/**
* Utility class for mathematical functions.
*/
public final class MathUtil {
/**
* Epsilon for fuzzy floating point comparisons.
*/
public static final double EPSILON = 1.0E-5;
/**
* Derivative constant.
*/
private static final double DERIVATIVE_DIST = 0.55;
/**
* Gets the standard deviation of an array of doubles.
*
* @param numArray The array of numbers to calculate the standard deviation of.
* @return double - The standard deviation.
*/
public static double standardDeviation(List<Number> numArray) {
double sum = 0.0, standardDeviation = 0.0;
int length = numArray.size();
for(Number num : numArray) {
sum += num.doubleValue();
}
double mean = sum / length;
for(Number num : numArray) {
standardDeviation += FastMath.pow2(num.doubleValue() - mean);
}
return FastMath.sqrt(standardDeviation / length);
}
/**
* Gets the carver seed for a chunk.
*
* @param chunkX Chunk's X coordinate
* @param chunkZ Chunk's Z coordinate
* @param seed World seed
* @return long - The carver seed.
*/
public static long getCarverChunkSeed(int chunkX, int chunkZ, long seed) {
Random r = new FastRandom(seed);
return chunkX * r.nextLong() ^ chunkZ * r.nextLong() ^ seed;
}
public static long hashToLong(String s) {
if(s == null) {
return 0;
}
long hash = 0;
for(char c : s.toCharArray()) {
hash = 31L * hash + c;
}
return hash;
}
/**
* Compare 2 floating-point values with epsilon to account for rounding errors
*
* @param a Value 1
* @param b Value 2
* @return Whether these values are equal
*/
public static boolean equals(double a, double b) {
return a == b || FastMath.abs(a - b) < EPSILON;
}
public static double derivative(Sampler sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
public static long squash(int first, int last) {
return (((long) first) << 32) | (last & 0xffffffffL);
}
}

View File

@@ -0,0 +1,50 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.world.biome.NormalizationUtil;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SuppressWarnings("unchecked")
public class ProbabilityCollection<E> {
private final Set<Object> cont = new HashSet<>();
private Object[] array = new Object[0];
private int size;
public com.dfsek.terra.api.math.ProbabilityCollection<E> add(E item, int probability) {
if(!cont.contains(item)) size++;
cont.add(item);
int oldLength = array.length;
Object[] newArray = new Object[array.length + probability];
System.arraycopy(array, 0, newArray, 0, array.length); // Expand array.
array = newArray;
for(int i = oldLength; i < array.length; i++) array[i] = item;
return this;
}
public E get() {
if(array.length == 0) return null;
return (E) array[ThreadLocalRandom.current().nextInt(array.length)];
}
public E get(Random r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
public E get(NoiseSampler n, double x, double z) {
if(array.length == 0) return null;
return (E) array[NormalizationUtil.normalize(n.getNoise(x, z), array.length, 1)];
}
public int getTotalProbability() {
return array.length;
}
public int size() {
return size;
}
}

View File

@@ -0,0 +1,123 @@
package com.dfsek.terra.api.math;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
public class Range implements Iterable<Integer> {
private int min;
private int max;
public Range(int min, int max) {
if(min > max) throw new IllegalArgumentException("Minimum must not be grater than maximum!");
this.max = max;
this.min = min;
}
public boolean isInRange(int test) {
return test >= min && test < max;
}
public int getMax() {
return max;
}
public com.dfsek.terra.api.math.Range setMax(int max) {
this.max = max;
return this;
}
public int getMin() {
return min;
}
public com.dfsek.terra.api.math.Range setMin(int min) {
this.min = min;
return this;
}
public int getRange() {
return max - min;
}
public com.dfsek.terra.api.math.Range multiply(int mult) {
min *= mult;
max *= mult;
return this;
}
public com.dfsek.terra.api.math.Range reflect(int pt) {
return new com.dfsek.terra.api.math.Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
}
public int get(Random r) {
return r.nextInt((max - min) + 1) + min;
}
public com.dfsek.terra.api.math.Range intersects(com.dfsek.terra.api.math.Range other) {
try {
return new com.dfsek.terra.api.math.Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
} catch(IllegalArgumentException e) {
return null;
}
}
public com.dfsek.terra.api.math.Range add(int add) {
this.min += add;
this.max += add;
return this;
}
public com.dfsek.terra.api.math.Range sub(int sub) {
this.min -= sub;
this.max -= sub;
return this;
}
@Override
public String toString() {
return "Min: " + getMin() + ", Max:" + getMax();
}
@Override
public int hashCode() {
return min * 31 + max;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof com.dfsek.terra.api.math.Range)) return false;
com.dfsek.terra.api.math.Range other = (com.dfsek.terra.api.math.Range) obj;
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
}
@NotNull
@Override
public Iterator<Integer> iterator() {
return new RangeIterator(this);
}
private static class RangeIterator implements Iterator<Integer> {
private final com.dfsek.terra.api.math.Range m;
private Integer current;
public RangeIterator(com.dfsek.terra.api.math.Range m) {
this.m = m;
current = m.getMin();
}
@Override
public boolean hasNext() {
return current < m.getMax();
}
@Override
public Integer next() {
current++;
return current - 1;
}
}
}

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.math;
package com.dfsek.terra.api.math.noise;
import parsii.eval.Function;

View File

@@ -1,14 +1,14 @@
package com.dfsek.terra.math;
package com.dfsek.terra.api.math.noise;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.util.hash.HashMapDoubleDouble;
import org.polydev.gaea.math.FastNoiseLite;
import parsii.eval.Expression;
import java.util.List;
public class NoiseFunction2 implements NoiseFunction {
private final FastNoiseLite gen;
private final NoiseSampler gen;
private final Cache cache = new Cache();
public NoiseFunction2(long seed, NoiseBuilder builder) {
@@ -44,7 +44,7 @@ public class NoiseFunction2 implements NoiseFunction {
private static final long serialVersionUID = 8915092734723467010L;
private static final int cacheSize = 384;
public double get(FastNoiseLite noise, double x, double z) {
public double get(NoiseSampler noise, double x, double z) {
double xx = x >= 0 ? x * 2 : x * -2 - 1;
double zz = z >= 0 ? z * 2 : z * -2 - 1;
double key = (xx >= zz) ? (xx * xx + xx + zz) : (zz * zz + xx);

View File

@@ -1,13 +1,13 @@
package com.dfsek.terra.math;
package com.dfsek.terra.api.math.noise;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.generation.config.NoiseBuilder;
import org.polydev.gaea.math.FastNoiseLite;
import parsii.eval.Expression;
import java.util.List;
public class NoiseFunction3 implements NoiseFunction {
private final FastNoiseLite gen;
private final NoiseSampler gen;
public NoiseFunction3(long seed, NoiseBuilder builder) {
this.gen = builder.build((int) seed);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
package com.dfsek.terra.api.math.noise.samplers;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.math.vector.Vector3;
public interface NoiseSampler {
/**
* 2D noise at given position using current settings
* <p>
* Noise output bounded between -1...1
*/
double getNoise(/*FNLdouble*/ double x, /*FNLdouble*/ double y);
/**
* 3D noise at given position using current settings
* <p>
* Noise output bounded between -1...1
*/
double getNoise(/*FNLdouble*/ double x, /*FNLdouble*/ double y, /*FNLdouble*/ double z);
default double getNoise(Vector3 vector3) {
return getNoise(vector3.getX(), vector3.getY(), vector3.getZ());
}
default double getNoise(Vector2 vector2) {
return getNoise(vector2.getX(), vector2.getZ());
}
}

View File

@@ -0,0 +1,21 @@
package com.dfsek.terra.api.math.noise.samplers;
public abstract class Normalizer implements NoiseSampler {
private final NoiseSampler sampler;
protected Normalizer(NoiseSampler sampler) {
this.sampler = sampler;
}
public abstract double normalize(double in);
@Override
public double getNoise(double x, double y) {
return normalize(sampler.getNoise(x, y));
}
@Override
public double getNoise(double x, double y, double z) {
return normalize(sampler.getNoise(x, y, z));
}
}

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.math;
package com.dfsek.terra.api.math.parsii;
import parsii.eval.Expression;
import parsii.eval.Function;

View File

@@ -1,13 +1,13 @@
package com.dfsek.terra.math;
package com.dfsek.terra.api.math.parsii;
import org.polydev.gaea.util.FastRandom;
import com.dfsek.terra.api.util.FastRandom;
import parsii.eval.Expression;
import parsii.eval.Function;
import java.util.List;
/**
* Provides access to a PRNG ({@link org.polydev.gaea.util.FastRandom})
* Provides access to a PRNG ({@link com.dfsek.terra.api.util.FastRandom})
* <p>
* Takes 1 argument, which sets the seed
*/

View File

@@ -1,4 +1,4 @@
package com.dfsek.terra.procgen.pixel;
package com.dfsek.terra.api.math.pixel;
public class Distribution {
public Distribution(Rectangle bound, int numPoints, double minRad) {

View File

@@ -1,6 +1,6 @@
package com.dfsek.terra.procgen.pixel;
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.procgen.math.Vector2;
import com.dfsek.terra.api.math.vector.Vector2;
import java.util.Set;

View File

@@ -1,6 +1,6 @@
package com.dfsek.terra.procgen.pixel;
package com.dfsek.terra.api.math.pixel;
import com.dfsek.terra.procgen.math.Vector2;
import com.dfsek.terra.api.math.vector.Vector2;
import net.jafama.FastMath;
import java.util.HashSet;

View File

@@ -0,0 +1,173 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.world.World;
import java.util.Objects;
public class Location implements Cloneable {
private World world;
private Vector3 vector;
private double pitch;
private double yaw;
public Location(World w, double x, double y, double z) {
this.world = w;
this.vector = new Vector3(x, y, z);
}
public Location(World w, Vector3 vector) {
this.world = w;
this.vector = vector;
}
public void setWorld(World world) {
this.world = world;
}
public Vector3 getVector() {
return vector;
}
public void setVector(Vector3 vector) {
this.vector = vector;
}
@Override
public Location clone() {
try {
Location other = (Location) super.clone();
other.setVector(other.getVector().clone());
return other;
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public int getBlockX() {
return vector.getBlockX();
}
public int getBlockY() {
return vector.getBlockY();
}
public int getBlockZ() {
return vector.getBlockZ();
}
public double getY() {
return vector.getY();
}
public Location setY(double y) {
vector.setY(y);
return this;
}
public double getX() {
return vector.getX();
}
public Location setX(double x) {
vector.setX(x);
return this;
}
public double getZ() {
return vector.getZ();
}
public Location setZ(double z) {
vector.setZ(z);
return this;
}
public World getWorld() {
return world;
}
public Location add(double x, double y, double z) {
vector.add(x, y, z);
return this;
}
public Block getBlock() {
return world.getBlockAt(this);
}
public Location subtract(int x, int y, int z) {
vector.subtract(x, y, z);
return this;
}
public Location add(Vector3 add) {
vector.add(add);
return this;
}
public Location add(Location add) {
vector.add(add.toVector());
return this;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Location)) {
return false;
}
final Location other = (Location) obj;
World world = this.world;
World otherWorld = other.world;
if(!Objects.equals(world, otherWorld)) {
return false;
}
if(Double.doubleToLongBits(this.vector.getX()) != Double.doubleToLongBits(other.vector.getX())) {
return false;
}
if(Double.doubleToLongBits(this.vector.getY()) != Double.doubleToLongBits(other.vector.getY())) {
return false;
}
return Double.doubleToLongBits(this.vector.getZ()) == Double.doubleToLongBits(other.vector.getZ());
}
public double getPitch() {
return pitch;
}
public void setPitch(double pitch) {
this.pitch = pitch;
}
public double getYaw() {
return yaw;
}
public void setYaw(double yaw) {
this.yaw = yaw;
}
@Override
public int hashCode() {
int hash = 3;
World world = (this.world == null) ? null : this.world;
hash = 19 * hash + (world != null ? world.hashCode() : 0);
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getX()) ^ (Double.doubleToLongBits(this.vector.getX()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getY()) ^ (Double.doubleToLongBits(this.vector.getY()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.vector.getZ()) ^ (Double.doubleToLongBits(this.vector.getZ()) >>> 32));
hash = 19 * hash + (int) (Double.doubleToLongBits(this.pitch) ^ Double.doubleToLongBits(this.pitch) >>> 32);
hash = 19 * hash + (int) (Double.doubleToLongBits(this.yaw) ^ Double.doubleToLongBits(this.yaw) >>> 32);
return hash;
}
public Vector3 toVector() {
return vector.clone();
}
@Override
public String toString() {
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
}
}

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra.procgen.math;
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import net.jafama.FastMath;
/**
@@ -157,11 +158,9 @@ public class Vector2 implements Cloneable {
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector2)) {
return false;
}
if(!(obj instanceof Vector2)) return false;
Vector2 other = (Vector2) obj;
return other.x == this.x && other.z == this.z;
return MathUtil.equals(this.x, other.x) && MathUtil.equals(this.z, other.z);
}
@Override
@@ -173,6 +172,12 @@ public class Vector2 implements Cloneable {
}
}
public Vector2 add(double x, double z) {
this.x += x;
this.z += z;
return this;
}
@Override
public String toString() {
return "(" + x + ", " + z + ")";

View File

@@ -0,0 +1,338 @@
package com.dfsek.terra.api.math.vector;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.platform.world.World;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
public class Vector3 implements Cloneable {
private double x;
private double y;
private double z;
public Vector3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public double getZ() {
return z;
}
public Vector3 setZ(double z) {
this.z = z;
return this;
}
public double getX() {
return x;
}
public Vector3 setX(double x) {
this.x = x;
return this;
}
public double getY() {
return y;
}
public Vector3 setY(double y) {
this.y = y;
return this;
}
public int getBlockX() {
return FastMath.floorToInt(x);
}
public int getBlockY() {
return FastMath.floorToInt(y);
}
public int getBlockZ() {
return FastMath.floorToInt(z);
}
public Vector3 multiply(double m) {
x *= m;
y *= m;
z *= m;
return this;
}
public Vector3 add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
public Vector3 add(Vector3 other) {
this.x += other.getX();
this.y += other.getY();
this.z += other.getZ();
return this;
}
public Vector3 add(Vector2 other) {
this.x += other.getX();
this.z += other.getZ();
return this;
}
public double lengthSquared() {
return x * x + y * y + z * z;
}
@Override
public Vector3 clone() {
try {
return (Vector3) super.clone();
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public double length() {
return FastMath.sqrt(lengthSquared());
}
public double inverseLength() {
return FastMath.invSqrtQuick(lengthSquared());
}
/**
* Returns if a vector is normalized
*
* @return whether the vector is normalised
*/
public boolean isNormalized() {
return MathUtil.equals(this.lengthSquared(), 1);
}
/**
* Rotates the vector around the x axis.
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundX(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double y = angleCos * getY() - angleSin * getZ();
double z = angleSin * getY() + angleCos * getZ();
return setY(y).setZ(z);
}
/**
* Rotates the vector around the y axis.
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundY(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double x = angleCos * getX() + angleSin * getZ();
double z = -angleSin * getX() + angleCos * getZ();
return setX(x).setZ(z);
}
/**
* Rotates the vector around the z axis
* <p>
* This piece of math is based on the standard rotation matrix for vectors
* in three dimensional space. This matrix can be found here:
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
* Matrix</a>.
*
* @param angle the angle to rotate the vector about. This angle is passed
* in radians
* @return the same vector
*/
@NotNull
public Vector3 rotateAroundZ(double angle) {
double angleCos = Math.cos(angle);
double angleSin = Math.sin(angle);
double x = angleCos * getX() - angleSin * getY();
double y = angleSin * getX() + angleCos * getY();
return setX(x).setY(y);
}
/**
* Get the distance between this vector and another. The value of this
* method is not cached and uses a costly square-root function, so do not
* repeatedly call this method to get the vector's magnitude. NaN will be
* returned if the inner result of the sqrt() function overflows, which
* will be caused if the distance is too long.
*
* @param o The other vector
* @return the distance
*/
public double distance(@NotNull Vector3 o) {
return FastMath.sqrt(FastMath.pow2(x - o.x) + FastMath.pow2(y - o.y) + FastMath.pow2(z - o.z));
}
/**
* Get the squared distance between this vector and another.
*
* @param o The other vector
* @return the distance
*/
public double distanceSquared(@NotNull Vector3 o) {
return FastMath.pow2(x - o.x) + FastMath.pow2(y - o.y) + FastMath.pow2(z - o.z);
}
/**
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
*
* <p>
* Rotation will follow the general Right-Hand-Rule, which means rotation
* will be counterclockwise when the axis is pointing towards the observer.
* <p>
* This method will always make sure the provided axis is a unit vector, to
* not modify the length of the vector when rotating.
*
* @param axis the axis to rotate the vector around. If the passed vector is
* not of length 1, it gets copied and normalized before using it for the
* rotation. Please use {@link Vector3#normalize()} on the instance before
* passing it to this method
* @param angle the angle to rotate the vector around the axis
* @return the same vector
* @throws IllegalArgumentException if the provided axis vector instance is
* null
*/
@NotNull
public Vector3 rotateAroundAxis(@NotNull Vector3 axis, double angle) throws IllegalArgumentException {
return rotateAroundNonUnitAxis(axis.isNormalized() ? axis : axis.clone().normalize(), angle);
}
/**
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
*
* <p>
* Rotation will follow the general Right-Hand-Rule, which means rotation
* will be counterclockwise when the axis is pointing towards the observer.
* <p>
* Note that the vector length will change accordingly to the axis vector
* length. If the provided axis is not a unit vector, the rotated vector
* will not have its previous length. The scaled length of the resulting
* vector will be related to the axis vector.
*
* @param axis the axis to rotate the vector around.
* @param angle the angle to rotate the vector around the axis
* @return the same vector
* @throws IllegalArgumentException if the provided axis vector instance is
* null
*/
@NotNull
public Vector3 rotateAroundNonUnitAxis(@NotNull Vector3 axis, double angle) throws IllegalArgumentException {
double x = getX(), y = getY(), z = getZ();
double x2 = axis.getX(), y2 = axis.getY(), z2 = axis.getZ();
double cosTheta = Math.cos(angle);
double sinTheta = Math.sin(angle);
double dotProduct = this.dot(axis);
double xPrime = x2 * dotProduct * (1d - cosTheta)
+ x * cosTheta
+ (-z2 * y + y2 * z) * sinTheta;
double yPrime = y2 * dotProduct * (1d - cosTheta)
+ y * cosTheta
+ (z2 * x - x2 * z) * sinTheta;
double zPrime = z2 * dotProduct * (1d - cosTheta)
+ z * cosTheta
+ (-y2 * x + x2 * y) * sinTheta;
return setX(xPrime).setY(yPrime).setZ(zPrime);
}
/**
* Calculates the dot product of this vector with another. The dot product
* is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar.
*
* @param other The other vector
* @return dot product
*/
public double dot(@NotNull Vector3 other) {
return x * other.x + y * other.y + z * other.z;
}
public Location toLocation(World world) {
return new Location(world, this.clone());
}
public Vector3 normalize() {
return this.multiply(this.inverseLength());
}
public Vector3 subtract(int x, int y, int z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
}
public Vector3 subtract(Vector3 end) {
x -= end.x;
y -= end.y;
z -= end.z;
return this;
}
/**
* Returns a hash code for this vector
*
* @return hash code
*/
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
return hash;
}
/**
* Checks to see if two objects are equal.
* <p>
* Only two Vectors can ever return true. This method uses a fuzzy match
* to account for floating point errors. The epsilon can be retrieved
* with epsilon.
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector3)) return false;
Vector3 other = (Vector3) obj;
return MathUtil.equals(x, other.x) && MathUtil.equals(y, other.y) && MathUtil.equals(x, other.z);
}
@Override
public String toString() {
return "(" + getX() + ", " + getY() + ", " + getZ() + ")";
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Cylinder extends VoxelGeometry {
public Cylinder(Vector3 start, int rad, int height) {
}
}

View File

@@ -1,14 +1,14 @@
package com.dfsek.terra.procgen.voxel;
package com.dfsek.terra.api.math.voxel;
import org.bukkit.util.Vector;
import org.polydev.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.math.vector.Vector3;
public class DeformedSphere extends VoxelGeometry {
public DeformedSphere(Vector start, int rad, double deform, FastNoiseLite noise) {
public DeformedSphere(Vector3 start, int rad, double deform, NoiseSampler noise) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector c = new Vector(x, y, z);
Vector3 c = new Vector3(x, y, z);
if(c.length() < (rad + 0.5) * ((noise.getNoise(x, y, z) + 1) * deform)) {
addVector(c.add(start));
}

View File

@@ -1,13 +1,13 @@
package com.dfsek.terra.procgen.voxel;
package com.dfsek.terra.api.math.voxel;
import org.bukkit.util.Vector;
import com.dfsek.terra.api.math.vector.Vector3;
public class Sphere extends VoxelGeometry {
public Sphere(Vector start, int rad) {
public Sphere(Vector3 start, int rad) {
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector c = new Vector(x, y, z);
Vector3 c = new Vector3(x, y, z);
if(c.length() < rad + 0.5) {
addVector(c.add(start));
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.math.voxel;
import com.dfsek.terra.api.math.vector.Vector3;
public class Tube extends VoxelGeometry {
public Tube(Vector3 start, Vector3 end, int radius) {
Vector3 step = start.clone().subtract(end).normalize();
Vector3 run = start.clone();
int steps = (int) start.distance(end);
for(int i = 0; i < steps; i++) {
merge(new Sphere(run, radius));
run.add(step);
}
}
}

View File

@@ -1,23 +1,23 @@
package com.dfsek.terra.procgen.voxel;
package com.dfsek.terra.api.math.voxel;
import org.bukkit.util.Vector;
import org.polydev.gaea.util.GlueList;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
public abstract class VoxelGeometry {
private final List<Vector> geometry = new GlueList<>();
private final List<Vector3> geometry = new GlueList<>();
public static VoxelGeometry getBlank() {
return new VoxelGeometry() {
};
}
public List<Vector> getGeometry() {
public List<Vector3> getGeometry() {
return geometry;
}
protected void addVector(Vector v) {
protected void addVector(Vector3 v) {
geometry.add(v);
}

View File

@@ -0,0 +1,5 @@
package com.dfsek.terra.api.platform;
public interface CommandSender extends Handle {
void sendMessage(String message);
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api.platform;
/**
* An interface that contains a platform delegate.
*/
public interface Handle {
Object getHandle();
}

View File

@@ -0,0 +1,41 @@
package com.dfsek.terra.api.platform;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.LoaderRegistrar;
import com.dfsek.terra.api.language.Language;
import com.dfsek.terra.api.platform.handle.ItemHandle;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.config.base.PluginConfig;
import com.dfsek.terra.registry.ConfigRegistry;
import java.io.File;
import java.util.logging.Logger;
public interface TerraPlugin extends LoaderRegistrar {
WorldHandle getWorldHandle();
boolean isEnabled();
TerraWorld getWorld(World world);
Logger getLogger();
PluginConfig getTerraConfig();
File getDataFolder();
boolean isDebug();
Language getLanguage();
ConfigRegistry getRegistry();
void reload();
ItemHandle getItemHandle();
void saveDefaultConfig();
String platformName();
}

View File

@@ -0,0 +1,5 @@
package com.dfsek.terra.api.platform.block;
public enum Axis {
X, Y, Z
}

View File

@@ -0,0 +1,31 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.state.BlockState;
public interface Block extends Handle {
void setBlockData(BlockData data, boolean physics);
BlockData getBlockData();
BlockState getState();
Block getRelative(BlockFace face);
Block getRelative(BlockFace face, int len);
boolean isEmpty();
Location getLocation();
MaterialData getType();
int getX();
int getZ();
int getY();
boolean isPassable();
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.platform.Handle;
public interface BlockData extends Cloneable, Handle {
MaterialData getMaterial();
boolean matches(MaterialData materialData);
BlockData clone();
String getAsString();
}

View File

@@ -0,0 +1,148 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.math.vector.Vector3;
import org.jetbrains.annotations.NotNull;
public enum BlockFace {
NORTH(0, 0, -1),
EAST(1, 0, 0),
SOUTH(0, 0, 1),
WEST(-1, 0, 0),
UP(0, 1, 0),
DOWN(0, -1, 0),
NORTH_EAST(NORTH, EAST),
NORTH_WEST(NORTH, WEST),
SOUTH_EAST(SOUTH, EAST),
SOUTH_WEST(SOUTH, WEST),
WEST_NORTH_WEST(WEST, NORTH_WEST),
NORTH_NORTH_WEST(NORTH, NORTH_WEST),
NORTH_NORTH_EAST(NORTH, NORTH_EAST),
EAST_NORTH_EAST(EAST, NORTH_EAST),
EAST_SOUTH_EAST(EAST, SOUTH_EAST),
SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST),
SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST),
WEST_SOUTH_WEST(WEST, SOUTH_WEST),
SELF(0, 0, 0);
private final int modX;
private final int modY;
private final int modZ;
BlockFace(final int modX, final int modY, final int modZ) {
this.modX = modX;
this.modY = modY;
this.modZ = modZ;
}
BlockFace(final BlockFace face1, final BlockFace face2) {
this.modX = face1.getModX() + face2.getModX();
this.modY = face1.getModY() + face2.getModY();
this.modZ = face1.getModZ() + face2.getModZ();
}
/**
* Get the amount of X-coordinates to modify to get the represented block
*
* @return Amount of X-coordinates to modify
*/
public int getModX() {
return modX;
}
/**
* Get the amount of Y-coordinates to modify to get the represented block
*
* @return Amount of Y-coordinates to modify
*/
public int getModY() {
return modY;
}
/**
* Get the amount of Z-coordinates to modify to get the represented block
*
* @return Amount of Z-coordinates to modify
*/
public int getModZ() {
return modZ;
}
/**
* Gets the normal vector corresponding to this block face.
*
* @return the normal vector
*/
@NotNull
public Vector3 getDirection() {
Vector3 direction = new Vector3(modX, modY, modZ);
if(modX != 0 || modY != 0 || modZ != 0) {
direction.normalize();
}
return direction;
}
@NotNull
public BlockFace getOppositeFace() {
switch(this) {
case NORTH:
return BlockFace.SOUTH;
case SOUTH:
return BlockFace.NORTH;
case EAST:
return BlockFace.WEST;
case WEST:
return BlockFace.EAST;
case UP:
return BlockFace.DOWN;
case DOWN:
return BlockFace.UP;
case NORTH_EAST:
return BlockFace.SOUTH_WEST;
case NORTH_WEST:
return BlockFace.SOUTH_EAST;
case SOUTH_EAST:
return BlockFace.NORTH_WEST;
case SOUTH_WEST:
return BlockFace.NORTH_EAST;
case WEST_NORTH_WEST:
return BlockFace.EAST_SOUTH_EAST;
case NORTH_NORTH_WEST:
return BlockFace.SOUTH_SOUTH_EAST;
case NORTH_NORTH_EAST:
return BlockFace.SOUTH_SOUTH_WEST;
case EAST_NORTH_EAST:
return BlockFace.WEST_SOUTH_WEST;
case EAST_SOUTH_EAST:
return BlockFace.WEST_NORTH_WEST;
case SOUTH_SOUTH_EAST:
return BlockFace.NORTH_NORTH_WEST;
case SOUTH_SOUTH_WEST:
return BlockFace.NORTH_NORTH_EAST;
case WEST_SOUTH_WEST:
return BlockFace.EAST_NORTH_EAST;
case SELF:
return BlockFace.SELF;
}
return BlockFace.SELF;
}
}

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.api.platform.block;
import com.dfsek.terra.api.platform.Handle;
public interface MaterialData extends Handle {
boolean matches(MaterialData other);
boolean matches(BlockData other);
boolean isSolid();
boolean isAir();
double getMaxDurability();
BlockData createBlockData();
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
public interface AnaloguePowerable extends BlockData {
int getMaximumPower();
int getPower();
void setPower(int power);
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
public interface Bisected extends BlockData {
Half getHalf();
void setHalf(Half half);
enum Half {
/**
* The top half of the block, normally with the higher y coordinate.
*/
TOP,
/**
* The bottom half of the block, normally with the lower y coordinate.
*/
BOTTOM
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
public interface Directional extends BlockData {
BlockFace getFacing();
void setFacing(BlockFace facing);
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
import java.util.Set;
public interface MultipleFacing extends BlockData {
Set<BlockFace> getFaces();
void setFace(BlockFace face, boolean facing);
Set<BlockFace> getAllowedFaces();
boolean hasFace(BlockFace f);
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.Axis;
import com.dfsek.terra.api.platform.block.BlockData;
import java.util.Set;
public interface Orientable extends BlockData {
Set<Axis> getAxes();
Axis getAxis();
void setAxis(Axis axis);
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
public interface Rail extends BlockData {
Shape getShape();
void setShape(Shape newShape);
enum Shape {
ASCENDING_EAST,
ASCENDING_NORTH,
ASCENDING_SOUTH,
ASCENDING_WEST,
EAST_WEST,
NORTH_EAST,
NORTH_SOUTH,
NORTH_WEST,
SOUTH_EAST,
SOUTH_WEST
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
import java.util.Set;
public interface RedstoneWire extends BlockData, AnaloguePowerable {
Set<BlockFace> getAllowedFaces();
Connection getFace(BlockFace face);
void setFace(BlockFace face, Connection connection);
enum Connection {
NONE, SIDE, UP
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
public interface Rotatable extends BlockData {
BlockFace getRotation();
void setRotation(BlockFace face);
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.block.data;
public interface Slab extends Waterlogged {
Type getType();
void setType(Type type);
enum Type {
TOP, BOTTOM, DOUBLE
}
}

View File

@@ -0,0 +1,30 @@
package com.dfsek.terra.api.platform.block.data;
public interface Stairs extends Waterlogged, Directional, Bisected {
Shape getShape();
void setShape(Shape shape);
enum Shape {
/**
* Regular stair block.
*/
STRAIGHT,
/**
* Inner corner stair block with higher left side.
*/
INNER_LEFT,
/**
* Inner corner stair block with higher right side.
*/
INNER_RIGHT,
/**
* Outer corner stair block with higher left side.
*/
OUTER_LEFT,
/**
* Outer corner stair block with higher right side.
*/
OUTER_RIGHT
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
public interface Wall extends BlockData, Waterlogged {
boolean isUp();
void setHeight(BlockFace face, Height height);
Height getHeight(BlockFace face);
void setUp(boolean up);
enum Height {
LOW, NONE, TALL
}
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.platform.block.data;
import com.dfsek.terra.api.platform.block.BlockData;
public interface Waterlogged extends BlockData {
boolean isWaterlogged();
void setWaterlogged(boolean waterlogged);
}

View File

@@ -0,0 +1,23 @@
package com.dfsek.terra.api.platform.block.state;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
public interface BlockState extends Handle {
Block getBlock();
int getX();
int getY();
int getZ();
BlockData getBlockData();
boolean update(boolean applyPhysics);
default void applyState(String state) {
// Do nothing by default.
}
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.block.state;
import com.dfsek.terra.api.platform.inventory.BlockInventoryHolder;
public interface Container extends BlockState, BlockInventoryHolder {
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.platform.block.state;
import com.dfsek.terra.api.platform.world.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public interface MobSpawner extends BlockState {
EntityType getSpawnedType();
void setSpawnedType(@NotNull EntityType creatureType);
int getDelay();
void setDelay(int delay);
int getMinSpawnDelay();
void setMinSpawnDelay(int delay);
int getMaxSpawnDelay();
void setMaxSpawnDelay(int delay);
int getSpawnCount();
void setSpawnCount(int spawnCount);
int getMaxNearbyEntities();
void setMaxNearbyEntities(int maxNearbyEntities);
int getRequiredPlayerRange();
void setRequiredPlayerRange(int requiredPlayerRange);
int getSpawnRange();
void setSpawnRange(int spawnRange);
}

View File

@@ -0,0 +1,94 @@
package com.dfsek.terra.api.platform.block.state;
import java.util.HashMap;
import java.util.Map;
public class SerialState {
protected final Map<String, Property<?>> properties = new HashMap<>();
public SerialState() {
}
public static Map<String, String> parse(String props) {
String[] sep = props.split(",");
Map<String, String> map = new HashMap<>();
for(String item : sep) {
map.put(item.substring(0, item.indexOf('=')), item.substring(item.indexOf('=') + 1));
}
return map;
}
private void checkExists(String prop) {
if(!properties.containsKey(prop)) throw new IllegalArgumentException("No such property \"" + prop + "\"");
}
private void checkType(Class<?> clazz, Object o, String id) {
if(!clazz.isInstance(o))
throw new IllegalArgumentException("Invalid data for property " + id + ": " + o);
}
public void setProperty(String id, Object value) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(prop.getValueClass(), value, id);
prop.setValue(value);
}
public int getInteger(String id) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(Integer.class, prop.getValue(), id);
return (Integer) prop.getValue();
}
public String getString(String id) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(String.class, prop.getValue(), id);
return (String) prop.getValue();
}
public long getLong(String id) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(Long.class, prop.getValue(), id);
return (Long) prop.getValue();
}
public boolean getBoolean(String id) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(Boolean.class, prop.getValue(), id);
return (Boolean) prop.getValue();
}
@SuppressWarnings("unchecked")
public <T> T get(String id, Class<T> clazz) {
checkExists(id);
Property<?> prop = properties.get(id);
checkType(clazz, prop.getValue(), id);
return (T) prop.getValue();
}
protected static class Property<T> {
private final Class<T> clazz;
private Object value;
public Property(Class<T> clazz) {
this.clazz = clazz;
}
public Class<T> getValueClass() {
return clazz;
}
@SuppressWarnings("unchecked")
public T getValue() {
return (T) value;
}
public void setValue(Object value) {
this.value = value;
}
}
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.block.state;
import org.jetbrains.annotations.NotNull;
public interface Sign extends BlockState {
@NotNull String[] getLines();
@NotNull String getLine(int index) throws IndexOutOfBoundsException;
void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException;
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.generator;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.World;
import java.util.Random;
public interface BlockPopulator extends Handle {
void populate(World world, Random random, Chunk chunk);
}

View File

@@ -0,0 +1,69 @@
package com.dfsek.terra.api.platform.generator;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.world.BiomeGrid;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Random;
public interface ChunkGenerator extends Handle {
boolean isParallelCapable();
boolean shouldGenerateCaves();
boolean shouldGenerateDecorations();
boolean shouldGenerateMobs();
boolean shouldGenerateStructures();
ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome);
List<BlockPopulator> getDefaultPopulators(World world);
@Nullable
TerraChunkGenerator getTerraGenerator();
interface ChunkData extends Handle {
/**
* Get the maximum height for the chunk.
* <p>
* Setting blocks at or above this height will do nothing.
*
* @return the maximum height
*/
int getMaxHeight();
/**
* Set the block at x,y,z in the chunk data to material.
* <p>
* Setting blocks outside the chunk's bounds does nothing.
*
* @param x the x location in the chunk from 0-15 inclusive
* @param y the y location in the chunk from 0 (inclusive) - maxHeight (exclusive)
* @param z the z location in the chunk from 0-15 inclusive
* @param blockData the type to set the block to
*/
void setBlock(int x, int y, int z, @NotNull BlockData blockData);
/**
* Get the type and data of the block at x, y, z.
* <p>
* Getting blocks outside the chunk's bounds returns air.
*
* @param x the x location in the chunk from 0-15 inclusive
* @param y the y location in the chunk from 0 (inclusive) - maxHeight (exclusive)
* @param z the z location in the chunk from 0-15 inclusive
* @return the data of the block or the BlockData for air if x, y or z are outside the chunk's bounds
*/
@NotNull BlockData getBlockData(int x, int y, int z);
}
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.generator;
import com.dfsek.terra.api.platform.Handle;
public interface GeneratorWrapper extends Handle {
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.platform.handle;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
import java.util.Set;
public interface ItemHandle {
ItemStack newItemStack(MaterialData material, int amount);
Enchantment getEnchantment(String id);
Set<Enchantment> getEnchantments();
}

View File

@@ -0,0 +1,26 @@
package com.dfsek.terra.api.platform.handle;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.world.Tree;
import com.dfsek.terra.api.platform.world.entity.EntityType;
/**
* Interface to be implemented for world manipulation.
*/
public interface WorldHandle {
void setBlockData(Block block, BlockData data, boolean physics);
BlockData getBlockData(Block block);
MaterialData getType(Block block);
BlockData createBlockData(String data);
MaterialData createMaterialData(String data);
Tree getTree(String id);
EntityType getEntity(String id);
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.block.Block;
public interface BlockInventoryHolder extends InventoryHolder {
Block getBlock();
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle;
public interface Inventory extends Handle {
int getSize();
ItemStack getItem(int slot);
void setItem(int slot, ItemStack newStack);
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle;
public interface InventoryHolder extends Handle {
Inventory getInventory();
}

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
public interface ItemStack extends Handle, Cloneable {
int getAmount();
void setAmount(int i);
MaterialData getType();
ItemStack clone();
ItemMeta getItemMeta();
void setItemMeta(ItemMeta meta);
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.inventory.item;
import com.dfsek.terra.api.platform.Handle;
public interface Damageable extends Handle {
int getDamage();
void setDamage(int damage);
boolean hasDamage();
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.platform.inventory.item;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.inventory.ItemStack;
public interface Enchantment extends Handle {
boolean canEnchantItem(ItemStack itemStack);
String getID();
boolean conflictsWith(Enchantment other);
int getMaxLevel();
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.platform.inventory.item;
import com.dfsek.terra.api.platform.Handle;
import java.util.Map;
public interface ItemMeta extends Handle {
Map<Enchantment, Integer> getEnchantments();
void addEnchantment(Enchantment enchantment, int level);
}

View File

@@ -0,0 +1,4 @@
/**
* API for platform implementations. Mostly interfaces to be implemented by platform delegates.
*/
package com.dfsek.terra.api.platform;

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.platform.Handle;
public interface Biome extends Handle {
}

View File

@@ -0,0 +1,46 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.platform.Handle;
import org.jetbrains.annotations.NotNull;
public interface BiomeGrid extends Handle {
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @return Biome value
*/
@NotNull
Biome getBiome(int x, int z);
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @return Biome value
*/
@NotNull
Biome getBiome(int x, int y, int z);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @param bio - Biome value
*/
void setBiome(int x, int z, @NotNull Biome bio);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @param bio - Biome value
*/
void setBiome(int x, int y, int z, @NotNull Biome bio);
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.Block;
public interface Chunk extends Handle {
int getX();
int getZ();
World getWorld();
Block getBlock(int x, int y, int z);
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.platform.Handle;
public interface Tree extends Handle, com.dfsek.terra.api.world.tree.Tree {
}

View File

@@ -0,0 +1,37 @@
package com.dfsek.terra.api.platform.world;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.generator.ChunkGenerator;
import com.dfsek.terra.api.platform.world.entity.Entity;
import com.dfsek.terra.api.platform.world.entity.EntityType;
import java.io.File;
import java.util.UUID;
public interface World extends Handle {
long getSeed();
int getMaxHeight();
ChunkGenerator getGenerator();
String getName();
UUID getUID();
boolean isChunkGenerated(int x, int z);
Chunk getChunkAt(int x, int z);
File getWorldFolder();
Block getBlockAt(int x, int y, int z);
Block getBlockAt(Location l);
boolean generateTree(Location l, Tree vanillaTreeType);
Entity spawnEntity(Location location, EntityType entityType);
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.world.entity;
import com.dfsek.terra.api.platform.Handle;
public interface Entity extends Handle {
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.platform.world.entity;
import com.dfsek.terra.api.platform.Handle;
public interface EntityType extends Handle {
}

View File

@@ -0,0 +1,36 @@
package com.dfsek.terra.api.profiler;
/**
* Class to hold a profiler data value. Contains formatting method to highlight value based on desired range.
*/
public class DataHolder {
private final long desired;
private final DataType type;
private final double desiredRangePercent;
/**
* Constructs a DataHolder with a DataType and a desired value, including a percentage around the desired value considered acceptable
*
* @param type The type of data held in this instance.
* @param desired The desired value. This should be the average value of whatever is being measured.
* @param desiredRangePercent The percentage around the desired value to be considered acceptable.
*/
public DataHolder(DataType type, long desired, double desiredRangePercent) {
this.desired = desired;
this.type = type;
this.desiredRangePercent = desiredRangePercent;
}
/**
* Returns a String, formatted with Bungee ChatColors.<br>
* GREEN if the value is better than desired and outside of acceptable range.<br>
* YELLOW if the value is better or worse than desired, and within acceptable range.<br>
* RED if the value is worse than desired and outside of acceptable range.<br>
*
* @param data The data to format.
* @return String - The formatted data.
*/
public String getFormattedData(long data) {
return type.getFormatted(data);
}
}

View File

@@ -0,0 +1,24 @@
package com.dfsek.terra.api.profiler;
import net.jafama.FastMath;
public enum DataType {
PERIOD_MILLISECONDS(Desire.LOW, 1000000, "ms"), PERIOD_NANOSECONDS(Desire.LOW, 1, "ns");
private final Desire desire;
private final long divisor;
private final String unit;
DataType(Desire d, long divisor, String unit) {
this.desire = d;
this.divisor = divisor;
this.unit = unit;
}
public String getFormatted(long value) {
return (double) FastMath.round(((double) value / divisor) * 100D) / 100D + unit;
}
public Desire getDesire() {
return desire;
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.profiler;
/**
* Enum to represent the "goal" of a value, whether it is desirable for the value to be high (e.g. Frequency), or low (e.g. Period)
*/
public enum Desire {
LOW, HIGH
}

View File

@@ -0,0 +1,87 @@
package com.dfsek.terra.api.profiler;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.util.GlueList;
import net.jafama.FastMath;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
/**
* Class to record and hold all data for a single type of measurement performed by the profiler.
*/
public class Measurement {
private final List<Long> measurements = new LinkedList<>();
private final long desirable;
private final DataType type;
private long min = Long.MAX_VALUE;
private long max = Long.MIN_VALUE;
/**
* Constructs a new Measurement with a desired value and DataType.
*
* @param desirable The desired value of the measurement.
* @param type The type of data the measurement is holding.
*/
public Measurement(long desirable, DataType type) {
this.desirable = desirable;
this.type = type;
}
public void record(long value) {
max = FastMath.max(value, max);
min = FastMath.min(value, min);
measurements.add(value);
}
public int size() {
return measurements.size();
}
public ProfileFuture beginMeasurement() {
ProfileFuture future = new ProfileFuture();
long current = System.nanoTime();
future.thenRun(() -> record(System.nanoTime() - current));
return future;
}
public void reset() {
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
measurements.clear();
}
public DataHolder getDataHolder() {
return new DataHolder(type, desirable, 0.25);
}
public long getMin() {
if(min == Long.MAX_VALUE) return 0;
return min;
}
public long getMax() {
if(max == Long.MIN_VALUE) return 0;
return max;
}
public long average() {
BigInteger running = BigInteger.valueOf(0);
List<Long> mTemp = new GlueList<>(measurements);
for(Long l : mTemp) {
running = running.add(BigInteger.valueOf(l));
}
if(measurements.size() == 0) return 0;
return running.divide(BigInteger.valueOf(measurements.size())).longValue();
}
public double getStdDev() {
return MathUtil.standardDeviation(new GlueList<>(measurements));
}
public int entries() {
return measurements.size();
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.profiler;
import java.util.concurrent.CompletableFuture;
public class ProfileFuture extends CompletableFuture<Boolean> implements AutoCloseable {
public ProfileFuture() {
super();
}
public boolean complete() {
return super.complete(true);
}
@Override
public void close() {
this.complete();
}
}

View File

@@ -0,0 +1,78 @@
package com.dfsek.terra.api.profiler;
import com.dfsek.terra.api.platform.world.World;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jafama.FastMath;
import java.util.Map;
public class WorldProfiler {
private final BiMap<String, Measurement> measures = HashBiMap.create();
private final World world;
private boolean isProfiling;
public WorldProfiler(World w) {
if(w.getGenerator().getTerraGenerator() == null)
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Gaea managed world!");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime");
isProfiling = false;
this.world = w;
}
public String getResultsFormatted() {
if(! isProfiling) return "Profiler is not currently running.";
StringBuilder result = new StringBuilder("Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n");
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
result
.append(e.getKey())
.append(": ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().average()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax()))
.append(" / ")
.append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D)
.append("ms")
.append(" (x").append(e.getValue().size()).append(")\n");
}
return result.toString();
}
public void reset() {
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
e.getValue().reset();
}
}
public com.dfsek.terra.api.profiler.WorldProfiler addMeasurement(Measurement m, String name) {
measures.put(name, m);
return this;
}
public void setMeasurement(String id, long value) {
if(isProfiling) measures.get(id).record(value);
}
public ProfileFuture measure(String id) {
if(isProfiling) return measures.get(id).beginMeasurement();
else return null;
}
public String getID(Measurement m) {
return measures.inverse().get(m);
}
public boolean isProfiling() {
return isProfiling;
}
public void setProfiling(boolean enabled) {
this.isProfiling = enabled;
}
public World getWorld() {
return world;
}
}

View File

@@ -0,0 +1,103 @@
package com.dfsek.terra.api.structures.loot;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.structures.loot.functions.AmountFunction;
import com.dfsek.terra.api.structures.loot.functions.DamageFunction;
import com.dfsek.terra.api.structures.loot.functions.EnchantFunction;
import com.dfsek.terra.api.structures.loot.functions.LootFunction;
import com.dfsek.terra.api.util.GlueList;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.List;
import java.util.Random;
/**
* Representation of a single item entry within a Loot Table pool.
*/
public class Entry {
private final MaterialData item;
private final long weight;
private final List<LootFunction> functions = new GlueList<>();
private final TerraPlugin main;
/**
* Instantiates an Entry from a JSON representation.
*
* @param entry The JSON Object to instantiate from.
*/
public Entry(JSONObject entry, TerraPlugin main) {
this.main = main;
String id = entry.get("name").toString();
this.item = main.getWorldHandle().createMaterialData(id);
long weight1;
try {
weight1 = (long) entry.get("weight");
} catch(NullPointerException e) {
weight1 = 1;
}
this.weight = weight1;
if(entry.containsKey("functions")) {
for(Object function : (JSONArray) entry.get("functions")) {
switch(((String) ((JSONObject) function).get("function"))) {
case "minecraft:set_count":
case "set_count":
Object loot = ((JSONObject) function).get("count");
long max, min;
if(loot instanceof Long) {
max = (Long) loot;
min = (Long) loot;
} else {
max = (long) ((JSONObject) loot).get("max");
min = (long) ((JSONObject) loot).get("min");
}
functions.add(new AmountFunction(FastMath.toIntExact(min), FastMath.toIntExact(max)));
break;
case "minecraft:set_damage":
case "set_damage":
long maxDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("max");
long minDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("min");
functions.add(new DamageFunction(FastMath.toIntExact(minDamage), FastMath.toIntExact(maxDamage)));
break;
case "minecraft:enchant_with_levels":
case "enchant_with_levels":
long maxEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("max");
long minEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("min");
JSONArray disabled = null;
if(((JSONObject) function).containsKey("disabled_enchants"))
disabled = (JSONArray) ((JSONObject) function).get("disabled_enchants");
functions.add(new EnchantFunction(FastMath.toIntExact(minEnchant), FastMath.toIntExact(maxEnchant), disabled, main));
break;
}
}
}
}
/**
* Fetches a single ItemStack from the Entry, applying all functions to it.
*
* @param r The Random instance to apply functions with
* @return ItemStack - The ItemStack with all functions applied.
*/
public ItemStack getItem(Random r) {
ItemStack item = main.getItemHandle().newItemStack(this.item, 1);
for(LootFunction f : functions) {
item = f.apply(item, r);
}
return item;
}
/**
* Gets the weight attribute of the Entry.
*
* @return long - The weight of the Entry.
*/
public long getWeight() {
return this.weight;
}
}

View File

@@ -0,0 +1,78 @@
package com.dfsek.terra.api.structures.loot;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.inventory.Inventory;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.util.GlueList;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.List;
import java.util.Random;
/**
* Class representation of a Loot Table to populate chest loot.
*/
public class LootTable {
private final List<Pool> pools = new GlueList<>();
/**
* Instantiates a LootTable from a JSON String.
*
* @param json The JSON String representing the loot table.
* @throws ParseException if malformed JSON is passed.
*/
public LootTable(String json, TerraPlugin main) throws ParseException {
JSONParser jsonParser = new JSONParser();
Object tableJSON = jsonParser.parse(json);
JSONArray poolArray = (JSONArray) ((JSONObject) tableJSON).get("pools");
for(Object pool : poolArray) {
pools.add(new Pool((JSONObject) pool, main));
}
}
/**
* Fetches a list of ItemStacks from the loot table using the given Random instance.
*
* @param r The Random instance to use.
* @return List&lt;ItemStack&gt; - The list of loot fetched.
*/
public List<ItemStack> getLoot(Random r) {
List<ItemStack> itemList = new GlueList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
}
return itemList;
}
/**
* Fills an Inventory with loot.
*
* @param i The Inventory to fill.
* @param r The The Random instance to use.
*/
public void fillInventory(Inventory i, Random r) {
List<ItemStack> loot = getLoot(r);
for(ItemStack stack : loot) {
int attempts = 0;
while(stack.getAmount() != 0 && attempts < 10) {
ItemStack newStack = stack.clone();
newStack.setAmount(1);
int slot = r.nextInt(i.getSize());
ItemStack slotItem = i.getItem(slot);
if(slotItem == null) {
i.setItem(slot, newStack);
stack.setAmount(stack.getAmount() - 1);
} else if(slotItem.getType().equals(newStack.getType())) {
ItemStack dep = newStack.clone();
dep.setAmount(newStack.getAmount() + slotItem.getAmount());
i.setItem(slot, dep);
stack.setAmount(stack.getAmount() - 1);
}
attempts++;
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.dfsek.terra.api.structures.loot;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.util.GlueList;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.List;
import java.util.Random;
/**
* Representation of a Loot Table pool, or a set of items to be fetched independently.
*/
public class Pool {
private final int max;
private final int min;
private final ProbabilityCollection<Entry> entries;
/**
* Instantiates a Pool from a JSON representation.
*
* @param pool The JSON Object to instantiate from.
*/
public Pool(JSONObject pool, TerraPlugin main) {
entries = new ProbabilityCollection<>();
Object amount = pool.get("rolls");
if(amount instanceof Long) {
max = FastMath.toIntExact((Long) amount);
min = FastMath.toIntExact((Long) amount);
} else {
max = FastMath.toIntExact((Long) ((JSONObject) amount).get("max"));
min = FastMath.toIntExact((Long) ((JSONObject) amount).get("min"));
}
for(Object entryJSON : (JSONArray) pool.get("entries")) {
Entry entry = new Entry((JSONObject) entryJSON, main);
entries.add(entry, FastMath.toIntExact(entry.getWeight()));
}
}
/**
* Fetches a list of items from the pool using the provided Random instance.
*
* @param r The Random instance to use.
* @return List&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new GlueList<>();
for(int i = 0; i < rolls; i++) {
items.add(entries.get(r).getItem(r));
}
return items;
}
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.structures.loot.functions;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import java.util.Random;
/**
* Loot LootFunction fot setting the amount of an item.
*/
public class AmountFunction implements LootFunction {
private final int max;
private final int min;
/**
* Instantiates an AmountFunction.
*
* @param min Minimum amount.
* @param max Maximum amount.
*/
public AmountFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
original.setAmount(r.nextInt(max - min + 1) + min);
return original;
}
}

View File

@@ -0,0 +1,42 @@
package com.dfsek.terra.api.structures.loot.functions;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.platform.inventory.item.Damageable;
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
import java.util.Random;
/**
* Loot LootFunction for setting the damage on items in Loot Tables
*/
public class DamageFunction implements LootFunction {
private final int max;
private final int min;
/**
* Instantiates a DamageFunction.
*
* @param min Minimum amount of damage (percentage, out of 100)
* @param max Maximum amount of damage (percentage, out of 100)
*/
public DamageFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
double itemDurability = (r.nextDouble() * (max - min)) + min;
Damageable damage = (Damageable) original.getItemMeta();
damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability()));
original.setItemMeta((ItemMeta) damage);
return original;
}
}

View File

@@ -0,0 +1,64 @@
package com.dfsek.terra.api.structures.loot.functions;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
import com.dfsek.terra.api.util.GlueList;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class EnchantFunction implements LootFunction {
private final int min;
private final int max;
private final JSONArray disabled;
private final TerraPlugin main;
public EnchantFunction(int min, int max, JSONArray disabled, TerraPlugin main) {
this.max = max;
this.min = min;
this.disabled = disabled;
this.main = main;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
double enchant = (r.nextDouble() * (max - min)) + min;
List<Enchantment> possible = new GlueList<>();
for(Enchantment ench : main.getItemHandle().getEnchantments()) {
if(ench.canEnchantItem(original) && (disabled == null || !this.disabled.contains(ench.getID()))) {
possible.add(ench);
}
}
int numEnchant = (r.nextInt((int) FastMath.abs(enchant)) / 10 + 1);
Collections.shuffle(possible);
ItemMeta meta = original.getItemMeta();
iter:
for(int i = 0; i < numEnchant && i < possible.size(); i++) {
Enchantment chosen = possible.get(i);
for(Enchantment ench : meta.getEnchantments().keySet()) {
if(chosen.conflictsWith(ench)) continue iter;
}
int lvl = r.nextInt(1 + (int) (((enchant / 40 > 1) ? 1 : enchant / 40) * (chosen.getMaxLevel())));
try {
meta.addEnchantment(chosen, FastMath.max(lvl, 1));
} catch(IllegalArgumentException e) {
main.getLogger().warning("Attempted to enchant " + original.getType() + " with " + chosen + " at level " + FastMath.max(lvl, 1) + ", but an unexpected exception occurred! Usually this is caused by a misbehaving enchantment plugin.");
}
}
original.setItemMeta(meta);
return original;
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.structures.loot.functions;
import com.dfsek.terra.api.platform.inventory.ItemStack;
import java.util.Random;
/**
* Interface for mutating items in Loot Tables.
*/
public interface LootFunction {
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
ItemStack apply(ItemStack original, Random r);
}

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