Merge remote-tracking branch 'origin/master' into fabric/1.17-dev

# Conflicts:
#	platforms/fabric/build.gradle.kts
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricBlockState.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricMobSpawner.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/block/state/FabricSign.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/features/PopulatorFeature.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/FabricWorld.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/chunk/FabricChunk.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricSeededWorldAccess.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldAccess.java
#	platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/handles/world/FabricWorldHandle.java
#	platforms/fabric/src/main/resources/fabric.mod.json
#	platforms/fabric/src/main/resources/terra.accesswidener
#	platforms/fabric/src/main/resources/terra.mixins.json
This commit is contained in:
dfsek
2021-05-28 13:50:59 -07:00
256 changed files with 6971 additions and 2866 deletions
+1 -1
View File
@@ -342,6 +342,6 @@ ij_json_wrap_long_lines = false
indent_size = 2 indent_size = 2
ij_yaml_keep_indents_on_empty_lines = true ij_yaml_keep_indents_on_empty_lines = true
ij_yaml_keep_line_breaks = true ij_yaml_keep_line_breaks = true
ij_yaml_space_before_colon = true ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true ij_yaml_spaces_within_brackets = true
+26 -10
View File
@@ -7,24 +7,40 @@ to your specifications, with no knowledge of Java required.
* Paper+ servers (Paper, Tuinity, Purpur, etc): [SpigotMC](https://www.spigotmc.org/resources/85151/) * Paper+ servers (Paper, Tuinity, Purpur, etc): [SpigotMC](https://www.spigotmc.org/resources/85151/)
* Fabric: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator) * Fabric: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator)
* Forge **(ALPHA - NOT PRODUCTION-READY)**: [Modrinth](https://modrinth.com/mod/terra) / [CurseForge](https://www.curseforge.com/minecraft/mc-mods/terra-world-generator)
## Building and running Terra ## Building and Running Terra
To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will produce a jar in `build/libs` To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will build all platforms, and
called `Terra-[CURRENT VERSION].jar`. You can put this right into your plugins dir, along with the correct Gaea version. produce JARs in `platforms/<platform>/build/libs`
If you would like to test it with a default server config, just run `./gradlew setupServer` or ### Production JARs:
`./gradlew.bat setupServer` to set up the server, then `./gradlew testWithPaper` or `gradlew.bat testWithPaper` to run the server. If you * Bukkit: `Terra-<version>-shaded.jar`
want a clean installation of the server, re-run the `setupServer` task. This will download a default server config * Fabric: `Terra-<version>-shaded-mapped.jar`
from [here](https://github.com/PolyhedralDev/WorldGenTestServer) * Forge: `Terra-<version>-shaded.jar`
and install the server in the `target/server` directory, along with all the needed plugins.
**Note: You will need to adjust the `NAME` variable `bukkit.yml` of the test server if you are not using the default Terra config.** ### Building a Specific Platform
To build a specific platform, run `gradlew :platforms:<platform>:build`.
JARs are produced in `platforms/<platform>/build/libs`.
### Running Minecraft in the IDE
To run Minecraft with Terra in the IDE (for testing) use the following tasks:
* Bukkit
* `installPaper` - Install a [Paper](https://github.com/PaperMC/Paper) test server. (Only needs to be run once).
* `installPurpur` - Install a [Purpur](https://github.com/pl3xgaming/Purpur) test server. (Only needs to be run once).
* `runPaper` - Run the Paper test server with Terra (`installPaper` must have been run previously).
* `runPurpur` - Run the Purpur test server with Terra (`installPurpur` must have been run previously).
* Fabric
* `runClient` - Run a Minecraft Fabric client with Terra installed.
* `runServer` - Run a Minecraft Fabric server with Terra installed.
* Forge
* `runClient` - Run a Minecraft Forge client with Terra installed.
* `runServer` - Run a Minecraft Forge server with Terra installed.
## Contributing ## Contributing
Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and
submit a PR! submit a PR!
Join the discord [here](https://discord.gg/PXUEbbF) if you would like to talk more about the project! Join the discord [here](https://discord.gg/PXUEbbF) if you would like to talk more about the project!
## Beta ## Beta
Terra is still in beta! While it is stable, it is not feature-complete. There is a lot to be added! Terra is still in beta! While it is stable, it is not feature-complete. There is a lot to be added!
+19 -2
View File
@@ -1,10 +1,27 @@
import com.dfsek.terra.getGitHash import com.dfsek.terra.getGitHash
val versionObj = Version("5", "1", "0", true) val versionObj = Version("5", "3", "3", true)
allprojects { allprojects {
version = versionObj version = versionObj
group = "com.dfsek.terra" group = "com.dfsek.terra"
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
maxHeapSize = "2G"
ignoreFailures = false
failFast = true
maxParallelForks = (Runtime.getRuntime().availableProcessors() - 1).takeIf { it > 0 } ?: 1
reports.html.isEnabled = false
reports.junitXml.isEnabled = false
}
} }
/** /**
* Version class that does version stuff. * Version class that does version stuff.
@@ -18,4 +35,4 @@ class Version(val major: String, val minor: String, val revision: String, val pr
else //Only use git hash if it's a prerelease. else //Only use git hash if it's a prerelease.
"$major.$minor.$revision-BETA+${getGitHash()}" "$major.$minor.$revision-BETA+${getGitHash()}"
} }
} }
@@ -7,30 +7,15 @@ import org.gradle.kotlin.dsl.withType
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
fun Project.configureCommon() { fun Project.configureCommon() {
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "idea")
configureDependencies() configureDependencies()
configureCompilation() configureCompilation()
configureDistribution() configureDistribution()
version = rootProject.version version = rootProject.version
tasks.withType<Test>().configureEach {
useJUnitPlatform()
maxHeapSize = "2G"
ignoreFailures = false
failFast = true
maxParallelForks = 12
}
} }
fun Project.getGitHash(): String { fun Project.getGitHash(): String {
val stdout = java.io.ByteArrayOutputStream() val stdout = ByteArrayOutputStream()
exec { exec {
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD") commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout standardOutput = stdout
@@ -3,14 +3,16 @@ package com.dfsek.terra
import org.gradle.api.JavaVersion import org.gradle.api.JavaVersion
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.*
import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.withType
import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.language.jvm.tasks.ProcessResources
fun Project.configureCompilation() { fun Project.configureCompilation() {
apply(plugin = "maven-publish")
apply(plugin = "idea")
configure<JavaPluginConvention> { configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
@@ -19,7 +21,7 @@ fun Project.configureCompilation() {
tasks.withType<JavaCompile> { tasks.withType<JavaCompile> {
options.encoding = "UTF-8" options.encoding = "UTF-8"
doFirst { doFirst {
options.compilerArgs = mutableListOf("-Xlint:all") options.compilerArgs.add("-Xlint:all")
} }
} }
@@ -27,7 +29,12 @@ fun Project.configureCompilation() {
include("**/*.*") include("**/*.*")
filter<org.apache.tools.ant.filters.ReplaceTokens>( filter<org.apache.tools.ant.filters.ReplaceTokens>(
"tokens" to mapOf( "tokens" to mapOf(
"VERSION" to project.version.toString() "VERSION" to project.version.toString(),
"DESCRIPTION" to project.properties["terra.description"],
"WIKI" to project.properties["terra.wiki"],
"SOURCE" to project.properties["terra.source"],
"ISSUES" to project.properties["terra.issues"],
"LICENSE" to project.properties["terra.license"]
) )
) )
} }
@@ -35,4 +42,19 @@ fun Project.configureCompilation() {
tasks.withType<Javadoc> { tasks.withType<Javadoc> {
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
tasks.withType<Jar> {
archiveBaseName.set("Terra-${archiveBaseName.get()}")
from("../LICENSE", "../../LICENSE")
}
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
}
tasks.register<Jar>("javadocJar") {
dependsOn("javadoc")
archiveClassifier.set("javadoc")
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
}
} }
@@ -1,13 +1,27 @@
package com.dfsek.terra package com.dfsek.terra
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.invoke
import org.gradle.kotlin.dsl.repositories import org.gradle.kotlin.dsl.repositories
fun Project.configureDependencies() { fun Project.configureDependencies() {
apply(plugin = "java")
apply(plugin = "java-library")
configurations {
val shaded = create("shaded")
val shadedApi = create("shadedApi")
shaded.extendsFrom(shadedApi)
getByName("api").extendsFrom(shadedApi)
val shadedImplementation = create("shadedImplementation")
shaded.extendsFrom(shadedImplementation)
getByName("implementation").extendsFrom(shadedImplementation)
}
repositories { repositories {
maven { url = uri("http://maven.enginehub.org/repo/") } maven { url = uri("https://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") } maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") } maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://maven.fabricmc.net/") } maven { url = uri("https://maven.fabricmc.net/") }
@@ -18,9 +32,7 @@ fun Project.configureDependencies() {
dependencies { dependencies {
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0") "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0")
"testImplementation"("org.yaml:snakeyaml:1.27") "testImplementation"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
"testImplementation"("com.googlecode.json-simple:json-simple:1.1.1") "api"("org.jetbrains:annotations:20.1.0")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
"compileOnly"("org.jetbrains:annotations:20.1.0")
} }
} }
@@ -14,26 +14,8 @@ fun Project.configureDistribution() {
apply(plugin = "java-library") apply(plugin = "java-library")
apply(plugin = "com.github.johnrengelman.shadow") 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") { val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
group = "terra"
doFirst { doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively() file("${buildDir}/resources/main/packs/").deleteRecursively()
@@ -45,21 +27,6 @@ fun Project.configureDistribution() {
} }
tasks["processResources"].dependsOn(downloadDefaultPacks) tasks["processResources"].dependsOn(downloadDefaultPacks)
tasks.withType<Jar> {
archiveBaseName.set("Terra-${archiveBaseName.get()}")
from("../LICENSE", "../../LICENSE")
}
tasks.register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
}
tasks.register<Jar>("javadocJar") {
dependsOn("javadoc")
archiveClassifier.set("javadoc")
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
}
tasks.named<ShadowJar>("shadowJar") { tasks.named<ShadowJar>("shadowJar") {
// Tell shadow to download the packs // Tell shadow to download the packs
dependsOn(downloadDefaultPacks) dependsOn(downloadDefaultPacks)
@@ -69,8 +36,14 @@ fun Project.configureDistribution() {
archiveClassifier.set("shaded") archiveClassifier.set("shaded")
setVersion(project.version) setVersion(project.version)
relocate("org.apache.commons", "com.dfsek.terra.lib.commons") relocate("org.apache.commons", "com.dfsek.terra.lib.commons")
relocate("parsii", "com.dfsek.terra.lib.parsii")
relocate("net.jafama", "com.dfsek.terra.lib.jafama") relocate("net.jafama", "com.dfsek.terra.lib.jafama")
relocate("org.objectweb.asm", "com.dfsek.terra.lib.asm")
relocate("com.google.errorprone", "com.dfsek.terra.lib.google.errorprone")
relocate("com.google.j2objc", "com.dfsek.terra.lib.google.j2objc")
relocate("org.checkerframework", "com.dfsek.terra.lib.checkerframework")
relocate("org.javax.annotation", "com.dfsek.terra.lib.javax.annotation")
relocate("org.json", "com.dfsek.terra.lib.json")
relocate("org.yaml", "com.dfsek.terra.lib.yaml")
minimize() minimize()
} }
convention.getPlugin<BasePluginConvention>().archivesBaseName = project.name convention.getPlugin<BasePluginConvention>().archivesBaseName = project.name
+8 -4
View File
@@ -1,11 +1,14 @@
import com.dfsek.terra.configureCommon import com.dfsek.terra.configureCompilation
import com.dfsek.terra.configureDependencies
plugins { plugins {
`java-library` `java-library`
`maven-publish` `maven-publish`
idea
} }
configureCommon() configureCompilation()
configureDependencies()
group = "com.dfsek.terra.common" group = "com.dfsek.terra.common"
@@ -14,13 +17,14 @@ dependencies {
"shadedApi"("commons-io:commons-io:2.4") "shadedApi"("commons-io:commons-io:2.4")
"shadedApi"("com.dfsek:Paralithic:0.3.2") "shadedApi"("com.dfsek:Paralithic:0.3.2")
"shadedApi"("com.dfsek:Tectonic:1.2.3") "shadedApi"("com.dfsek:Tectonic:1.3.1")
"shadedApi"("net.jafama:jafama:2.3.2") "shadedApi"("net.jafama:jafama:2.3.2")
"shadedApi"("org.yaml:snakeyaml:1.27") "shadedApi"("org.yaml:snakeyaml:1.27")
"shadedApi"("org.ow2.asm:asm:9.0") "shadedApi"("org.ow2.asm:asm:9.0")
"shadedApi"("commons-io:commons-io:2.6") "shadedApi"("commons-io:commons-io:2.6")
"compileOnly"("com.googlecode.json-simple:json-simple:1.1") "shadedApi"("com.googlecode.json-simple:json-simple:1.1.1")
"shadedApi"("org.yaml:snakeyaml:1.27")
"compileOnly"("com.google.guava:guava:30.0-jre") "compileOnly"("com.google.guava:guava:30.0-jre")
@@ -7,14 +7,19 @@ import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.registry.CheckedRegistry; import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.LockedRegistry; import com.dfsek.terra.api.registry.LockedRegistry;
import com.dfsek.terra.api.util.JarUtil;
import com.dfsek.terra.api.util.logging.DebugLogger; import com.dfsek.terra.api.util.logging.DebugLogger;
import com.dfsek.terra.api.util.logging.Logger; import com.dfsek.terra.api.util.logging.Logger;
import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.jar.JarFile;
/** /**
* Represents a Terra mod/plugin instance. * Represents a Terra mod/plugin instance.
@@ -64,4 +69,10 @@ public interface TerraPlugin extends LoaderRegistrar {
default void runPossiblyUnsafeTask(Runnable task) { default void runPossiblyUnsafeTask(Runnable task) {
task.run(); task.run();
} }
Profiler getProfiler();
default JarFile getModJar() throws URISyntaxException, IOException {
return JarUtil.getJarFile();
}
} }
@@ -65,7 +65,7 @@ public class TerraCommandManager implements CommandManager {
return; return;
} }
if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !TerraWorld.isTerraWorld(((Player) sender).getWorld()))) { if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !(((Player) sender).getWorld()).isTerraWorld())) {
sender.sendMessage("Command must be executed in a Terra world."); sender.sendMessage("Command must be executed in a Terra world.");
return; return;
} }
@@ -160,7 +160,6 @@ public class TerraCommandManager implements CommandManager {
if(field.isAnnotationPresent(SwitchTarget.class)) { if(field.isAnnotationPresent(SwitchTarget.class)) {
SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class); SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class);
if(!holder.switches.containsValue(switchTarget.value())) { if(!holder.switches.containsValue(switchTarget.value())) {
System.out.println(holder.switches);
throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\""); throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\"");
} }
@@ -0,0 +1,20 @@
package com.dfsek.terra.api.event.events;
import com.dfsek.terra.api.util.mutable.MutableBoolean;
/**
* Abstract class containing basic {@link Cancellable} implementation.
*/
public abstract class AbstractCancellable implements Cancellable {
private final MutableBoolean cancelled = new MutableBoolean(false);
@Override
public boolean isCancelled() {
return cancelled.get();
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled.set(cancelled);
}
}
@@ -1,5 +1,7 @@
package com.dfsek.terra.api.event.events.config; package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.terra.api.event.events.PackEvent; import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
@@ -8,13 +10,28 @@ import com.dfsek.terra.config.pack.ConfigPack;
*/ */
public abstract class ConfigPackLoadEvent implements PackEvent { public abstract class ConfigPackLoadEvent implements PackEvent {
private final ConfigPack pack; private final ConfigPack pack;
private final ExceptionalConsumer<ConfigTemplate> configLoader;
public ConfigPackLoadEvent(ConfigPack pack) { public ConfigPackLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader) {
this.pack = pack; this.pack = pack;
this.configLoader = configLoader;
} }
@Override @Override
public ConfigPack getPack() { public ConfigPack getPack() {
return pack; return pack;
} }
/**
* Load a custom {@link ConfigTemplate} using the pack manifest.
*
* @param template Template to register.
*/
public void loadTemplate(ConfigTemplate template) throws ConfigException {
configLoader.accept(template);
}
public interface ExceptionalConsumer<T extends ConfigTemplate> {
void accept(T value) throws ConfigException;
}
} }
@@ -1,12 +1,13 @@
package com.dfsek.terra.api.event.events.config; package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
/** /**
* Called when a config pack has finished loading. * Called when a config pack has finished loading.
*/ */
public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent { public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent {
public ConfigPackPostLoadEvent(ConfigPack pack) { public ConfigPackPostLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> loader) {
super(pack); super(pack, loader);
} }
} }
@@ -1,12 +1,14 @@
package com.dfsek.terra.api.event.events.config; package com.dfsek.terra.api.event.events.config;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
/** /**
* Called before a config pack's registries are filled. At this point, the pack manifest has been loaded, and all registries are empty. * Called before a config pack's registries are filled. At this point, the pack manifest has been loaded, and all registries are empty.
*/ */
public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent { public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent {
public ConfigPackPreLoadEvent(ConfigPack pack) { public ConfigPackPreLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader) {
super(pack); super(pack, configLoader);
} }
} }
@@ -1,6 +1,5 @@
package com.dfsek.terra.api.event.events.world; package com.dfsek.terra.api.event.events.world;
import com.dfsek.terra.api.event.events.Event;
import com.dfsek.terra.api.event.events.PackEvent; import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
@@ -9,18 +8,24 @@ import com.dfsek.terra.world.TerraWorld;
/** /**
* Called upon initialization of a TerraWorld. * Called upon initialization of a TerraWorld.
*/ */
public class TerraWorldLoadEvent implements Event { public class TerraWorldLoadEvent implements PackEvent {
private final TerraWorld world; private final TerraWorld world;
private final ConfigPack pack;
public TerraWorldLoadEvent(TerraWorld world) { public TerraWorldLoadEvent(TerraWorld world, ConfigPack pack) {
this.world = world; this.world = world;
this.pack = pack;
} }
public TerraWorld getWorld() { public TerraWorld getWorld() {
return world; return world;
} }
public WorldConfig getPack() { public ConfigPack getPack() {
return pack;
}
public WorldConfig getWorldConfig() {
return world.getConfig(); return world.getConfig();
} }
} }
@@ -0,0 +1,45 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedEntity;
import com.dfsek.terra.config.pack.ConfigPack;
/**
* Called when an entity is spawned via {@link BufferedEntity}.
*/
public class EntitySpawnEvent implements PackEvent {
private final ConfigPack pack;
private final Entity entity;
private final Location location;
public EntitySpawnEvent(ConfigPack pack, Entity entity, Location location) {
this.pack = pack;
this.entity = entity;
this.location = location;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the entity that triggered the event.
*
* @return The entity.
*/
public Entity getEntity() {
return entity;
}
/**
* Get the location of the entity.
*
* @return Location of the entity.
*/
public Location getLocation() {
return location;
}
}
@@ -0,0 +1,80 @@
package com.dfsek.terra.api.event.events.world.generation;
import com.dfsek.terra.api.event.events.AbstractCancellable;
import com.dfsek.terra.api.event.events.Cancellable;
import com.dfsek.terra.api.event.events.PackEvent;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.state.Container;
import com.dfsek.terra.api.structures.loot.LootTable;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication;
import com.dfsek.terra.config.pack.ConfigPack;
import org.jetbrains.annotations.NotNull;
/**
* Called when loot is populated via {@link BufferedLootApplication}.
*/
public class LootPopulateEvent extends AbstractCancellable implements PackEvent, Cancellable {
private final Block block;
private final Container container;
private LootTable table;
private final ConfigPack pack;
private final StructureScript script;
public LootPopulateEvent(Block block, Container container, LootTable table, ConfigPack pack, StructureScript script) {
this.block = block;
this.container = container;
this.table = table;
this.pack = pack;
this.script = script;
}
@Override
public ConfigPack getPack() {
return pack;
}
/**
* Get the block containing the tile entity loot is applied to.
*
* @return Block at which loot is applied.
*/
public Block getBlock() {
return block;
}
/**
* Get the {@link Container} representing the inventory.
*
* @return Inventory recieving loot.
*/
public Container getContainer() {
return container;
}
/**
* Get the loot table to be populated.
* @return Loot table.
*/
public LootTable getTable() {
return table;
}
/**
* Set the loot table to be populated.
*
* @param table New loot table.
*/
public void setTable(@NotNull LootTable table) {
this.table = table;
}
/**
* Get the script used to generate the structure.
*
* @return Structure script.
*/
public StructureScript getStructureScript() {
return script;
}
}
@@ -1,6 +1,7 @@
package com.dfsek.terra.api.platform.inventory; package com.dfsek.terra.api.platform.inventory;
import com.dfsek.terra.api.platform.Handle; import com.dfsek.terra.api.platform.Handle;
import com.dfsek.terra.api.platform.inventory.item.Damageable;
import com.dfsek.terra.api.platform.inventory.item.ItemMeta; import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
public interface ItemStack extends Handle { public interface ItemStack extends Handle {
@@ -13,4 +14,8 @@ public interface ItemStack extends Handle {
ItemMeta getItemMeta(); ItemMeta getItemMeta();
void setItemMeta(ItemMeta meta); void setItemMeta(ItemMeta meta);
default boolean isDamageable() {
return getItemMeta() instanceof Damageable;
}
} }
@@ -6,6 +6,8 @@ import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.entity.Entity; import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.platform.entity.EntityType; import com.dfsek.terra.api.platform.entity.EntityType;
import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; import com.dfsek.terra.api.platform.world.generator.ChunkGenerator;
import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import java.io.File; import java.io.File;
import java.util.UUID; import java.util.UUID;
@@ -17,20 +19,12 @@ public interface World extends Handle {
ChunkGenerator getGenerator(); ChunkGenerator getGenerator();
String getName();
UUID getUID();
boolean isChunkGenerated(int x, int z);
Chunk getChunkAt(int x, int z); Chunk getChunkAt(int x, int z);
default Chunk getChunkAt(Location location) { default Chunk getChunkAt(Location location) {
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4); return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
} }
File getWorldFolder();
Block getBlockAt(int x, int y, int z); Block getBlockAt(int x, int y, int z);
default Block getBlockAt(Location l) { default Block getBlockAt(Location l) {
@@ -40,4 +34,12 @@ public interface World extends Handle {
Entity spawnEntity(Location location, EntityType entityType); Entity spawnEntity(Location location, EntityType entityType);
int getMinHeight(); int getMinHeight();
default boolean isTerraWorld() {
return getGenerator().getHandle() instanceof GeneratorWrapper;
}
default TerraChunkGenerator getTerraGenerator() {
return ((GeneratorWrapper) getGenerator().getHandle()).getHandle();
}
} }
@@ -34,8 +34,8 @@ public class DamageFunction implements LootFunction {
@Override @Override
public ItemStack apply(ItemStack original, Random r) { public ItemStack apply(ItemStack original, Random r) {
if(original == null) return null; if(original == null) return null;
if(!original.isDamageable()) return original;
ItemMeta meta = original.getItemMeta(); ItemMeta meta = original.getItemMeta();
if(!(meta instanceof Damageable)) return original;
double itemDurability = (r.nextDouble() * (max - min)) + min; double itemDurability = (r.nextDouble() * (max - min)) + min;
Damageable damage = (Damageable) meta; Damageable damage = (Damageable) meta;
damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability())); damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability()));
@@ -29,6 +29,7 @@ import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.api.structures.structure.buffer.Buffer; import com.dfsek.terra.api.structures.structure.buffer.Buffer;
import com.dfsek.terra.api.structures.structure.buffer.DirectBuffer; import com.dfsek.terra.api.structures.structure.buffer.DirectBuffer;
import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer; import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer;
import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.registry.config.FunctionRegistry; import com.dfsek.terra.registry.config.FunctionRegistry;
import com.dfsek.terra.registry.config.LootRegistry; import com.dfsek.terra.registry.config.LootRegistry;
import com.dfsek.terra.registry.config.ScriptRegistry; import com.dfsek.terra.registry.config.ScriptRegistry;
@@ -39,6 +40,7 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -52,7 +54,7 @@ public class StructureScript {
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException { public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException {
Parser parser; Parser parser;
try { try {
parser = new Parser(IOUtils.toString(inputStream)); parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset()));
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -68,7 +70,7 @@ public class StructureScript {
.registerFunction("setMark", new SetMarkFunctionBuilder()) .registerFunction("setMark", new SetMarkFunctionBuilder())
.registerFunction("getMark", new GetMarkFunctionBuilder()) .registerFunction("getMark", new GetMarkFunctionBuilder())
.registerFunction("pull", new PullFunctionBuilder(main)) .registerFunction("pull", new PullFunctionBuilder(main))
.registerFunction("loot", new LootFunctionBuilder(main, lootRegistry)) .registerFunction("loot", new LootFunctionBuilder(main, lootRegistry, this))
.registerFunction("entity", new EntityFunctionBuilder(main)) .registerFunction("entity", new EntityFunctionBuilder(main))
.registerFunction("getBiome", new BiomeFunctionBuilder(main)) .registerFunction("getBiome", new BiomeFunctionBuilder(main))
.registerFunction("getBlock", new CheckBlockFunctionBuilder()) .registerFunction("getBlock", new CheckBlockFunctionBuilder())
@@ -87,6 +89,12 @@ public class StructureScript {
.registerFunction("ceil", new UnaryNumberFunctionBuilder(number -> FastMath.ceil(number.doubleValue()))) .registerFunction("ceil", new UnaryNumberFunctionBuilder(number -> FastMath.ceil(number.doubleValue())))
.registerFunction("log", new UnaryNumberFunctionBuilder(number -> FastMath.log(number.doubleValue()))) .registerFunction("log", new UnaryNumberFunctionBuilder(number -> FastMath.log(number.doubleValue())))
.registerFunction("round", new UnaryNumberFunctionBuilder(number -> FastMath.round(number.doubleValue()))) .registerFunction("round", new UnaryNumberFunctionBuilder(number -> FastMath.round(number.doubleValue())))
.registerFunction("sin", new UnaryNumberFunctionBuilder(number -> FastMath.sin(number.doubleValue())))
.registerFunction("cos", new UnaryNumberFunctionBuilder(number -> FastMath.cos(number.doubleValue())))
.registerFunction("tan", new UnaryNumberFunctionBuilder(number -> FastMath.tan(number.doubleValue())))
.registerFunction("asin", new UnaryNumberFunctionBuilder(number -> FastMath.asin(number.doubleValue())))
.registerFunction("acos", new UnaryNumberFunctionBuilder(number -> FastMath.acos(number.doubleValue())))
.registerFunction("atan", new UnaryNumberFunctionBuilder(number -> FastMath.atan(number.doubleValue())))
.registerFunction("max", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.max(number.doubleValue(), number2.doubleValue()))) .registerFunction("max", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.max(number.doubleValue(), number2.doubleValue())))
.registerFunction("min", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue()))); .registerFunction("min", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue())));
@@ -104,22 +112,31 @@ public class StructureScript {
* @param rotation Rotation of structure * @param rotation Rotation of structure
* @return Whether generation was successful * @return Whether generation was successful
*/ */
@SuppressWarnings("try")
public boolean execute(Location location, Random random, Rotation rotation) { public boolean execute(Location location, Random random, Rotation rotation) {
StructureBuffer buffer = new StructureBuffer(location); try(ProfileFrame ignore = main.getProfiler().profile("terrascript:" + id)) {
boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); StructureBuffer buffer = new StructureBuffer(location);
buffer.paste(); boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0));
return level; buffer.paste();
return level;
}
} }
@SuppressWarnings("try")
public boolean execute(Location location, Chunk chunk, Random random, Rotation rotation) { public boolean execute(Location location, Chunk chunk, Random random, Rotation rotation) {
StructureBuffer buffer = computeBuffer(location, random, rotation); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_chunk:" + id)) {
buffer.paste(chunk); StructureBuffer buffer = computeBuffer(location, random, rotation);
return buffer.succeeded(); buffer.paste(chunk);
return buffer.succeeded();
}
} }
@SuppressWarnings("try")
public boolean test(Location location, Random random, Rotation rotation) { public boolean test(Location location, Random random, Rotation rotation) {
StructureBuffer buffer = computeBuffer(location, random, rotation); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_test:" + id)) {
return buffer.succeeded(); StructureBuffer buffer = computeBuffer(location, random, rotation);
return buffer.succeeded();
}
} }
private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) { private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) {
@@ -134,13 +151,19 @@ public class StructureScript {
} }
} }
@SuppressWarnings("try")
public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) { public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions)); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_recursive:" + id)) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions));
}
} }
@SuppressWarnings("try")
public boolean executeDirect(Location location, Random random, Rotation rotation) { public boolean executeDirect(Location location, Random random, Rotation rotation) {
DirectBuffer buffer = new DirectBuffer(location); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_direct:" + id)) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); DirectBuffer buffer = new DirectBuffer(location);
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0));
}
} }
public String getId() { public String getId() {
@@ -3,6 +3,7 @@ package com.dfsek.terra.api.structures.script.builders;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.structures.parser.lang.Returnable; import com.dfsek.terra.api.structures.parser.lang.Returnable;
import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder; import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.script.functions.LootFunction; import com.dfsek.terra.api.structures.script.functions.LootFunction;
import com.dfsek.terra.api.structures.tokenizer.Position; import com.dfsek.terra.api.structures.tokenizer.Position;
import com.dfsek.terra.registry.config.LootRegistry; import com.dfsek.terra.registry.config.LootRegistry;
@@ -12,16 +13,18 @@ import java.util.List;
public class LootFunctionBuilder implements FunctionBuilder<LootFunction> { public class LootFunctionBuilder implements FunctionBuilder<LootFunction> {
private final TerraPlugin main; private final TerraPlugin main;
private final LootRegistry registry; private final LootRegistry registry;
private final StructureScript script;
public LootFunctionBuilder(TerraPlugin main, LootRegistry registry) { public LootFunctionBuilder(TerraPlugin main, LootRegistry registry, StructureScript script) {
this.main = main; this.main = main;
this.registry = registry; this.registry = registry;
this.script = script;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public LootFunction build(List<Returnable<?>> argumentList, Position position) { public LootFunction build(List<Returnable<?>> argumentList, Position position) {
return new LootFunction(registry, (Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), (Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), main, position); return new LootFunction(registry, (Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), (Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), main, position, script);
} }
@Override @Override
@@ -21,9 +21,11 @@ public class EntityFunction implements Function<Void> {
private final EntityType data; private final EntityType data;
private final Returnable<Number> x, y, z; private final Returnable<Number> x, y, z;
private final Position position; private final Position position;
private final TerraPlugin main;
public EntityFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, TerraPlugin main, Position position) throws ParseException { public EntityFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, TerraPlugin main, Position position) throws ParseException {
this.position = position; this.position = position;
this.main = main;
if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition()); if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition());
this.data = main.getWorldHandle().getEntity(((ConstantExpression<String>) data).getConstant()); this.data = main.getWorldHandle().getEntity(((ConstantExpression<String>) data).getConstant());
@@ -39,7 +41,7 @@ public class EntityFunction implements Function<Void> {
RotationUtil.rotateVector(xz, arguments.getRotation()); RotationUtil.rotateVector(xz, arguments.getRotation());
arguments.getBuffer().addItem(new BufferedEntity(data), new Vector3(xz.getX(), y.apply(implementationArguments, variableMap).doubleValue(), xz.getZ()).toLocation(arguments.getBuffer().getOrigin().getWorld())); arguments.getBuffer().addItem(new BufferedEntity(data, main), new Vector3(xz.getX(), y.apply(implementationArguments, variableMap).doubleValue(), xz.getZ()).toLocation(arguments.getBuffer().getOrigin().getWorld()));
return null; return null;
} }
@@ -8,6 +8,7 @@ import com.dfsek.terra.api.structures.parser.lang.ImplementationArguments;
import com.dfsek.terra.api.structures.parser.lang.Returnable; import com.dfsek.terra.api.structures.parser.lang.Returnable;
import com.dfsek.terra.api.structures.parser.lang.functions.Function; import com.dfsek.terra.api.structures.parser.lang.functions.Function;
import com.dfsek.terra.api.structures.parser.lang.variables.Variable; import com.dfsek.terra.api.structures.parser.lang.variables.Variable;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.script.TerraImplementationArguments; import com.dfsek.terra.api.structures.script.TerraImplementationArguments;
import com.dfsek.terra.api.structures.structure.RotationUtil; import com.dfsek.terra.api.structures.structure.RotationUtil;
import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication; import com.dfsek.terra.api.structures.structure.buffer.items.BufferedLootApplication;
@@ -23,8 +24,9 @@ public class LootFunction implements Function<Void> {
private final Returnable<Number> x, y, z; private final Returnable<Number> x, y, z;
private final Position position; private final Position position;
private final TerraPlugin main; private final TerraPlugin main;
private final StructureScript script;
public LootFunction(LootRegistry registry, Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, TerraPlugin main, Position position) { public LootFunction(LootRegistry registry, Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, TerraPlugin main, Position position, StructureScript script) {
this.registry = registry; this.registry = registry;
this.position = position; this.position = position;
this.data = data; this.data = data;
@@ -32,6 +34,7 @@ public class LootFunction implements Function<Void> {
this.y = y; this.y = y;
this.z = z; this.z = z;
this.main = main; this.main = main;
this.script = script;
} }
@Override @Override
@@ -49,7 +52,7 @@ public class LootFunction implements Function<Void> {
return null; return null;
} }
arguments.getBuffer().addItem(new BufferedLootApplication(table, main), new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), FastMath.roundToInt(xz.getZ())).toLocation(arguments.getBuffer().getOrigin().getWorld())); arguments.getBuffer().addItem(new BufferedLootApplication(table, main, script), new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), FastMath.roundToInt(xz.getZ())).toLocation(arguments.getBuffer().getOrigin().getWorld()));
return null; return null;
} }
@@ -1,18 +1,24 @@
package com.dfsek.terra.api.structures.structure.buffer.items; package com.dfsek.terra.api.structures.structure.buffer.items;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.event.events.world.generation.EntitySpawnEvent;
import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.entity.Entity;
import com.dfsek.terra.api.platform.entity.EntityType; import com.dfsek.terra.api.platform.entity.EntityType;
public class BufferedEntity implements BufferedItem { public class BufferedEntity implements BufferedItem {
private final EntityType type; private final EntityType type;
private final TerraPlugin main;
public BufferedEntity(EntityType type) { public BufferedEntity(EntityType type, TerraPlugin main) {
this.type = type; this.type = type;
this.main = main;
} }
@Override @Override
public void paste(Location origin) { public void paste(Location origin) {
origin.clone().add(0.5, 0, 0.5).getWorld().spawnEntity(origin, type); Entity entity = origin.clone().add(0.5, 0, 0.5).getWorld().spawnEntity(origin, type);
main.getEventManager().callEvent(new EntitySpawnEvent(entity.getWorld().getTerraGenerator().getConfigPack(), entity, entity.getLocation()));
} }
} }
@@ -1,35 +1,46 @@
package com.dfsek.terra.api.structures.structure.buffer.items; package com.dfsek.terra.api.structures.structure.buffer.items;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.event.events.world.generation.LootPopulateEvent;
import com.dfsek.terra.api.math.vector.Location; import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.state.BlockState; import com.dfsek.terra.api.platform.block.state.BlockState;
import com.dfsek.terra.api.platform.block.state.Container; import com.dfsek.terra.api.platform.block.state.Container;
import com.dfsek.terra.api.structures.loot.LootTable; import com.dfsek.terra.api.structures.loot.LootTable;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.util.FastRandom;
public class BufferedLootApplication implements BufferedItem { public class BufferedLootApplication implements BufferedItem {
private final LootTable table; private final LootTable table;
private final TerraPlugin main; private final TerraPlugin main;
private final StructureScript structure;
public BufferedLootApplication(LootTable table, TerraPlugin main) { public BufferedLootApplication(LootTable table, TerraPlugin main, StructureScript structure) {
this.table = table; this.table = table;
this.main = main; this.main = main;
this.structure = structure;
} }
@Override @Override
public void paste(Location origin) { public void paste(Location origin) {
try { try {
BlockState data = origin.getBlock().getState(); Block block = origin.getBlock();
BlockState data = block.getState();
if(!(data instanceof Container)) { if(!(data instanceof Container)) {
main.logger().severe("Failed to place loot at " + origin + "; block " + data + " is not container."); main.logger().severe("Failed to place loot at " + origin + "; block " + data + " is not container.");
return; return;
} }
Container container = (Container) data; Container container = (Container) data;
table.fillInventory(container.getInventory(), new FastRandom(origin.hashCode()));
LootPopulateEvent event = new LootPopulateEvent(block, container, table, block.getLocation().getWorld().getTerraGenerator().getConfigPack(), structure);
main.getEventManager().callEvent(event);
if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(), new FastRandom(origin.hashCode()));
data.update(false); data.update(false);
} catch(Exception e) { } catch(Exception e) {
main.logger().warning("Could not apply loot at " + origin + ": " + e.getMessage()); main.logger().warning("Could not apply loot at " + origin + ": " + e.getMessage());
main.getDebugLogger().stack(e); e.printStackTrace();
} }
} }
} }
@@ -21,7 +21,7 @@ public class BufferedStateManipulator implements BufferedItem {
state.update(false); state.update(false);
} catch(Exception e) { } catch(Exception e) {
main.logger().warning("Could not apply BlockState at " + origin + ": " + e.getMessage()); main.logger().warning("Could not apply BlockState at " + origin + ": " + e.getMessage());
main.getDebugLogger().stack(e); e.printStackTrace();
} }
} }
} }
@@ -1,8 +0,0 @@
package com.dfsek.terra.api.transform;
public class NotNullValidator<T> implements Validator<T> {
@Override
public boolean validate(T value) {
return !(value == null);
}
}
@@ -53,6 +53,7 @@ public class Transformer<F, T> {
private final LinkedHashMap<Transform<F, T>, List<Validator<T>>> transforms = new LinkedHashMap<>(); private final LinkedHashMap<Transform<F, T>, List<Validator<T>>> transforms = new LinkedHashMap<>();
@SafeVarargs @SafeVarargs
@SuppressWarnings("varargs")
public final Builder<F, T> addTransform(Transform<F, T> transform, Validator<T>... validators) { public final Builder<F, T> addTransform(Transform<F, T> transform, Validator<T>... validators) {
transforms.put(transform, Arrays.asList(validators)); transforms.put(transform, Arrays.asList(validators));
return this; return this;
@@ -1,6 +1,12 @@
package com.dfsek.terra.api.transform; package com.dfsek.terra.api.transform;
import java.util.Objects;
public interface Validator<T> { public interface Validator<T> {
boolean validate(T value) throws TransformException; boolean validate(T value) throws TransformException;
static <T> Validator<T> notNull() {
return Objects::nonNull;
}
} }
@@ -1,9 +1,13 @@
package com.dfsek.terra.api.util; package com.dfsek.terra.api.util;
import com.dfsek.terra.api.TerraPlugin;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@@ -32,4 +36,12 @@ public class JarUtil {
} }
} }
} }
public static JarFile getJarFile() throws URISyntaxException, IOException {
return new JarFile(new File(getJarURL().toURI()));
}
public static URL getJarURL() {
return TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation();
}
} }
@@ -5,6 +5,14 @@ import org.jetbrains.annotations.NotNull;
public class MutableBoolean implements MutablePrimitive<Boolean> { public class MutableBoolean implements MutablePrimitive<Boolean> {
private boolean value; private boolean value;
public MutableBoolean() {
this.value = false;
}
public MutableBoolean(boolean value) {
this.value = value;
}
@Override @Override
public Boolean get() { public Boolean get() {
return value; return value;
@@ -0,0 +1,7 @@
package com.dfsek.terra.api.world.generation;
/**
* Marker interface that marks a feature as "chunkified" (only modifying one chunk at a time)
*/
public interface Chunkified {
}
@@ -1,8 +0,0 @@
package com.dfsek.terra.api.world.generation;
/**
* The phase of terrain generation. Used for modifying values based on the phase of generation.
*/
public enum GenerationPhase {
BASE, POPULATE, GENERATION_POPULATE, PALETTE_APPLY, POST_GEN
}
@@ -6,10 +6,10 @@ import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.platform.world.generator.ChunkData; import com.dfsek.terra.api.platform.world.generator.ChunkData;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.world.generation.math.SamplerCache;
import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Random; import java.util.Random;
public interface TerraChunkGenerator { public interface TerraChunkGenerator {
@@ -32,4 +32,6 @@ public interface TerraChunkGenerator {
TerraPlugin getMain(); TerraPlugin getMain();
Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth); Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth);
List<TerraBlockPopulator> getPopulators();
} }
@@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileQueryCommand implements CommandTemplate { public class ProfileQueryCommand implements CommandTemplate {
@Inject @Inject
@@ -21,8 +15,9 @@ public class ProfileQueryCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; StringBuilder data = new StringBuilder("Terra Profiler data dump: \n");
TerraWorld world = main.getWorld(player.getWorld()); main.getProfiler().getTimings().forEach((id, timings) -> data.append(id).append(": ").append(timings.toString()).append('\n'));
player.sendMessage(world.getProfiler().getResultsFormatted()); main.logger().info(data.toString());
sender.sendMessage("Profiler data dumped to console.");
} }
} }
@@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileResetCommand implements CommandTemplate { public class ProfileResetCommand implements CommandTemplate {
@Inject @Inject
@@ -21,9 +15,7 @@ public class ProfileResetCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().reset();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiler reset.");
world.getProfiler().reset();
player.sendMessage("Profiler reset.");
} }
} }
@@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileStartCommand implements CommandTemplate { public class ProfileStartCommand implements CommandTemplate {
@Inject @Inject
@@ -21,9 +15,7 @@ public class ProfileStartCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().start();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiling enabled.");
world.getProfiler().setProfiling(true);
player.sendMessage("Profiling enabled.");
} }
} }
@@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileStopCommand implements CommandTemplate { public class ProfileStopCommand implements CommandTemplate {
@Inject @Inject
@@ -21,9 +15,7 @@ public class ProfileStopCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().stop();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiling disabled.");
world.getProfiler().setProfiling(false);
player.sendMessage("Profiling disabled.");
} }
} }
@@ -63,8 +63,6 @@ public class StructureLoadCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
System.out.println(rotation);
Player player = (Player) sender; Player player = (Player) sender;
long t = System.nanoTime(); long t = System.nanoTime();
@@ -69,12 +69,12 @@ public class PluginConfig implements ConfigTemplate {
ConfigLoader loader = new ConfigLoader(); ConfigLoader loader = new ConfigLoader();
loader.load(this, file); loader.load(this, file);
if(dumpDefaultConfig) { // Don't dump default config if already loaded. if(dumpDefaultConfig) { // Don't dump default config if already loaded.
try(JarFile jar = new JarFile(new File(TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { try(JarFile jar = main.getModJar()) {
JarUtil.copyResourcesToDirectory(jar, "packs", new File(main.getDataFolder(), "packs").toString()); JarUtil.copyResourcesToDirectory(jar, "packs", new File(main.getDataFolder(), "packs").toString());
} catch(IOException | URISyntaxException e) { } catch(IOException | URISyntaxException e) {
main.getDebugLogger().error("Failed to dump default config files!"); main.getDebugLogger().error("Failed to dump default config files!");
e.printStackTrace(); e.printStackTrace();
main.getDebugLogger().error("Report this to Terra!"); main.getDebugLogger().error("Either you're on Forge, or this is a bug. If it's the latter, report this to Terra!");
} }
} }
} catch(ConfigException | IOException e) { } catch(ConfigException | IOException e) {
@@ -4,7 +4,10 @@ import com.dfsek.terra.api.platform.world.Biome;
import com.dfsek.terra.api.util.collections.ProbabilityCollection; import com.dfsek.terra.api.util.collections.ProbabilityCollection;
import com.dfsek.terra.api.util.seeded.SeededBuilder; import com.dfsek.terra.api.util.seeded.SeededBuilder;
import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.config.templates.BiomeTemplate;
public interface BiomeBuilder extends SeededBuilder<TerraBiome> { public interface BiomeBuilder extends SeededBuilder<TerraBiome> {
ProbabilityCollection<Biome> getVanillaBiomes(); ProbabilityCollection<Biome> getVanillaBiomes();
BiomeTemplate getTemplate();
} }
@@ -1,147 +0,0 @@
package com.dfsek.terra.config.builder;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.terra.api.math.noise.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.ExpressionSampler;
import com.dfsek.terra.api.math.noise.samplers.noise.ConstantSampler;
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
import com.dfsek.terra.api.world.palette.holder.PaletteHolder;
import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
import com.dfsek.terra.world.generation.WorldGenerator;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class GeneratorBuilder {
private final Map<Long, WorldGenerator> gens = Collections.synchronizedMap(new HashMap<>());
private String noiseEquation;
private String elevationEquation;
private String carvingEquation;
private Scope varScope;
private Map<String, NoiseSeeded> noiseBuilderMap;
private Map<String, FunctionTemplate> functionTemplateMap;
private PaletteHolder palettes;
private PaletteHolder slantPalettes;
private boolean preventInterpolation;
private boolean interpolateElevation;
private NoiseSeeded biomeNoise;
private double elevationWeight;
private int blendDistance;
private int blendStep;
private double blendWeight;
public WorldGenerator build(long seed) {
synchronized(gens) {
return gens.computeIfAbsent(seed, k -> {
NoiseSampler noise;
NoiseSampler elevation;
NoiseSampler carving;
try {
noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap, functionTemplateMap);
} catch(ParseException e) {
throw new RuntimeException(e);
}
return new WorldGenerator(palettes, slantPalettes, noise, elevation, carving, biomeNoise.apply(seed), elevationWeight, blendDistance, blendStep, blendWeight);
});
}
}
public void setBlendWeight(double blendWeight) {
this.blendWeight = blendWeight;
}
public void setFunctionTemplateMap(Map<String, FunctionTemplate> functionTemplateMap) {
this.functionTemplateMap = functionTemplateMap;
}
public void setBlendStep(int blendStep) {
this.blendStep = blendStep;
}
public void setBlendDistance(int blendDistance) {
this.blendDistance = blendDistance;
}
public void setBiomeNoise(NoiseSeeded biomeNoise) {
this.biomeNoise = biomeNoise;
}
public void setElevationWeight(double elevationWeight) {
this.elevationWeight = elevationWeight;
}
public void setNoiseEquation(String noiseEquation) {
this.noiseEquation = noiseEquation;
}
public void setElevationEquation(String elevationEquation) {
this.elevationEquation = elevationEquation;
}
public void setCarvingEquation(String carvingEquation) {
this.carvingEquation = carvingEquation;
}
public Scope getVarScope() {
return varScope;
}
public void setVarScope(Scope varScope) {
this.varScope = varScope;
}
public void setNoiseBuilderMap(Map<String, NoiseSeeded> noiseBuilderMap) {
this.noiseBuilderMap = noiseBuilderMap;
}
public PaletteHolder getPalettes() {
return palettes;
}
public void setPalettes(PaletteHolder palettes) {
this.palettes = palettes;
}
public PaletteHolder getSlantPalettes() {
return slantPalettes;
}
public void setSlantPalettes(PaletteHolder slantPalettes) {
this.slantPalettes = slantPalettes;
}
public boolean isPreventInterpolation() {
return preventInterpolation;
}
public void setPreventInterpolation(boolean preventInterpolation) {
this.preventInterpolation = preventInterpolation;
}
public void setInterpolateElevation(boolean interpolateElevation) {
this.interpolateElevation = interpolateElevation;
}
public boolean interpolateElevation() {
return interpolateElevation;
}
}
@@ -66,4 +66,9 @@ public class UserDefinedBiomeBuilder implements BiomeBuilder {
public ProbabilityCollection<Biome> getVanillaBiomes() { public ProbabilityCollection<Biome> getVanillaBiomes() {
return template.getVanilla(); return template.getVanilla();
} }
@Override
public BiomeTemplate getTemplate() {
return template;
}
} }
@@ -9,9 +9,6 @@ import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.platform.world.generator.ChunkGenerator; import com.dfsek.terra.api.platform.world.generator.ChunkGenerator;
import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper;
import java.io.File;
import java.util.UUID;
public class DummyWorld implements World { public class DummyWorld implements World {
@Override @Override
public Object getHandle() { public Object getHandle() {
@@ -33,31 +30,11 @@ public class DummyWorld implements World {
return () -> (GeneratorWrapper) () -> null; return () -> (GeneratorWrapper) () -> null;
} }
@Override
public String getName() {
return "DUMMY";
}
@Override
public UUID getUID() {
return UUID.randomUUID();
}
@Override
public boolean isChunkGenerated(int x, int z) {
return false;
}
@Override @Override
public Chunk getChunkAt(int x, int z) { public Chunk getChunkAt(int x, int z) {
throw new UnsupportedOperationException("Cannot get chunk in DummyWorld"); throw new UnsupportedOperationException("Cannot get chunk in DummyWorld");
} }
@Override
public File getWorldFolder() {
throw new UnsupportedOperationException("Cannot get folder of DummyWorld");
}
@Override @Override
public Block getBlockAt(int x, int y, int z) { public Block getBlockAt(int x, int y, int z) {
throw new UnsupportedOperationException("Cannot get block in DummyWorld"); throw new UnsupportedOperationException("Cannot get block in DummyWorld");
@@ -1,5 +1,6 @@
package com.dfsek.terra.config.fileloaders; package com.dfsek.terra.config.fileloaders;
import com.dfsek.tectonic.config.Configuration;
import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.terra.api.util.GlueList; import com.dfsek.terra.api.util.GlueList;
@@ -18,8 +19,12 @@ public abstract class Loader {
* *
* @param consumer Something to do with the streams. * @param consumer Something to do with the streams.
*/ */
public Loader then(ExceptionalConsumer<List<InputStream>> consumer) throws ConfigException { public Loader then(ExceptionalConsumer<List<Configuration>> consumer) throws ConfigException {
consumer.accept(new GlueList<>(streams.values())); List<Configuration> list = new GlueList<>();
streams.forEach((id, stream) -> {
list.add(new Configuration(stream, id));
});
consumer.accept(list);
return this; return this;
} }
@@ -17,7 +17,7 @@ public final class LangUtil {
public static void load(String langID, TerraPlugin main) { public static void load(String langID, TerraPlugin main) {
Logger logger = main.logger(); Logger logger = main.logger();
File file = new File(main.getDataFolder(), "lang"); File file = new File(main.getDataFolder(), "lang");
try(JarFile jar = new JarFile(new File(TerraPlugin.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { try(JarFile jar = main.getModJar()) {
copyResourcesToDirectory(jar, "lang", file.toString()); copyResourcesToDirectory(jar, "lang", file.toString());
} catch(IOException | URISyntaxException e) { } catch(IOException | URISyntaxException e) {
main.getDebugLogger().error("Failed to dump language files!"); main.getDebugLogger().error("Failed to dump language files!");
@@ -18,7 +18,7 @@ public class OreHolderLoader implements TypeLoader<OreHolder> {
Map<String, Object> map = (Map<String, Object>) o; Map<String, Object> map = (Map<String, Object>) o;
for(Map.Entry<String, Object> entry : map.entrySet()) { for(Map.Entry<String, Object> entry : map.entrySet()) {
holder.add(configLoader.loadClass(Ore.class, entry.getKey()), (OreConfig) configLoader.loadType(OreConfig.class, entry.getValue())); holder.add(configLoader.loadClass(Ore.class, entry.getKey()), configLoader.loadClass(OreConfig.class, entry.getValue()), entry.getKey());
} }
return holder; return holder;
@@ -65,7 +65,6 @@ public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFuncti
Map<String, Function> noiseFunctionMap = new HashMap<>(); Map<String, Function> noiseFunctionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : expressions.entrySet()) { for(Map.Entry<String, FunctionTemplate> entry : expressions.entrySet()) {
System.out.println(entry);
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue(), new Parser(), new Scope())); noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue(), new Parser(), new Scope()));
} }
@@ -2,6 +2,7 @@ package com.dfsek.terra.config.pack;
import com.dfsek.paralithic.eval.parser.Scope; import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.tectonic.abstraction.AbstractConfigLoader; import com.dfsek.tectonic.abstraction.AbstractConfigLoader;
import com.dfsek.tectonic.config.Configuration;
import com.dfsek.tectonic.exception.ConfigException; import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.tectonic.loading.ConfigLoader;
@@ -110,6 +111,8 @@ public class ConfigPack implements LoaderRegistrar {
private final TerraPlugin main; private final TerraPlugin main;
private final Loader loader; private final Loader loader;
private final Configuration configuration;
private final BiomeProvider.BiomeProviderBuilder biomeProviderBuilder; private final BiomeProvider.BiomeProviderBuilder biomeProviderBuilder;
@@ -130,15 +133,20 @@ public class ConfigPack implements LoaderRegistrar {
File pack = new File(folder, "pack.yml"); File pack = new File(folder, "pack.yml");
try { try {
selfLoader.load(template, new FileInputStream(pack)); configuration = new Configuration(new FileInputStream(pack));
selfLoader.load(template, configuration);
main.logger().info("Loading config pack \"" + template.getID() + "\""); main.logger().info("Loading config pack \"" + template.getID() + "\"");
main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this, template -> selfLoader.load(template, configuration)));
load(l, main); load(l, main);
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate(); ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
selfLoader.load(packPostTemplate, new FileInputStream(pack)); selfLoader.load(packPostTemplate, new FileInputStream(pack));
biomeProviderBuilder = packPostTemplate.getProviderBuilder(); biomeProviderBuilder = packPostTemplate.getProviderBuilder();
biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time. biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time.
checkDeadEntries(main);
} catch(FileNotFoundException e) { } catch(FileNotFoundException e) {
throw new LoadException("No pack.yml file found in " + folder.getAbsolutePath(), e); throw new LoadException("No pack.yml file found in " + folder.getAbsolutePath(), e);
} }
@@ -173,9 +181,12 @@ public class ConfigPack implements LoaderRegistrar {
if(pack == null) throw new LoadException("No pack.yml file found in " + file.getName()); if(pack == null) throw new LoadException("No pack.yml file found in " + file.getName());
selfLoader.load(template, file.getInputStream(pack)); configuration = new Configuration(file.getInputStream(pack));
selfLoader.load(template, configuration);
main.logger().info("Loading config pack \"" + template.getID() + "\""); main.logger().info("Loading config pack \"" + template.getID() + "\"");
main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this, template -> selfLoader.load(template, configuration)));
load(l, main); load(l, main);
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate(); ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
@@ -183,6 +194,7 @@ public class ConfigPack implements LoaderRegistrar {
selfLoader.load(packPostTemplate, file.getInputStream(pack)); selfLoader.load(packPostTemplate, file.getInputStream(pack));
biomeProviderBuilder = packPostTemplate.getProviderBuilder(); biomeProviderBuilder = packPostTemplate.getProviderBuilder();
biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time. biomeProviderBuilder.build(0); // Build dummy provider to catch errors at load time.
checkDeadEntries(main);
} catch(IOException e) { } catch(IOException e) {
throw new LoadException("Unable to load pack.yml from ZIP file", e); throw new LoadException("Unable to load pack.yml from ZIP file", e);
} }
@@ -198,9 +210,17 @@ public class ConfigPack implements LoaderRegistrar {
for(C template : configTemplates) registry.add(template.getID(), factory.build(template, main)); for(C template : configTemplates) registry.add(template.getID(), factory.build(template, main));
} }
private void load(long start, TerraPlugin main) throws ConfigException { private void checkDeadEntries(TerraPlugin main) {
main.getEventManager().callEvent(new ConfigPackPreLoadEvent(this)); biomeRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in biome registry: '" + id + "'"));
paletteRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in palette registry: '" + id + "'"));
floraRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in flora registry: '" + id + "'"));
carverRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in carver registry: '" + id + "'"));
treeRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in tree registry: '" + id + "'"));
oreRegistry.getDeadEntries().forEach((id, value) -> main.getDebugLogger().warn("Dead entry in ore registry: '" + id + "'"));
}
private void load(long start, TerraPlugin main) throws ConfigException {
for(Map.Entry<String, Double> var : template.getVariables().entrySet()) { for(Map.Entry<String, Double> var : template.getVariables().entrySet()) {
varScope.create(var.getKey(), var.getValue()); varScope.create(var.getKey(), var.getValue());
} }
@@ -225,15 +245,15 @@ public class ConfigPack implements LoaderRegistrar {
}).close(); }).close();
loader loader
.open("carving", ".yml").then(streams -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.load(streams, CarverTemplate::new), main)).close() .open("carving", ".yml").then(configs -> buildAll(new CarverFactory(this), carverRegistry, abstractConfigLoader.loadConfigs(configs, CarverTemplate::new), main)).close()
.open("palettes", ".yml").then(streams -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.load(streams, PaletteTemplate::new), main)).close() .open("palettes", ".yml").then(configs -> buildAll(new PaletteFactory(), paletteRegistry, abstractConfigLoader.loadConfigs(configs, PaletteTemplate::new), main)).close()
.open("ores", ".yml").then(streams -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.load(streams, OreTemplate::new), main)).close() .open("ores", ".yml").then(configs -> buildAll(new OreFactory(), oreRegistry, abstractConfigLoader.loadConfigs(configs, OreTemplate::new), main)).close()
.open("structures/trees", ".yml").then(streams -> buildAll(new TreeFactory(), treeRegistry, abstractConfigLoader.load(streams, TreeTemplate::new), main)).close() .open("structures/trees", ".yml").then(configs -> buildAll(new TreeFactory(), treeRegistry, abstractConfigLoader.loadConfigs(configs, TreeTemplate::new), main)).close()
.open("structures/structures", ".yml").then(streams -> buildAll(new StructureFactory(), structureRegistry, abstractConfigLoader.load(streams, StructureTemplate::new), main)).close() .open("structures/structures", ".yml").then(configs -> buildAll(new StructureFactory(), structureRegistry, abstractConfigLoader.loadConfigs(configs, StructureTemplate::new), main)).close()
.open("flora", ".yml").then(streams -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.load(streams, FloraTemplate::new), main)).close() .open("flora", ".yml").then(configs -> buildAll(new FloraFactory(), floraRegistry, abstractConfigLoader.loadConfigs(configs, FloraTemplate::new), main)).close()
.open("biomes", ".yml").then(streams -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.load(streams, () -> new BiomeTemplate(this, main)), main)).close(); .open("biomes", ".yml").then(configs -> buildAll(new BiomeFactory(this), biomeRegistry, abstractConfigLoader.loadConfigs(configs, () -> new BiomeTemplate(this, main)), main)).close();
main.getEventManager().callEvent(new ConfigPackPostLoadEvent(this)); main.getEventManager().callEvent(new ConfigPackPostLoadEvent(this, template -> selfLoader.load(template, configuration)));
main.logger().info("Loaded config pack \"" + template.getID() + "\" v" + template.getVersion() + " by " + template.getAuthor() + " in " + (System.nanoTime() - start) / 1000000D + "ms."); main.logger().info("Loaded config pack \"" + template.getID() + "\" v" + template.getVersion() + " by " + template.getAuthor() + " in " + (System.nanoTime() - start) / 1000000D + "ms.");
} }
@@ -73,6 +73,46 @@ public class ConfigPackTemplate implements ConfigTemplate {
@Default @Default
private String version = "0.1.0"; private String version = "0.1.0";
@Value("disable.carvers")
@Default
private boolean disableCarvers = false;
@Value("disable.structures")
@Default
private boolean disableStructures = false;
@Value("disable.ores")
@Default
private boolean disableOres = false;
@Value("disable.trees")
@Default
private boolean disableTrees = false;
@Value("disable.flora")
@Default
private boolean disableFlora = false;
public boolean disableCarvers() {
return disableCarvers;
}
public boolean disableFlora() {
return disableFlora;
}
public boolean disableOres() {
return disableOres;
}
public boolean disableStructures() {
return disableStructures;
}
public boolean disableTrees() {
return disableTrees;
}
public LinkedHashMap<String, FunctionTemplate> getFunctions() { public LinkedHashMap<String, FunctionTemplate> getFunctions() {
return functions; return functions;
} }
@@ -190,10 +190,19 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
@Default @Default
private Map<UserDefinedCarver, Integer> carvers = new HashMap<>(); private Map<UserDefinedCarver, Integer> carvers = new HashMap<>();
@Value("colors")
@Abstractable
@Default
private Map<String, Integer> colors = new HashMap<>(); // Plain ol' map, so platforms can decide what to do with colors (if anything).
public Set<String> getTags() { public Set<String> getTags() {
return tags; return tags;
} }
public Map<String, Integer> getColors() {
return colors;
}
public Map<UserDefinedCarver, Integer> getCarvers() { public Map<UserDefinedCarver, Integer> getCarvers() {
return carvers; return carvers;
} }
@@ -1,36 +0,0 @@
package com.dfsek.terra.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);
}
}
@@ -1,24 +0,0 @@
package com.dfsek.terra.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;
}
}
@@ -1,10 +0,0 @@
package com.dfsek.terra.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
}
@@ -0,0 +1,24 @@
package com.dfsek.terra.profiler;
public class Frame {
private final String id;
private final long start;
public Frame(String id) {
this.id = id;
this.start = System.nanoTime();
}
public String getId() {
return id;
}
public long getStart() {
return start;
}
@Override
public String toString() {
return id;
}
}
@@ -1,87 +0,0 @@
package com.dfsek.terra.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();
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.profiler;
public class ProfileFrame implements AutoCloseable {
private final Runnable action;
public ProfileFrame(Runnable action) {
this.action = action;
}
@Override
public void close() {
action.run();
}
}
@@ -1,18 +0,0 @@
package com.dfsek.terra.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();
}
}
@@ -0,0 +1,56 @@
package com.dfsek.terra.profiler;
import java.util.Map;
public interface Profiler {
/**
* Push a frame to this profiler.
*
* @param frame ID of frame.
*/
void push(String frame);
/**
* Pop a frame from this profiler.
*
* @param frame ID of frame. Must match ID
* at the top of the profiler stack.
*/
void pop(String frame);
/**
* Start profiling.
*/
void start();
/**
* Stop profiling.
*/
void stop();
/**
* Get the profiler data.
*
* @return Profiler data.
*/
Map<String, Timings> getTimings();
/**
* Return a {@link AutoCloseable} implementation that
* may be used in a try-with-resources statement for
* more intuitive profiling, with auto-push/pop.
*
* @param frame ID of frame.
* @return {@link AutoCloseable} implementation for use
* in try-with-resources.
*/
default ProfileFrame profile(String frame) {
push(frame);
return new ProfileFrame(() -> pop(frame));
}
/**
* Clear the profiler data.
*/
void reset();
}
@@ -0,0 +1,91 @@
package com.dfsek.terra.profiler;
import com.dfsek.terra.api.util.mutable.MutableInteger;
import com.dfsek.terra.profiler.exception.MalformedStackException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class ProfilerImpl implements Profiler {
private static final ThreadLocal<Stack<Frame>> THREAD_STACK = ThreadLocal.withInitial(Stack::new);
private static final ThreadLocal<Map<String, List<Long>>> TIMINGS = ThreadLocal.withInitial(HashMap::new);
private final List<Map<String, List<Long>>> accessibleThreadMaps = new ArrayList<>();
private volatile boolean running = false;
private static boolean instantiated = false;
private static final ThreadLocal<Boolean> SAFE = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<MutableInteger> STACK_SIZE = ThreadLocal.withInitial(() -> new MutableInteger(0));
public ProfilerImpl() {
if(instantiated) throw new IllegalStateException("Only one instance of Profiler may exist!");
instantiated = true;
}
@Override
public void push(String frame) {
STACK_SIZE.get().increment();
if(running && SAFE.get()) {
Stack<Frame> stack = THREAD_STACK.get();
stack.push(new Frame(stack.isEmpty() ? frame : stack.peek().getId() + "." + frame));
} else SAFE.set(false);
}
@Override
public void pop(String frame) {
MutableInteger size = STACK_SIZE.get();
size.decrement();
if(running && SAFE.get()) {
long time = System.nanoTime();
Stack<Frame> stack = THREAD_STACK.get();
Map<String, List<Long>> timingsMap = TIMINGS.get();
if(timingsMap.size() == 0) {
synchronized(accessibleThreadMaps) {
accessibleThreadMaps.add(timingsMap);
}
}
Frame top = stack.pop();
if((stack.size() != 0 && !top.getId().endsWith("." + frame)) || (stack.size() == 0 && !top.getId().equals(frame)))
throw new MalformedStackException("Expected " + frame + ", found " + top);
List<Long> timings = timingsMap.computeIfAbsent(top.getId(), id -> new ArrayList<>());
timings.add(time - top.getStart());
}
if(size.get() == 0) SAFE.set(true);
}
@Override
public void start() {
running = true;
}
@Override
public void stop() {
running = false;
}
@Override
public Map<String, Timings> getTimings() {
Map<String, Timings> map = new HashMap<>();
accessibleThreadMaps.forEach(smap -> smap.forEach((key, list) -> {
String[] keys = key.split("\\.");
Timings timings = map.computeIfAbsent(keys[0], id -> new Timings());
for(int i = 1; i < keys.length; i++) {
timings = timings.getSubItem(keys[i]);
}
list.forEach(timings::addTime);
}));
return map;
}
@Override
public void reset() {
accessibleThreadMaps.forEach(Map::clear);
}
}
@@ -0,0 +1,73 @@
package com.dfsek.terra.profiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Timings {
private final Map<String, Timings> subItems = new HashMap<>();
private final List<Long> timings = new ArrayList<>();
public void addTime(long time) {
timings.add(time);
}
public List<Long> getTimings() {
return timings;
}
public double average() {
return (double) timings.stream().reduce(0L, Long::sum) / timings.size();
}
public long max() {
return timings.stream().mapToLong(Long::longValue).max().orElse(0L);
}
public long min() {
return timings.stream().mapToLong(Long::longValue).min().orElse(0L);
}
public double sum() {
return timings.stream().mapToDouble(Long::doubleValue).sum();
}
public Timings getSubItem(String id) {
return subItems.computeIfAbsent(id, s -> new Timings());
}
public String toString(int indent, Timings parent, Set<Integer> branches) {
StringBuilder builder = new StringBuilder();
builder.append((double) min() / 1000000).append("ms min / ").append(average() / 1000000).append("ms avg / ")
.append((double) max() / 1000000).append("ms max (").append(timings.size()).append(" samples, ")
.append((sum() / parent.sum()) * 100).append("% of parent)");
List<String> frames = new ArrayList<>();
Set<Integer> newBranches = new HashSet<>(branches);
newBranches.add(indent);
subItems.forEach((id, timings) -> frames.add(id + ": " + timings.toString(indent + 1, this, newBranches)));
for(int i = 0; i < frames.size(); i++) {
builder.append('\n');
for(int j = 0; j < indent; j++) {
if(branches.contains(j)) builder.append("");
else builder.append(" ");
}
if(i == frames.size() - 1 && !frames.get(i).contains("\n")) builder.append("└───");
else builder.append("├───");
builder.append(frames.get(i));
}
return builder.toString();
}
@Override
public String toString() {
return toString(1, this, Collections.emptySet());
}
}
@@ -1,85 +0,0 @@
package com.dfsek.terra.profiler;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.world.TerraWorld;
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(!TerraWorld.isTerraWorld(w))
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Terra managed world!");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime")
.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");
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.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;
}
}
@@ -0,0 +1,17 @@
package com.dfsek.terra.profiler.exception;
public class MalformedStackException extends ProfilerException {
private static final long serialVersionUID = -3009539681021691054L;
public MalformedStackException(String message) {
super(message);
}
public MalformedStackException(String message, Throwable cause) {
super(message, cause);
}
public MalformedStackException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,17 @@
package com.dfsek.terra.profiler.exception;
public class ProfilerException extends RuntimeException {
private static final long serialVersionUID = 8206737998791649002L;
public ProfilerException(String message) {
super(message);
}
public ProfilerException(String message, Throwable cause) {
super(message, cause);
}
public ProfilerException(Throwable cause) {
super(cause);
}
}
@@ -7,18 +7,19 @@ import com.dfsek.terra.registry.exception.DuplicateEntryException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
/** /**
* Registry implementation with read/write access. For internal use only. * Registry implementation with read/write access. For internal use only.
* @param <T> * @param <T>
*/ */
public class OpenRegistry<T> implements Registry<T> { public class OpenRegistry<T> implements Registry<T> {
private final Map<String, T> objects = new HashMap<>(); private final Map<String, Entry<T>> objects = new HashMap<>();
@Override @Override
public T load(Type type, Object o, ConfigLoader configLoader) throws LoadException { public T load(Type type, Object o, ConfigLoader configLoader) throws LoadException {
@@ -35,6 +36,10 @@ public class OpenRegistry<T> implements Registry<T> {
* @param value Value to add. * @param value Value to add.
*/ */
public boolean add(String identifier, T value) { public boolean add(String identifier, T value) {
return add(identifier, new Entry<>(value));
}
protected boolean add(String identifier, Entry<T> value) {
boolean exists = objects.containsKey(identifier); boolean exists = objects.containsKey(identifier);
objects.put(identifier, value); objects.put(identifier, value);
return exists; return exists;
@@ -60,22 +65,24 @@ public class OpenRegistry<T> implements Registry<T> {
@Override @Override
public T get(String identifier) { public T get(String identifier) {
return objects.get(identifier); Entry<T> entry = objects.get(identifier);
if(entry == null) return null;
return entry.getValue();
} }
@Override @Override
public void forEach(Consumer<T> consumer) { public void forEach(Consumer<T> consumer) {
objects.forEach((id, obj) -> consumer.accept(obj)); objects.forEach((id, obj) -> consumer.accept(obj.getRaw()));
} }
@Override @Override
public void forEach(BiConsumer<String, T> consumer) { public void forEach(BiConsumer<String, T> consumer) {
objects.forEach(consumer); objects.forEach((id, entry) -> consumer.accept(id, entry.getRaw()));
} }
@Override @Override
public Set<T> entries() { public Set<T> entries() {
return new HashSet<>(objects.values()); return objects.values().stream().map(Entry::getRaw).collect(Collectors.toSet());
} }
@Override @Override
@@ -83,10 +90,41 @@ public class OpenRegistry<T> implements Registry<T> {
return objects.keySet(); return objects.keySet();
} }
public Map<String, T> getDeadEntries() {
Map<String, T> dead = new HashMap<>();
objects.forEach((id, entry) -> {
if(entry.dead()) dead.put(id, entry.value); // dont increment value here.
});
return dead;
}
/** /**
* Clears all entries from the registry. * Clears all entries from the registry.
*/ */
public void clear() { public void clear() {
objects.clear(); objects.clear();
} }
protected static final class Entry<T> {
private final T value;
private final AtomicInteger access = new AtomicInteger(0);
public Entry(T value) {
this.value = value;
}
public T getValue() {
access.incrementAndGet();
return value;
}
private T getRaw() {
return value;
}
public boolean dead() {
return access.get() == 0;
}
}
} }
@@ -57,7 +57,9 @@ public class FloraRegistry extends OpenRegistry<Flora> {
private void addItem(String id, Callable<ConstantFlora> flora) { private void addItem(String id, Callable<ConstantFlora> flora) {
try { try {
add(id, flora.call()); Entry<Flora> entry = new Entry<>(flora.call());
entry.getValue(); // Mark as not dead.
add(id, entry);
} catch(Exception e) { } catch(Exception e) {
main.logger().warning("Failed to load Flora item: " + id + ": " + e.getMessage()); main.logger().warning("Failed to load Flora item: " + id + ": " + e.getMessage());
} }
@@ -66,10 +68,4 @@ public class FloraRegistry extends OpenRegistry<Flora> {
private BlockData data(String s) { private BlockData data(String s) {
return main.getWorldHandle().createBlockData(s); return main.getWorldHandle().createBlockData(s);
} }
@Override
public Flora get(String identifier) {
return super.get(identifier);
}
} }
@@ -13,7 +13,6 @@ import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.profiler.WorldProfiler;
import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler;
import net.jafama.FastMath; import net.jafama.FastMath;
@@ -21,33 +20,25 @@ public class TerraWorld {
private final BiomeProvider provider; private final BiomeProvider provider;
private final WorldConfig config; private final WorldConfig config;
private final boolean safe; private final boolean safe;
private final WorldProfiler profiler;
private final World world; private final World world;
private final BlockData air; private final BlockData air;
public TerraWorld(World w, ConfigPack c, TerraPlugin main) { public TerraWorld(World w, ConfigPack c, TerraPlugin main) {
if(!isTerraWorld(w)) throw new IllegalArgumentException("World " + w + " is not a Terra World!"); if(!w.isTerraWorld()) throw new IllegalArgumentException("World " + w + " is not a Terra World!");
this.world = w; this.world = w;
config = c.toWorldConfig(this); config = c.toWorldConfig(this);
this.provider = config.getProvider(); this.provider = config.getProvider();
profiler = new WorldProfiler(w);
air = main.getWorldHandle().createBlockData("minecraft:air"); air = main.getWorldHandle().createBlockData("minecraft:air");
main.getEventManager().callEvent(new TerraWorldLoadEvent(this)); main.getEventManager().callEvent(new TerraWorldLoadEvent(this, c));
safe = true; safe = true;
} }
public static boolean isTerraWorld(World w) {
return w.getGenerator().getHandle() instanceof GeneratorWrapper;
}
public World getWorld() { public World getWorld() {
return world; return world;
} }
public TerraChunkGenerator getGenerator() {
return ((GeneratorWrapper) world.getGenerator().getHandle()).getHandle();
}
public BiomeProvider getBiomeProvider() { public BiomeProvider getBiomeProvider() {
return provider; return provider;
@@ -61,9 +52,6 @@ public class TerraWorld {
return safe; return safe;
} }
public WorldProfiler getProfiler() {
return profiler;
}
/** /**
* Get a block at an ungenerated location * Get a block at an ungenerated location
@@ -10,20 +10,27 @@ import com.dfsek.terra.api.util.world.PaletteUtil;
import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.Carver;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.carving.NoiseCarver;
import com.dfsek.terra.world.generation.math.SamplerCache; import com.dfsek.terra.world.generation.math.SamplerCache;
import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler;
import com.dfsek.terra.world.generation.math.samplers.Sampler2D; import com.dfsek.terra.world.generation.math.samplers.Sampler2D;
import com.dfsek.terra.world.population.CavePopulator;
import com.dfsek.terra.world.population.OrePopulator;
import com.dfsek.terra.world.population.StructurePopulator;
import com.dfsek.terra.world.population.TreePopulator;
import net.jafama.FastMath; import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
public class DefaultChunkGenerator2D implements TerraChunkGenerator { public class DefaultChunkGenerator2D implements TerraChunkGenerator {
@@ -31,12 +38,18 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator {
private final TerraPlugin main; private final TerraPlugin main;
private final Carver carver; private final Carver carver;
private final List<TerraBlockPopulator> blockPopulators = new ArrayList<>();
private final SamplerCache cache; private final SamplerCache cache;
public DefaultChunkGenerator2D(ConfigPack c, TerraPlugin main, SamplerCache cache) { public DefaultChunkGenerator2D(ConfigPack c, TerraPlugin main, SamplerCache cache) {
this.configPack = c; this.configPack = c;
this.main = main; this.main = main;
blockPopulators.add(new CavePopulator(main));
blockPopulators.add(new StructurePopulator(main));
blockPopulators.add(new OrePopulator(main));
blockPopulators.add(new TreePopulator(main));
blockPopulators.add(new TreePopulator(main));
carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main); carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main);
this.cache = cache; this.cache = cache;
} }
@@ -81,7 +94,7 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator {
public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
BiomeProvider grid = tw.getBiomeProvider(); BiomeProvider grid = tw.getBiomeProvider();
try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_2d")) {
if(!tw.isSafe()) return chunk; if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4); int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4); int zOrig = (chunkZ << 4);
@@ -125,4 +138,9 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator {
public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) { public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) {
return new Sampler2D(chunkX, chunkZ, provider, world, elevationSmooth); return new Sampler2D(chunkX, chunkZ, provider, world, elevationSmooth);
} }
@Override
public List<TerraBlockPopulator> getPopulators() {
return blockPopulators;
}
} }
@@ -17,19 +17,27 @@ import com.dfsek.terra.api.util.world.PaletteUtil;
import com.dfsek.terra.api.world.biome.TerraBiome; import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.api.world.palette.SinglePalette; import com.dfsek.terra.api.world.palette.SinglePalette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.Carver;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.carving.NoiseCarver;
import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler;
import com.dfsek.terra.world.generation.math.samplers.Sampler3D; import com.dfsek.terra.world.generation.math.samplers.Sampler3D;
import com.dfsek.terra.world.population.CavePopulator;
import com.dfsek.terra.world.population.FloraPopulator;
import com.dfsek.terra.world.population.OrePopulator;
import com.dfsek.terra.world.population.StructurePopulator;
import com.dfsek.terra.world.population.TreePopulator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@@ -38,14 +46,20 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
private final TerraPlugin main; private final TerraPlugin main;
private final BlockType water; private final BlockType water;
private final SinglePalette<BlockData> blank; private final SinglePalette<BlockData> blank;
private final List<TerraBlockPopulator> blockPopulators = new ArrayList<>();
private final Carver carver; private final Carver carver;
public DefaultChunkGenerator3D(ConfigPack c, TerraPlugin main) { public DefaultChunkGenerator3D(ConfigPack c, TerraPlugin main) {
this.configPack = c; this.configPack = c;
this.main = main; this.main = main;
blockPopulators.add(new CavePopulator(main));
blockPopulators.add(new StructurePopulator(main));
blockPopulators.add(new OrePopulator(main));
blockPopulators.add(new TreePopulator(main));
blockPopulators.add(new FloraPopulator(main));
carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main); carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main);
water = main.getWorldHandle().createBlockData("minecraft:water").getBlockType(); water = main.getWorldHandle().createBlockData("minecraft:water").getBlockType();
blank = new SinglePalette<>(main.getWorldHandle().createBlockData("minecraft:air")); blank = new SinglePalette<>(main.getWorldHandle().createBlockData("minecraft:air"));
@@ -89,9 +103,10 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
@Override @Override
@SuppressWarnings({"try"}) @SuppressWarnings({"try"})
public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) {
TerraWorld tw = main.getWorld(world); try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_3d")) {
BiomeProvider grid = tw.getBiomeProvider(); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { BiomeProvider grid = tw.getBiomeProvider();
if(!tw.isSafe()) return chunk; if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4); int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4); int zOrig = (chunkZ << 4);
@@ -105,7 +120,7 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
int cx = xOrig + x; int cx = xOrig + x;
int cz = zOrig + z; int cz = zOrig + z;
TerraBiome b = grid.getBiome(xOrig + x, zOrig + z); TerraBiome b = grid.getBiome(cx, cz);
BiomeTemplate c = ((UserDefinedBiome) b).getConfig(); BiomeTemplate c = ((UserDefinedBiome) b).getConfig();
int sea = c.getSeaLevel(); int sea = c.getSeaLevel();
@@ -212,17 +227,20 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
return false; return false;
} }
@SuppressWarnings({"try"})
static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, TerraPlugin main) { static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, TerraPlugin main) {
int xOrig = (chunkX << 4); try(ProfileFrame ignore = main.getProfiler().profile("biomes")) {
int zOrig = (chunkZ << 4); int xOrig = (chunkX << 4);
BiomeProvider grid = main.getWorld(world).getBiomeProvider(); int zOrig = (chunkZ << 4);
for(int x = 0; x < 4; x++) { BiomeProvider grid = main.getWorld(world).getBiomeProvider();
for(int z = 0; z < 4; z++) { for(int x = 0; x < 4; x++) {
int cx = xOrig + (x << 2); for(int z = 0; z < 4; z++) {
int cz = zOrig + (z << 2); int cx = xOrig + (x << 2);
TerraBiome b = grid.getBiome(cx, cz); int cz = zOrig + (z << 2);
TerraBiome b = grid.getBiome(cx, cz);
biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz)); biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz));
}
} }
} }
} }
@@ -236,4 +254,9 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) { public Sampler createSampler(int chunkX, int chunkZ, BiomeProvider provider, World world, int elevationSmooth) {
return new Sampler3D(chunkX, chunkZ, provider, world, elevationSmooth); return new Sampler3D(chunkX, chunkZ, provider, world, elevationSmooth);
} }
@Override
public List<TerraBlockPopulator> getPopulators() {
return blockPopulators;
}
} }
@@ -25,7 +25,7 @@ public class SamplerCache {
public Sampler load(@NotNull Long key) { public Sampler load(@NotNull Long key) {
int cx = (int) (key >> 32); int cx = (int) (key >> 32);
int cz = (int) key.longValue(); int cz = (int) key.longValue();
return world.getGenerator().createSampler(cx, cz, world.getBiomeProvider(), world.getWorld(), world.getConfig().getTemplate().getElevationBlend()); return world.getWorld().getTerraGenerator().createSampler(cx, cz, world.getBiomeProvider(), world.getWorld(), world.getConfig().getTemplate().getElevationBlend());
} }
}); });
} }
@@ -9,11 +9,12 @@ import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.util.world.PopulationUtil; import com.dfsek.terra.api.util.world.PopulationUtil;
import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.carving.UserDefinedCarver; import com.dfsek.terra.carving.UserDefinedCarver;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.config.templates.CarverTemplate; import com.dfsek.terra.config.templates.CarverTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -23,7 +24,7 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
public class CavePopulator implements TerraBlockPopulator { public class CavePopulator implements TerraBlockPopulator, Chunkified {
private static final Map<BlockType, BlockData> shiftStorage = new HashMap<>(); // Persist BlockData created for shifts, to avoid re-calculating each time. private static final Map<BlockType, BlockData> shiftStorage = new HashMap<>(); // Persist BlockData created for shifts, to avoid re-calculating each time.
private final TerraPlugin main; private final TerraPlugin main;
@@ -37,48 +38,51 @@ public class CavePopulator implements TerraBlockPopulator {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
WorldHandle handle = main.getWorldHandle(); WorldHandle handle = main.getWorldHandle();
BlockData AIR = handle.createBlockData("minecraft:air"); BlockData AIR = handle.createBlockData("minecraft:air");
try(ProfileFuture ignored = tw.getProfiler().measure("CaveTime")) { try(ProfileFrame ignore = main.getProfiler().profile("carving")) {
Random random = PopulationUtil.getRandom(chunk); Random random = PopulationUtil.getRandom(chunk);
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
WorldConfig config = tw.getConfig(); WorldConfig config = tw.getConfig();
if(config.getTemplate().disableCarvers()) return;
for(UserDefinedCarver c : config.getCarvers()) { for(UserDefinedCarver c : config.getCarvers()) {
CarverTemplate template = c.getConfig(); CarverTemplate template = c.getConfig();
Map<Location, BlockData> shiftCandidate = new HashMap<>(); Map<Location, BlockData> shiftCandidate = new HashMap<>();
Set<Block> updateNeeded = new HashSet<>(); Set<Block> updateNeeded = new HashSet<>();
c.carve(chunk.getX(), chunk.getZ(), world, (v, type) -> { c.carve(chunk.getX(), chunk.getZ(), world, (v, type) -> {
Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); try(ProfileFrame ignored = main.getProfiler().profile("carving:" + c.getConfig().getID())) {
BlockData m = b.getBlockData(); Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ());
BlockType re = m.getBlockType(); BlockData m = b.getBlockData();
switch(type) { BlockType re = m.getBlockType();
case CENTER: switch(type) {
if(template.getInner().canReplace(re)) { case CENTER:
b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false); if(template.getInner().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case WALL: break;
if(template.getOuter().canReplace(re)) { case WALL:
b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false); if(template.getOuter().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case TOP: break;
if(template.getTop().canReplace(re)) { case TOP:
b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false); if(template.getTop().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case BOTTOM: break;
if(template.getBottom().canReplace(re)) { case BOTTOM:
b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false); if(template.getBottom().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
break;
}
} }
}); });
for(Map.Entry<Location, BlockData> entry : shiftCandidate.entrySet()) { for(Map.Entry<Location, BlockData> entry : shiftCandidate.entrySet()) {
@@ -91,7 +95,7 @@ public class CavePopulator implements TerraBlockPopulator {
if(template.getShift().get(entry.getValue().getBlockType()).contains(mut.getBlock().getBlockData().getBlockType())) { if(template.getShift().get(entry.getValue().getBlockType()).contains(mut.getBlock().getBlockData().getBlockType())) {
mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue().getBlockType(), BlockType::getDefaultData), false); mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue().getBlockType(), BlockType::getDefaultData), false);
} }
} catch(NullPointerException ignore) { } catch(NullPointerException ignored) {
} }
} }
for(Block b : updateNeeded) { for(Block b : updateNeeded) {
@@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.flora.FloraLayer; import com.dfsek.terra.world.population.items.flora.FloraLayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -32,7 +32,9 @@ public class FloraPopulator implements TerraBlockPopulator {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("FloraTime")) { try(ProfileFrame ignore = main.getProfiler().profile("flora")) {
if(tw.getConfig().getTemplate().disableFlora()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
BiomeProvider provider = tw.getBiomeProvider(); BiomeProvider provider = tw.getBiomeProvider();
Map<Vector2, List<FloraLayer>> layers = new HashMap<>(); Map<Vector2, List<FloraLayer>> layers = new HashMap<>();
@@ -10,7 +10,7 @@ import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -27,7 +27,9 @@ public class OrePopulator implements TerraBlockPopulator {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("OreTime")) { try(ProfileFrame ignore = main.getProfiler().profile("ore")) {
if(tw.getConfig().getTemplate().disableOres()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
for(int cx = -1; cx <= 1; cx++) { for(int cx = -1; cx <= 1; cx++) {
for(int cz = -1; cz <= 1; cz++) { for(int cz = -1; cz <= 1; cz++) {
@@ -38,11 +40,13 @@ public class OrePopulator implements TerraBlockPopulator {
BiomeTemplate config = ((UserDefinedBiome) b).getConfig(); BiomeTemplate config = ((UserDefinedBiome) b).getConfig();
int finalCx = cx; int finalCx = cx;
int finalCz = cz; int finalCz = cz;
config.getOreHolder().forEach((ore, oreConfig) -> { config.getOreHolder().forEach((id, orePair) -> {
int amount = oreConfig.getAmount().get(random); try(ProfileFrame ignored = main.getProfiler().profile("ore:" + id)) {
for(int i = 0; i < amount; i++) { int amount = orePair.getRight().getAmount().get(random);
Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, oreConfig.getHeight().get(random), random.nextInt(16) + 16 * finalCz); for(int i = 0; i < amount; i++) {
ore.generate(location, chunk, random); Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, orePair.getRight().getHeight().get(random), random.nextInt(16) + 16 * finalCz);
orePair.getLeft().generate(location, chunk, random);
}
} }
}); });
} }
@@ -9,10 +9,10 @@ import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.TerraStructure; import com.dfsek.terra.world.population.items.TerraStructure;
import net.jafama.FastMath; import net.jafama.FastMath;
@@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Random; import java.util.Random;
public class StructurePopulator implements TerraBlockPopulator { public class StructurePopulator implements TerraBlockPopulator, Chunkified {
private final TerraPlugin main; private final TerraPlugin main;
public StructurePopulator(TerraPlugin main) { public StructurePopulator(TerraPlugin main) {
@@ -31,7 +31,9 @@ public class StructurePopulator implements TerraBlockPopulator {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("StructureTime")) { try(ProfileFrame ignore = main.getProfiler().profile("structure")) {
if(tw.getConfig().getTemplate().disableStructures()) return;
int cx = (chunk.getX() << 4); int cx = (chunk.getX() << 4);
int cz = (chunk.getZ() << 4); int cz = (chunk.getZ() << 4);
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
@@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.tree.TreeLayer; import com.dfsek.terra.world.population.items.tree.TreeLayer;
import net.jafama.FastMath; import net.jafama.FastMath;
@@ -32,7 +32,9 @@ public class TreePopulator implements TerraBlockPopulator {
@SuppressWarnings("try") @SuppressWarnings("try")
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("TreeTime")) { try(ProfileFrame ignore = main.getProfiler().profile("tree")) {
if(tw.getConfig().getTemplate().disableTrees()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
BiomeProvider provider = tw.getBiomeProvider(); BiomeProvider provider = tw.getBiomeProvider();
Random random = PopulationUtil.getRandom(chunk); Random random = PopulationUtil.getRandom(chunk);
@@ -40,8 +42,9 @@ public class TreePopulator implements TerraBlockPopulator {
for(int z = 0; z < 16; z += 2) { for(int z = 0; z < 16; z += 2) {
UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z);
for(TreeLayer layer : biome.getConfig().getTrees()) { for(TreeLayer layer : biome.getConfig().getTrees()) {
if(layer.getDensity() >= random.nextDouble() * 100) if(layer.getDensity() >= random.nextDouble() * 100) {
layer.place(chunk, new Vector2(offset(random, x), offset(random, z))); layer.place(chunk, new Vector2(offset(random, x), offset(random, z)));
}
} }
} }
} }
@@ -16,7 +16,6 @@ public abstract class Ore {
protected TerraPlugin main; protected TerraPlugin main;
public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) { public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) {
this.material = material; this.material = material;
this.replaceable = replaceable; this.replaceable = replaceable;
this.applyGravity = applyGravity; this.applyGravity = applyGravity;
@@ -1,6 +1,7 @@
package com.dfsek.terra.world.population.items.ores; package com.dfsek.terra.world.population.items.ores;
import com.dfsek.terra.api.util.GlueList; import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.util.generic.pair.ImmutablePair;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@@ -11,22 +12,24 @@ import java.util.function.BiConsumer;
public class OreHolder { public class OreHolder {
private final List<Entry> entries = new GlueList<>(); private final List<Entry> entries = new GlueList<>();
public void forEach(BiConsumer<Ore, OreConfig> consumer) { public void forEach(BiConsumer<String, ImmutablePair<Ore, OreConfig>> consumer) {
entries.forEach(entry -> consumer.accept(entry.getOre(), entry.getConfig())); entries.forEach(entry -> consumer.accept(entry.getId(), ImmutablePair.of(entry.getOre(), entry.getConfig())));
} }
public OreHolder add(Ore ore, OreConfig config) { public OreHolder add(Ore ore, OreConfig config, String id) {
entries.add(new Entry(ore, config)); entries.add(new Entry(ore, config, id));
return this; return this;
} }
private static final class Entry { private static final class Entry {
private final Ore ore; private final Ore ore;
private final OreConfig config; private final OreConfig config;
private final String id;
private Entry(Ore ore, OreConfig config) { private Entry(Ore ore, OreConfig config, String id) {
this.ore = ore; this.ore = ore;
this.config = config; this.config = config;
this.id = id;
} }
public OreConfig getConfig() { public OreConfig getConfig() {
@@ -36,5 +39,9 @@ public class OreHolder {
public Ore getOre() { public Ore getOre() {
return ore; return ore;
} }
public String getId() {
return id;
}
} }
} }
+1 -2
View File
@@ -7,7 +7,6 @@ cache:
carver: 512 carver: 512
structure: 32 structure: 32
sampler: 128 sampler: 128
master-disable: biome-provider: 32
caves: false
script: script:
max-recursion: 1000 max-recursion: 1000
+122
View File
@@ -0,0 +1,122 @@
enable:
- "Pokud se vám líbí Terra, můžete nás podpořit na Patreon!"
- "Získáte přístup k experimentálním funkcím, než budou vydány!"
- "Podpořit projekt můžete zde: https://www.patreon.com/dfsek"
disable:
- "Děkujeme, že používáte Terra!"
command:
debug-only: "Tento příkaz musí být použit se zapnutým debug modem!"
player-only: "Tento příkaz je pouze pro hráče!"
invalid: "Neznámý příkaz. (Očekáváno %1$s argumentů, nalezeno %2$s)."
players-only: "Příkaz je pouze pro použití hráčem."
world: "Tento příkaz musí být použit v Terra světě!"
reload: "Obnoven Terra konfig."
reload-error: "Nastaly chyby při obnově Terra konfigurace. Podívejte se do logů pro více informací."
version: "Na tomto serveru běží Terra verze \"%1$s\", na platformě \"%2$s\""
main-menu:
- "--------------------Terra--------------------"
- "reload - Obnoví konfigurační data"
- "biome - Ukáže aktuální biom"
- "ore - Generuje žílu rud v lokaci, ve směru kam se koukáte (Pro debugging)"
- "save-data - Uložit populační data"
- "structure - Nahrát a exportovat struktury"
- "profile - Profiler možnosti"
- "image - Obrázek/GUI možnosti"
biome:
biome-found: "Lokalizován biom na (%1$s, %2$s)"
unable-to-locate: "Nelze lokalizovat biom."
invalid-radius: "Nesprávný radius: \"%s\""
invalid: "Nesprávné ID bomu: \"%s\""
in: "Jste v \"%s\""
packs:
main: "Aktuální instalované konfigurační balíčky:"
pack: " - %1$s v%3$s od %2$s"
none: "Žádné konfigurační balíčky nejsou nainstalovány."
ore:
main-menu:
- "---------------Terra/ore---------------"
- "Generuje žílu rud na bloku, na který koukáte."
out-of-range: "Blok mimo dosah"
invalid-ore: "Nelze nalézt Rudu \"%s\""
geometry:
main-menu:
- "---------------Terra/geometry----------------"
- "Různé voxel geometrické debugging příkazy"
- "sphere - Generuje kouli"
- "deformsphere - Generuje deformovanou kouli"
- "tube - Generuje válec"
deform:
invalid-radius: "Nesprávný radius: \"%s\""
invalid-deform: "Nesprávná deformace: \"%s\""
invalid-frequency: "Nesprávná frekvence: \"%s\""
sphere:
invalid-radius: "Nesprávný radius: \"%s\""
tube:
invalid-radius: "Nesprávný radius: \"%s\""
image:
main-menu:
- "---------------Terra/image---------------"
- "render - Vykreslí obrázek s danou šířkou a výškou, který může být později importován jako svět."
- "gui - Otevřít debug GUI (Musí být zapnuto v konfigu)"
gui:
main-menu:
- "-------------Terra/image/gui-------------"
- "raw - Otevře GUI s raw Biom daty"
- "step - Znovu vykreslí data k lepšímu zobrazení borderu"
debug: "Debug mod musí být zapnutý pro použití této možnosti! Debug GUI NENÍ PRODUKČNĚ BEZPEČNÉ!"
render:
save: "Uložen obrázek jako \"%s\""
error: "Nastala chyba při generování obrázku!"
profile:
main-menu:
- "---------------Terra/profile---------------"
- "start - Zapne profiler"
- "stop - Vypne profiler"
- "query - Ukáže profiler data"
- "reset - Resetuje profiler data"
reset: "Profiler byl resetován."
start: "Profiler byl zapnut."
stop: "Profiler byl vypnut."
structure:
main-menu:
- "---------------Terra/structure---------------"
- "export - Export vašeho výběru WorldEdit jako Terra struktura."
- "load - Načíst Terra strukturu"
invalid-radius: "Nesprávný radius: \"%s\""
invalid-rotation: "Nesprávná rotace: \"%s\""
invalid: "Nesprávné ID struktury: \"%s\""
export: "Uložena struktura do \"%s\""
world-config:
load: "Načteny konfigurační hodnoty světa pro svět \"%s\"..."
not-found: "Konfigurace pro svět \"%s\" nenalezena. Kopíruji defaultní konfig."
using-image: "Načítám svět z obrázku."
error: "Nelze načíst konfigurace pro svět %s"
done: "Načítání světa \"%1$s\" dokončeno. Uplynulý čas: %2$sms"
config-pack:
loaded: "Konfigurační balíček %1$s v%4$s od %3$s byl načten za %2$sms."
config:
loaded: "Načten %1$s ze souboru %2$s"
loaded-all: "Načteno %1$s %2$s(s) za %3$sms."
error:
invalid-failover: "Nesprávný typ failover: \"%s\""
duplicate: "Duplikační ID nalezeno v souboru: %s"
file:
- "Konfigurační chyba pro Terra objekt. Soubor: %1$s"
- "%2$s"
- "Opravte jej před pokračováním!"
generic:
- "Nastala chyba při načítání konfigurace."
- "Prosíme nahlašte to na Terra."
warning:
no-population: "Žádné populační chunky nebyly načteny. Pokud je to poprvé, co zapínáte server s Terra, či vytváříte nový svět, je tohle normální."
error:
severe-config: "Vážná konfigurační chyba zabránila Terra ze správné generace terénu na koordinátech: %1$s, %2$s. Prosíme zkontrolujte konfiguraci pro chyby. Jakékoli konfigurační chyby jsou vypsány výše."
debug:
data-save : "Uložena populační data."
use-paper:
- "Vypadá to, že používáte Spigot/CraftBukkit."
- "Ačkoli Terra &ofunguje&r na Spigot, některé funkce budou ztraceny. (Terra je netestována na CraftBukkit; žádná podpora nebude dána pro CraftBukkit)."
- "Pro nejvíce funkcí s Terra, použijte Paper."
- "Navíc, Paper přináší obrovské výkonnostní zlepešení než Spigot, a všechny Spigot pluginy by měli fungovat s Paper!"
- "Pro nejlepší zážitek s Terra a všemi pluginy, použijte prosím Paper."
- "Více info najdete na stránce Paperu: https://papermc.io/"
@@ -39,6 +39,8 @@ import com.dfsek.terra.config.loaders.config.biome.templates.provider.SingleBiom
import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader; import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.AbstractableTemplate; import com.dfsek.terra.config.templates.AbstractableTemplate;
import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.registry.config.BiomeRegistry; import com.dfsek.terra.registry.config.BiomeRegistry;
import com.dfsek.terra.registry.config.NoiseRegistry; import com.dfsek.terra.registry.config.NoiseRegistry;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
@@ -136,6 +138,11 @@ public class DistributionTest {
return null; return null;
} }
@Override
public Profiler getProfiler() {
return null;
}
@Override @Override
public void register(TypeRegistry registry) { public void register(TypeRegistry registry) {
@@ -152,7 +159,7 @@ public class DistributionTest {
new GenericLoaders(MAIN).register(loader); new GenericLoaders(MAIN).register(loader);
BiomeRegistry biomeRegistry = new BiomeRegistry(); BiomeRegistry biomeRegistry = new BiomeRegistry();
folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.load(inputStreams, TestBiome::new), MAIN)); folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.loadConfigs(inputStreams, TestBiome::new), MAIN));
BiomeProviderTemplate template = new BiomeProviderTemplate(); BiomeProviderTemplate template = new BiomeProviderTemplate();
ConfigLoader pipeLoader = new ConfigLoader() ConfigLoader pipeLoader = new ConfigLoader()
@@ -306,6 +313,11 @@ public class DistributionTest {
return null; return null;
} }
@Override
public BiomeTemplate getTemplate() {
return null;
}
@Override @Override
public Generator getGenerator(World w) { public Generator getGenerator(World w) {
return null; return null;
+1 -1
View File
@@ -46,7 +46,7 @@ public class ImageTest {
OpenRegistry<TerraBiome> biomeRegistry = new OpenRegistry<TerraBiome>() { OpenRegistry<TerraBiome> biomeRegistry = new OpenRegistry<TerraBiome>() {
}; };
folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.load(inputStreams, TestBiome::new), null)); folderLoader.open("biomes", ".yml").then(inputStreams -> ConfigPack.buildAll((template, main) -> template, biomeRegistry, loader.loadConfigs(inputStreams, TestBiome::new), null));
return new ImageBiomeProvider(biomeRegistry.entries(), ImageIO.read(ImageTest.class.getResourceAsStream("/map.jpg")), 1, ImageBiomeProvider.Align.CENTER); return new ImageBiomeProvider(biomeRegistry.entries(), ImageIO.read(ImageTest.class.getResourceAsStream("/map.jpg")), 1, ImageBiomeProvider.Align.CENTER);
} }
@@ -0,0 +1,63 @@
package profiler;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
public class ProfilerTest {
private static final Profiler PROFILER = new ProfilerImpl();
//@Test
public static void main(String... a) throws InterruptedException {
//PROFILER.start();
for(int i = 0; i < 1000; i++) {
doThing();
}
for(int i = 0; i < 100; i++) {
doThirdOtherThing();
}
for(int i = 0; i < 100; i++) {
doOtherThing();
}
PROFILER.stop();
PROFILER.push("thing");
PROFILER.push("thing2");
PROFILER.start();
PROFILER.pop("thing2");
PROFILER.pop("thing");
PROFILER.push("thing4");
PROFILER.pop("thing4");
PROFILER.getTimings().forEach((id, timings) -> {
System.out.println(id + ": " + timings.toString());
});
}
private static void doThing() throws InterruptedException {
PROFILER.push("thing");
Thread.sleep(1);
doOtherThing();
thing4();
PROFILER.pop("thing");
}
private static void doOtherThing() throws InterruptedException {
PROFILER.push("thing2");
Thread.sleep(2);
doThirdOtherThing();
thing4();
PROFILER.pop("thing2");
}
private static void doThirdOtherThing() throws InterruptedException {
PROFILER.push("thing3");
Thread.sleep(2);
PROFILER.pop("thing3");
}
private static void thing4() throws InterruptedException {
PROFILER.push("thing4");
Thread.sleep(2);
PROFILER.pop("thing4");
}
}
@@ -13,6 +13,7 @@ import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -20,7 +21,7 @@ import java.util.Map;
public class ParserTest { public class ParserTest {
@Test @Test
public void parse() throws IOException, ParseException { public void parse() throws IOException, ParseException {
Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"))); Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"), Charset.defaultCharset()));
parser.registerFunction("test", new FunctionBuilder<Test1>() { parser.registerFunction("test", new FunctionBuilder<Test1>() {
@Override @Override
+15
View File
@@ -0,0 +1,15 @@
# Magic options for more perf
org.gradle.jvmargs=-Xmx2048M -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.vfs.watch=true
kapt.use.worker.api=true
kapt.include.compile.classpath=false
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.warning.mode=all
#org.gradle.logging.level=info
# Project information
terra.description=A fast, modern, extensible, platform-agnostic data-driven world generator
terra.source=https://github.com/PolyhedralDev/Terra
terra.issues=https://github.com/PolyhedralDev/Terra/issues
terra.wiki=https://github.com/PolyhedralDev/Terra/wiki
terra.license=GNU General Public License, version 3.0
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
org.gradle.jvmargs=-Xmx4096m org.gradle.jvmargs=-Xmx4096m
+138 -56
View File
@@ -15,12 +15,12 @@ configureCommon()
group = "com.dfsek.terra.bukkit" group = "com.dfsek.terra.bukkit"
repositories { val mcVersion = "1.16.5"
mavenCentral() val testDir = "target/server"
maven { url = uri("http://maven.enginehub.org/repo/") } val testMem = "3G"
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") } val paperURL = "https://papermc.io/api/v1/paper/%version%/latest/download/"
} val purpurURL = "https://ci.pl3x.net/job/Purpur/lastSuccessfulBuild/artifact/final/purpurclip.jar"
dependencies { dependencies {
"shadedApi"(project(":common")) "shadedApi"(project(":common"))
@@ -32,76 +32,159 @@ dependencies {
"compileOnly"("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT") "compileOnly"("com.sk89q.worldedit:worldedit-bukkit:7.2.0-SNAPSHOT")
"shadedImplementation"("com.google.guava:guava:30.0-jre") "shadedApi"("com.google.guava:guava:30.0-jre")
} }
val testDir = "target/server/" val jvmFlags = 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", "-javaagent:paperclip.jar")
val setupServer = tasks.create("setupServer") { fun downloadPaperclip(url: String, dir: String) {
dependsOn("shadowJar") val clip = URL(url.replace("%version%", mcVersion))
doFirst { val clipReadableByteChannel = Channels.newChannel(clip.openStream())
// clean val clipFile = file("$testDir/$dir/paperclip.jar")
file("${testDir}/").deleteRecursively() val clipOutputStream = clipFile.outputStream()
file("${testDir}/plugins").mkdirs() val clipFileChannel = clipOutputStream.channel
clipFileChannel.transferFrom(clipReadableByteChannel, 0, Long.MAX_VALUE)
}
// Downloading latest paper jar. fun copyTerra(dir: String) {
val paperUrl = URL("https://papermc.io/api/v1/paper/1.16.4/latest/download") file("$testDir/$dir").walk().forEach {
val paperReadableByteChannel = Channels.newChannel(paperUrl.openStream()) if(it.name.startsWith("Terra-") && it.name.endsWith(".jar")) it.delete() // Delete old Terra jar(s)
val paperFile = file("${testDir}/paper.jar") }
val paperFileOutputStream = paperFile.outputStream() copy {
val paperFileChannel = paperFileOutputStream.channel from("$buildDir/libs/Terra-bukkit-$version-shaded.jar")
paperFileChannel.transferFrom(paperReadableByteChannel, 0, Long.MAX_VALUE) into("$testDir/$dir/plugins/")
// 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 testWithPaper = task<JavaExec>(name = "testWithPaper") { fun installServer(dir: String) {
// clean
file("$testDir/$dir").deleteRecursively()
file("$testDir/$dir/plugins").mkdirs()
// Cloning test setup.
gitClone("https://github.com/PolyhedralDev/WorldGenTestServer")
// Copying plugins
Files.move(file("WorldGenTestServer/plugins").toPath(),
file("$testDir/$dir/plugins").toPath(),
StandardCopyOption.REPLACE_EXISTING)
// Copying config
val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText()
file("$testDir/$dir/server.properties").writeText(serverText)
val bukkitText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/bukkit.yml").readText()
file("$testDir/$dir/bukkit.yml").writeText(bukkitText.replace("\${world}", "world").replace("\${gen}", "Terra:DEFAULT"))
println("By proceeding, you are agreeing to the Minecraft EULA: https://account.mojang.com/documents/minecraft_eula")
file("$testDir/$dir/eula.txt").writeText("eula=true")
// clean up
file("WorldGenTestServer").deleteRecursively()
}
fun deleteFolder(folder: File) {
if(folder.exists()) folder.deleteRecursively()
}
fun deleteFile(file: File) {
if(file.exists()) file.delete()
}
tasks.create("cleanWorlds") {
group = "bukkit"
doFirst {
deleteFolder(file("$testDir/paper/world"))
deleteFolder(file("$testDir/paper/world_nether"))
deleteFolder(file("$testDir/paper/world_the_end"))
deleteFolder(file("$testDir/purpur/world"))
deleteFolder(file("$testDir/purpur/world_nether"))
deleteFolder(file("$testDir/purpur/world_the_end"))
}
}
tasks.create("updatePaper") {
group = "bukkit"
doFirst {
deleteFile(file("$testDir/paper/paperclip.jar"))
downloadPaperclip(paperURL, "paper")
}
}
tasks.create("updatePurpur") {
group = "bukkit"
doFirst {
deleteFile(file("$testDir/paper/paperclip.jar"))
downloadPaperclip(purpurURL, "purpur")
}
}
tasks.create("installPaper") {
group = "bukkit"
dependsOn("shadowJar")
doFirst {
installServer("paper")
// Downloading latest paper jar.
downloadPaperclip(paperURL, "paper")
}
}
tasks.create("installPurpur") {
group = "bukkit"
dependsOn("shadowJar")
doFirst {
installServer("purpur")
// Downloading latest paper jar.
downloadPaperclip(purpurURL, "purpur")
}
}
task<JavaExec>(name = "runPaper") {
group = "bukkit"
standardInput = System.`in` standardInput = System.`in`
dependsOn("shadowJar") dependsOn("shadowJar")
// Copy Terra into dir // Copy Terra into dir
doFirst { doFirst {
copy { copyTerra("paper")
from("${buildDir}/libs/Terra-bukkit-${version}-shaded.jar")
into("${testDir}/plugins/")
}
} }
main = "io.papermc.paperclip.Paperclip" main = "io.papermc.paperclip.Paperclip"
jvmArgs = listOf("-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200", jvmArgs = jvmFlags
"-XX:+UnlockExperimentalVMOptions", "-XX:+DisableExplicitGC", "-XX:+AlwaysPreTouch", maxHeapSize = testMem
"-XX:G1NewSizePercent=30", "-XX:G1MaxNewSizePercent=40", "-XX:G1HeapRegionSize=8M", minHeapSize = testMem
"-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 = "4G"
minHeapSize = "4G"
//args = listOf("nogui") //args = listOf("nogui")
workingDir = file("${testDir}/") workingDir = file("$testDir/paper")
classpath = files("${testDir}/paper.jar") classpath = files("$testDir/paper/paperclip.jar")
}
task<JavaExec>(name = "runPurpur") {
group = "bukkit"
standardInput = System.`in`
dependsOn("shadowJar")
// Copy Terra into dir
doFirst {
copyTerra("purpur")
}
main = "io.papermc.paperclip.Paperclip"
jvmArgs = jvmFlags
maxHeapSize = testMem
minHeapSize = testMem
//args = listOf("nogui")
workingDir = file("$testDir/purpur")
classpath = files("$testDir/purpur/paperclip.jar")
} }
tasks.named<ShadowJar>("shadowJar") { tasks.named<ShadowJar>("shadowJar") {
relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats") relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats")
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib") relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("com.google.common", "com.dfsek.terra.lib.google.common")
} }
publishing { publishing {
publications { publications {
create<MavenPublication>("mavenJava") { create<MavenPublication>("mavenJava") {
@@ -112,7 +195,6 @@ publishing {
repositories { repositories {
val mavenUrl = "https://repo.codemc.io/repository/maven-releases/" val mavenUrl = "https://repo.codemc.io/repository/maven-releases/"
val mavenSnapshotUrl = "https://repo.codemc.io/repository/maven-snapshots/"
maven(mavenUrl) { maven(mavenUrl) {
val mavenUsername: String? by project val mavenUsername: String? by project
@@ -34,12 +34,15 @@ import com.dfsek.terra.bukkit.listeners.SpigotListener;
import com.dfsek.terra.bukkit.listeners.TerraListener; import com.dfsek.terra.bukkit.listeners.TerraListener;
import com.dfsek.terra.bukkit.util.PaperUtil; import com.dfsek.terra.bukkit.util.PaperUtil;
import com.dfsek.terra.bukkit.world.BukkitBiome; import com.dfsek.terra.bukkit.world.BukkitBiome;
import com.dfsek.terra.bukkit.world.BukkitWorld;
import com.dfsek.terra.commands.CommandUtil; import com.dfsek.terra.commands.CommandUtil;
import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.GenericLoaders;
import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.config.lang.LangUtil;
import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.AddonRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
@@ -64,6 +67,8 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin {
private final Map<World, TerraWorld> worldMap = new HashMap<>(); private final Map<World, TerraWorld> worldMap = new HashMap<>();
private final Map<String, ConfigPack> worlds = new HashMap<>(); private final Map<String, ConfigPack> worlds = new HashMap<>();
private final Profiler profiler = new ProfilerImpl();
private final ConfigRegistry registry = new ConfigRegistry(); private final ConfigRegistry registry = new ConfigRegistry();
private final CheckedRegistry<ConfigPack> checkedRegistry = new CheckedRegistry<>(registry); private final CheckedRegistry<ConfigPack> checkedRegistry = new CheckedRegistry<>(registry);
@@ -141,6 +146,11 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin {
Bukkit.getScheduler().runTask(this, task); Bukkit.getScheduler().runTask(this, task);
} }
@Override
public Profiler getProfiler() {
return profiler;
}
@Override @Override
public void onDisable() { public void onDisable() {
BukkitChunkGeneratorWrapper.saveAll(); BukkitChunkGeneratorWrapper.saveAll();
@@ -259,14 +269,15 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin {
return checkedRegistry; return checkedRegistry;
} }
public TerraWorld getWorld(World w) { public TerraWorld getWorld(World world) {
if(!TerraWorld.isTerraWorld(w)) BukkitWorld w = (BukkitWorld) world;
if(!w.isTerraWorld())
throw new IllegalArgumentException("Not a Terra world! " + w.getGenerator()); throw new IllegalArgumentException("Not a Terra world! " + w.getGenerator());
if(!worlds.containsKey(w.getName())) { if(!worlds.containsKey(w.getName())) {
getLogger().warning("Unexpected world load detected: \"" + w.getName() + "\""); getLogger().warning("Unexpected world load detected: \"" + w.getName() + "\"");
return new TerraWorld(w, ((TerraChunkGenerator) w.getGenerator().getHandle()).getConfigPack(), this); return new TerraWorld(w, ((TerraChunkGenerator) w.getGenerator().getHandle()).getConfigPack(), this);
} }
return worldMap.computeIfAbsent(w, world -> new TerraWorld(w, worlds.get(w.getName()), this)); return worldMap.computeIfAbsent(w, w2 -> new TerraWorld(w, worlds.get(w.getName()), this));
} }
@Override @Override
@@ -3,19 +3,11 @@ package com.dfsek.terra.bukkit.generator;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.bukkit.population.PopulationManager; import com.dfsek.terra.bukkit.population.PopulationManager;
import com.dfsek.terra.bukkit.world.BukkitAdapter; import com.dfsek.terra.bukkit.world.BukkitAdapter;
import com.dfsek.terra.bukkit.world.BukkitBiomeGrid; import com.dfsek.terra.bukkit.world.BukkitBiomeGrid;
import com.dfsek.terra.profiler.DataType;
import com.dfsek.terra.profiler.Measurement;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.CavePopulator;
import com.dfsek.terra.world.population.FloraPopulator;
import com.dfsek.terra.world.population.OrePopulator;
import com.dfsek.terra.world.population.StructurePopulator;
import com.dfsek.terra.world.population.TreePopulator;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
@@ -23,12 +15,11 @@ import org.jetbrains.annotations.NotNull;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors;
public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper {
@@ -40,20 +31,13 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
private final TerraPlugin main; private final TerraPlugin main;
private final List<TerraBlockPopulator> populators = new LinkedList<>();
private boolean needsLoad = true; private boolean needsLoad = true;
public BukkitChunkGeneratorWrapper(TerraChunkGenerator delegate) { public BukkitChunkGeneratorWrapper(TerraChunkGenerator delegate) {
this.delegate = delegate; this.delegate = delegate;
this.main = delegate.getMain(); this.main = delegate.getMain();
popMan = new PopulationManager(main); this.popMan = new PopulationManager(delegate, main);
popMan.attach(new OrePopulator(main));
popMan.attach(new TreePopulator(main));
popMan.attach(new FloraPopulator(main));
populators.add(new CavePopulator(main));
populators.add(new StructurePopulator(main));
populators.add(popMan);
} }
@@ -68,7 +52,7 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
} }
public static synchronized void fixChunk(Chunk c) { public static synchronized void fixChunk(Chunk c) {
if(!TerraWorld.isTerraWorld(c.getWorld())) throw new IllegalArgumentException(); if(!c.getWorld().isTerraWorld()) throw new IllegalArgumentException();
popMap.get(c.getWorld()).checkNeighbors(c.getX(), c.getZ(), c.getWorld()); popMap.get(c.getWorld()).checkNeighbors(c.getX(), c.getZ(), c.getWorld());
} }
@@ -81,8 +65,6 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
e.printStackTrace(); e.printStackTrace();
} }
popMap.put(w, popMan); popMap.put(w, popMan);
main.getWorld(w).getProfiler().addMeasurement(new Measurement(15000000, DataType.PERIOD_MILLISECONDS), "PopulationManagerTime");
popMan.attachProfiler(main.getWorld(w).getProfiler());
needsLoad = false; needsLoad = false;
} }
@@ -96,7 +78,7 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
@Override @Override
public @NotNull List<BlockPopulator> getDefaultPopulators(@NotNull World world) { public @NotNull List<BlockPopulator> getDefaultPopulators(@NotNull World world) {
return populators.stream().map(BukkitPopulatorWrapper::new).collect(Collectors.toList()); return Arrays.asList(popMan, new BukkitPopulatorWrapper(delegate));
} }
@Override @Override
@@ -1,27 +0,0 @@
package com.dfsek.terra.bukkit.generator;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.platform.world.generator.BlockPopulator;
import com.dfsek.terra.bukkit.world.BukkitChunk;
import com.dfsek.terra.bukkit.world.BukkitWorld;
import java.util.Random;
public class BukkitPopulator implements BlockPopulator {
private final org.bukkit.generator.BlockPopulator handle;
public BukkitPopulator(org.bukkit.generator.BlockPopulator handle) {
this.handle = handle;
}
@Override
public void populate(World world, Random random, Chunk chunk) {
handle.populate(((BukkitWorld) world).getHandle(), random, ((BukkitChunk) chunk).getHandle());
}
@Override
public org.bukkit.generator.BlockPopulator getHandle() {
return handle;
}
}
@@ -1,6 +1,8 @@
package com.dfsek.terra.bukkit.generator; package com.dfsek.terra.bukkit.generator;
import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.bukkit.world.BukkitAdapter; import com.dfsek.terra.bukkit.world.BukkitAdapter;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.World; import org.bukkit.World;
@@ -10,14 +12,18 @@ import org.jetbrains.annotations.NotNull;
import java.util.Random; import java.util.Random;
public class BukkitPopulatorWrapper extends BlockPopulator { public class BukkitPopulatorWrapper extends BlockPopulator {
private final TerraBlockPopulator delegate; private final TerraChunkGenerator delegate;
public BukkitPopulatorWrapper(TerraBlockPopulator delegate) { public BukkitPopulatorWrapper(TerraChunkGenerator delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@Override @Override
public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk source) { public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk source) {
delegate.populate(BukkitAdapter.adapt(world), BukkitAdapter.adapt(source)); delegate.getPopulators().forEach(populator -> {
if(populator instanceof Chunkified) {
populator.populate(BukkitAdapter.adapt(world), BukkitAdapter.adapt(source));
}
});
} }
} }
@@ -44,7 +44,7 @@ public class CommonListener implements Listener {
public void onSaplingGrow(StructureGrowEvent e) { public void onSaplingGrow(StructureGrowEvent e) {
if(e.isCancelled()) return; if(e.isCancelled()) return;
World bukkit = BukkitAdapter.adapt(e.getWorld()); World bukkit = BukkitAdapter.adapt(e.getWorld());
if(!TerraWorld.isTerraWorld(bukkit)) return; if(!bukkit.isTerraWorld()) return;
TerraWorld tw = main.getWorld(bukkit); TerraWorld tw = main.getWorld(bukkit);
WorldConfig c = tw.getConfig(); WorldConfig c = tw.getConfig();
if(c.getTemplate().isDisableSaplings()) return; if(c.getTemplate().isDisableSaplings()) return;
@@ -18,8 +18,7 @@ public class PaperListener implements Listener {
@EventHandler @EventHandler
public void onStructureLocate(StructureLocateEvent e) { public void onStructureLocate(StructureLocateEvent e) {
if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getWorld()))) return; if(!BukkitAdapter.adapt(e.getWorld()).isTerraWorld()) return;
e.setResult(null); // Assume no result.
String name = "minecraft:" + e.getType().getName(); String name = "minecraft:" + e.getType().getName();
main.getDebugLogger().info("Overriding structure location for \"" + name + "\""); main.getDebugLogger().info("Overriding structure location for \"" + name + "\"");
TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getWorld())); TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getWorld()));
@@ -32,10 +31,8 @@ public class PaperListener implements Listener {
}, main); }, main);
finder.run(); // Do this synchronously. finder.run(); // Do this synchronously.
} else { } else {
main.logger().warning("No overrides are defined for \"" + name + "\""); e.setResult(e.getOrigin());
main.logger().warning("No overrides are defined for \"" + name + "\". Locating this structure will NOT work properly!");
} }
} }
} }
@@ -34,7 +34,7 @@ public class SpigotListener implements Listener {
Entity entity = e.getEntity(); Entity entity = e.getEntity();
if(e.getEntityType().equals(EntityType.ENDER_SIGNAL)) { if(e.getEntityType().equals(EntityType.ENDER_SIGNAL)) {
main.getDebugLogger().info("Detected Ender Signal..."); main.getDebugLogger().info("Detected Ender Signal...");
if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return;
TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getEntity().getWorld())); TerraWorld tw = main.getWorld(BukkitAdapter.adapt(e.getEntity().getWorld()));
EnderSignal signal = (EnderSignal) entity; EnderSignal signal = (EnderSignal) entity;
TerraStructure config = tw.getConfig().getStructureRegistry().get(tw.getConfig().getTemplate().getLocatable().get("STRONGHOLD")); TerraStructure config = tw.getConfig().getStructureRegistry().get(tw.getConfig().getTemplate().getLocatable().get("STRONGHOLD"));
@@ -53,7 +53,7 @@ public class SpigotListener implements Listener {
@EventHandler @EventHandler
public void onCartographerChange(VillagerAcquireTradeEvent e) { public void onCartographerChange(VillagerAcquireTradeEvent e) {
if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return;
if(!(e.getEntity() instanceof Villager)) return; if(!(e.getEntity() instanceof Villager)) return;
if(((Villager) e.getEntity()).getProfession().equals(Villager.Profession.CARTOGRAPHER)) { if(((Villager) e.getEntity()).getProfession().equals(Villager.Profession.CARTOGRAPHER)) {
main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning."); main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning.");
@@ -65,7 +65,7 @@ public class SpigotListener implements Listener {
@EventHandler @EventHandler
public void onCartographerLevel(VillagerCareerChangeEvent e) { public void onCartographerLevel(VillagerCareerChangeEvent e) {
if(!TerraWorld.isTerraWorld(BukkitAdapter.adapt(e.getEntity().getWorld()))) return; if(!BukkitAdapter.adapt(e.getEntity().getWorld()).isTerraWorld()) return;
if(e.getProfession().equals(Villager.Profession.CARTOGRAPHER)) { if(e.getProfession().equals(Villager.Profession.CARTOGRAPHER)) {
main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning."); main.logger().severe("Prevented server crash by stopping Cartographer villager from spawning.");
main.logger().severe("Please upgrade to Paper, which has a StructureLocateEvent that fixes this issue"); main.logger().severe("Please upgrade to Paper, which has a StructureLocateEvent that fixes this issue");
@@ -22,7 +22,9 @@ public class TerraListener implements EventListener {
public void injectTrees(ConfigPackPreLoadEvent event) { public void injectTrees(ConfigPackPreLoadEvent event) {
for(TreeType value : TreeType.values()) { for(TreeType value : TreeType.values()) {
try { try {
event.getPack().getTreeRegistry().add(BukkitAdapter.TREE_TRANSFORMER.translate(value), new BukkitTree(value, main)); String id = BukkitAdapter.TREE_TRANSFORMER.translate(value);
event.getPack().getTreeRegistry().add(id, new BukkitTree(value, main));
event.getPack().getTreeRegistry().get(id); // Platform trees should never be marked "dead"
} catch(DuplicateEntryException ignore) { // If another addon has already registered trees, do nothing. } catch(DuplicateEntryException ignore) { // If another addon has already registered trees, do nothing.
} }
} }
@@ -2,6 +2,7 @@ package com.dfsek.terra.bukkit.population;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.bukkit.world.BukkitWorld;
import java.io.Serializable; import java.io.Serializable;
import java.util.UUID; import java.util.UUID;
@@ -21,7 +22,7 @@ public class ChunkCoordinate implements Serializable {
public ChunkCoordinate(Chunk c) { public ChunkCoordinate(Chunk c) {
this.x = c.getX(); this.x = c.getX();
this.z = c.getZ(); this.z = c.getZ();
this.worldID = c.getWorld().getUID(); this.worldID = ((BukkitWorld) c.getWorld()).getUID();
} }
public UUID getWorldID() { public UUID getWorldID() {
@@ -1,20 +0,0 @@
package com.dfsek.terra.bukkit.population;
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;
}
}
@@ -4,80 +4,57 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList; import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.bukkit.TerraBukkitPlugin; import com.dfsek.terra.bukkit.TerraBukkitPlugin;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.bukkit.world.BukkitAdapter;
import com.dfsek.terra.profiler.WorldProfiler; import com.dfsek.terra.bukkit.world.BukkitWorld;
import com.dfsek.terra.profiler.ProfileFrame;
import org.bukkit.generator.BlockPopulator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Random; import java.util.Random;
/** /**
* Cursed management class for the horrors of Bukkit population * Cursed management class for the horrors of Bukkit population
*/ */
public class PopulationManager implements TerraBlockPopulator { public class PopulationManager extends BlockPopulator {
private final List<TerraBlockPopulator> attachedPopulators = new GlueList<>(); private final TerraChunkGenerator generator;
private final HashSet<ChunkCoordinate> needsPop = new HashSet<>(); private final HashSet<ChunkCoordinate> needsPop = new HashSet<>();
private final TerraPlugin main; private final TerraPlugin main;
private WorldProfiler profiler;
public PopulationManager(TerraPlugin main) { public PopulationManager(TerraChunkGenerator generator, TerraPlugin main) {
this.generator = generator;
this.main = main; this.main = main;
} }
public void attach(TerraBlockPopulator populator) {
this.attachedPopulators.add(populator);
}
@Override
@SuppressWarnings("try")
public void populate(@NotNull World world, @NotNull Chunk chunk) {
try(ProfileFuture ignored = measure()) {
needsPop.add(new ChunkCoordinate(chunk));
int x = chunk.getX();
int z = chunk.getZ();
if(((TerraBukkitPlugin) main).isEnabled()) {
for(int xi = -1; xi <= 1; xi++) {
for(int zi = -1; zi <= 1; zi++) {
if(xi == 0 && zi == 0) continue;
if(world.isChunkGenerated(xi + x, zi + z)) checkNeighbors(xi + x, zi + z, world);
}
}
}
}
}
private ProfileFuture measure() {
if(profiler != null) return profiler.measure("PopulationManagerTime");
return null;
}
public void attachProfiler(WorldProfiler p) {
this.profiler = p;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized void saveBlocks(World w) throws IOException { public synchronized void saveBlocks(World w) throws IOException {
File f = new File(Gaea.getGaeaFolder(w), "chunks.bin"); File f = new File(getDataFolder(w), "chunks.bin");
f.createNewFile(); f.createNewFile();
SerializationUtil.toFile((HashSet<ChunkCoordinate>) needsPop.clone(), f); SerializationUtil.toFile((HashSet<ChunkCoordinate>) needsPop.clone(), f);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized void loadBlocks(World w) throws IOException, ClassNotFoundException { public synchronized void loadBlocks(World w) throws IOException, ClassNotFoundException {
File f = new File(Gaea.getGaeaFolder(w), "chunks.bin"); File f = new File(getDataFolder(w), "chunks.bin");
needsPop.addAll((HashSet<ChunkCoordinate>) SerializationUtil.fromFile(f)); needsPop.addAll((HashSet<ChunkCoordinate>) SerializationUtil.fromFile(f));
} }
public static File getDataFolder(World w) {
File f = new File(((BukkitWorld) w).getWorldFolder(), "gaea");
f.mkdirs();
return f;
}
// Synchronize to prevent chunks from being queued for population multiple times. // Synchronize to prevent chunks from being queued for population multiple times.
public synchronized void checkNeighbors(int x, int z, World w) { public synchronized void checkNeighbors(int x, int z, World world) {
ChunkCoordinate c = new ChunkCoordinate(x, z, w.getUID()); BukkitWorld w = (BukkitWorld) world;
ChunkCoordinate c = new ChunkCoordinate(x, z, (w).getUID());
if(w.isChunkGenerated(x + 1, z) if(w.isChunkGenerated(x + 1, z)
&& w.isChunkGenerated(x - 1, z) && w.isChunkGenerated(x - 1, z)
&& w.isChunkGenerated(x, z + 1) && w.isChunkGenerated(x, z + 1)
@@ -87,10 +64,31 @@ public class PopulationManager implements TerraBlockPopulator {
long zRand = (random.nextLong() / 2L << 1L) + 1L; long zRand = (random.nextLong() / 2L << 1L) + 1L;
random.setSeed((long) x * xRand + (long) z * zRand ^ w.getSeed()); random.setSeed((long) x * xRand + (long) z * zRand ^ w.getSeed());
Chunk currentChunk = w.getChunkAt(x, z); Chunk currentChunk = w.getChunkAt(x, z);
for(TerraBlockPopulator r : attachedPopulators) { generator.getPopulators().forEach(populator -> {
r.populate(w, currentChunk); if(!(populator instanceof Chunkified)) {
} populator.populate(w, currentChunk);
}
});
needsPop.remove(c); needsPop.remove(c);
} }
} }
@Override
@SuppressWarnings("try")
public void populate(org.bukkit.@NotNull World world, @NotNull Random random, org.bukkit.@NotNull Chunk source) {
try(ProfileFrame ignore = main.getProfiler().profile("popman")) {
Chunk chunk = BukkitAdapter.adapt(source);
needsPop.add(new ChunkCoordinate(chunk));
int x = chunk.getX();
int z = chunk.getZ();
if(((TerraBukkitPlugin) main).isEnabled()) {
for(int xi = -1; xi <= 1; xi++) {
for(int zi = -1; zi <= 1; zi++) {
if(xi == 0 && zi == 0) continue;
if(world.isChunkGenerated(xi + x, zi + z)) checkNeighbors(xi + x, zi + z, BukkitAdapter.adapt(world));
}
}
}
}
}
} }
@@ -5,8 +5,10 @@ import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.handle.WorldHandle; import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Tree; import com.dfsek.terra.api.platform.world.Tree;
import com.dfsek.terra.api.util.collections.MaterialSet; import com.dfsek.terra.api.util.collections.MaterialSet;
import com.dfsek.terra.profiler.ProfileFrame;
import org.bukkit.TreeType; import org.bukkit.TreeType;
import java.util.Locale;
import java.util.Random; import java.util.Random;
public class BukkitTree implements Tree { public class BukkitTree implements Tree {
@@ -40,8 +42,11 @@ public class BukkitTree implements Tree {
} }
@Override @Override
@SuppressWarnings("try")
public boolean plant(Location l, Random r) { public boolean plant(Location l, Random r) {
return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate); try(ProfileFrame ignore = main.getProfiler().profile("bukkit_tree:" + delegate.toString().toLowerCase(Locale.ROOT))) {
return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate);
}
} }
@Override @Override
@@ -37,17 +37,14 @@ public class BukkitWorld implements World {
return new BukkitChunkGenerator(delegate.getGenerator()); return new BukkitChunkGenerator(delegate.getGenerator());
} }
@Override
public String getName() { public String getName() {
return delegate.getName(); return delegate.getName();
} }
@Override
public UUID getUID() { public UUID getUID() {
return delegate.getUID(); return delegate.getUID();
} }
@Override
public boolean isChunkGenerated(int x, int z) { public boolean isChunkGenerated(int x, int z) {
return delegate.isChunkGenerated(x, z); return delegate.isChunkGenerated(x, z);
} }
@@ -57,7 +54,6 @@ public class BukkitWorld implements World {
return BukkitAdapter.adapt(delegate.getChunkAt(x, z)); return BukkitAdapter.adapt(delegate.getChunkAt(x, z));
} }
@Override
public File getWorldFolder() { public File getWorldFolder() {
return delegate.getWorldFolder(); return delegate.getWorldFolder();
} }

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