mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-05-20 00:30:20 +00:00
Compare commits
335 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed2fa85d81 | |||
| 788a3c2d48 | |||
| 1bd5cf31fa | |||
| bb2601252a | |||
| 0600176e31 | |||
| 51c51111a2 | |||
| d3e4270f44 | |||
| 4f3f555aa0 | |||
| cf0d5cd99c | |||
| e3bbedb56b | |||
| 3f37907256 | |||
| 486dcfc63d | |||
| 34947c2168 | |||
| fa164e5281 | |||
| ec3b0e5d04 | |||
| 4c63c2681f | |||
| dd661feaf1 | |||
| 1f17bfff1b | |||
| 809a0b375d | |||
| d55b3415ac | |||
| edcb818842 | |||
| 1e429e1bb3 | |||
| 3472859afb | |||
| 26025ec276 | |||
| 3b3905b513 | |||
| ce3d09cf2e | |||
| 5dd00db8d2 | |||
| 2dc7b50141 | |||
| b2ebcc63aa | |||
| f45bc0a0cc | |||
| bbe53cbca3 | |||
| 7354155c52 | |||
| c1acfee910 | |||
| 5287323865 | |||
| b09d0e42aa | |||
| a3f14061dd | |||
| 557098de17 | |||
| 2fdb96a850 | |||
| d93d064d97 | |||
| ae76fb7dc4 | |||
| 2626afd066 | |||
| c0042cfb6b | |||
| a549d2ef34 | |||
| 4aa20c32b8 | |||
| ce7033b4ca | |||
| 41a54f4b25 | |||
| f96740f1fa | |||
| 8844cd5069 | |||
| 800d846af4 | |||
| 95d50a0391 | |||
| 5a83eab1fe | |||
| bac026a1f4 | |||
| 65482c493a | |||
| a05f837ca2 | |||
| 6fbb5d712e | |||
| 2c9d195474 | |||
| b663d34320 | |||
| 43095d0df1 | |||
| 47e0dc862c | |||
| 460b11b9c8 | |||
| d974a72cb9 | |||
| e86f37fdfb | |||
| 2ed120dc4c | |||
| 973ae785f4 | |||
| 4835813e2e | |||
| 632409050b | |||
| 9d991dbb97 | |||
| cddf7c20e4 | |||
| 5fd2fc59f4 | |||
| 97d7ccacbf | |||
| 242e56b1d8 | |||
| 4c7aa11353 | |||
| cac84ffe03 | |||
| cf66e1e226 | |||
| fa647e1e2c | |||
| 4203121d40 | |||
| 82fe6d5aa4 | |||
| 32db83f091 | |||
| 0ab949174a | |||
| 2bfaa95a81 | |||
| 96de1554f1 | |||
| f83dcd802c | |||
| 808aa50f5f | |||
| e00271e493 | |||
| 76bf245e16 | |||
| 37e441206a | |||
| 5376f7e22e | |||
| 1186fc6624 | |||
| a1b3680643 | |||
| 501399919f | |||
| 725d57d967 | |||
| a821501392 | |||
| d3458148bd | |||
| df4da810ec | |||
| 8f47c84c8e | |||
| f61a544a57 | |||
| 3217d66c69 | |||
| fd48f5f110 | |||
| 77a4c95c4a | |||
| dbc60b1d82 | |||
| ed942bb997 | |||
| 6866084872 | |||
| 4c77419dcd | |||
| ecba6e0843 | |||
| 86dcb476f1 | |||
| 13e0857882 | |||
| bf93a9239c | |||
| 2d18aab709 | |||
| a1359da374 | |||
| f7bda835f9 | |||
| 7595896831 | |||
| 6614d19845 | |||
| 6209b86560 | |||
| a30859a3d4 | |||
| ddbb46289b | |||
| 64c35a9609 | |||
| f21069ab2e | |||
| 457729b832 | |||
| 756f04a0b3 | |||
| 5ee32cc3ba | |||
| 955558bc21 | |||
| c43a872c23 | |||
| de41b92d5d | |||
| 03091230ed | |||
| a8c88915ea | |||
| 4cd4720101 | |||
| 3b9280b19c | |||
| 2d27e07441 | |||
| 20a5762d2e | |||
| 146f71f704 | |||
| 1d4b0bc100 | |||
| 138ee0a448 | |||
| 2c8cae9d45 | |||
| 061d2b6493 | |||
| e71df936ab | |||
| f4253acb78 | |||
| c12518fa49 | |||
| 4704b2ebf7 | |||
| 89fdfdfb34 | |||
| 35d85f2aa3 | |||
| c0368f1c6d | |||
| abc069046c | |||
| 46d0b08068 | |||
| a7e3a0286e | |||
| 6da8924868 | |||
| d9dd6afe4b | |||
| dfec26f789 | |||
| d13be5e159 | |||
| 51c5f70d64 | |||
| 05b1902c06 | |||
| f4ae2cac68 | |||
| ea3995afce | |||
| c41d60c38f | |||
| 19edcbddd5 | |||
| 4f65555e82 | |||
| 9956cab507 | |||
| fddf0c51b7 | |||
| e2a52afb67 | |||
| c8c3a33912 | |||
| b178f69e47 | |||
| 049a56fcb0 | |||
| 2d41dd8f08 | |||
| aa9e33af1d | |||
| 02870805c7 | |||
| e493825ab7 | |||
| 762b248641 | |||
| f81ccee020 | |||
| 3561e5f30f | |||
| c67817b9d2 | |||
| 756619edb6 | |||
| ee1c889d54 | |||
| 9f3dcf07b6 | |||
| 93a2f103f7 | |||
| 3ea12ceeab | |||
| ce8ec51ae4 | |||
| 54bb4ef109 | |||
| 59b655ce5d | |||
| 4c1e1bb7d5 | |||
| eee54f507e | |||
| 6f1b1611ab | |||
| 205499220d | |||
| a0c5631eba | |||
| 9323abc788 | |||
| 632f898dc8 | |||
| 8737b0d984 | |||
| bcb68853d5 | |||
| 8823d6d65e | |||
| 5d3a2b6e84 | |||
| 23fb7753ab | |||
| f8e7e343cb | |||
| e5f4c5dc8d | |||
| 8a10867e5f | |||
| da366a75e8 | |||
| eb4bf74cc6 | |||
| 168c0ced13 | |||
| 5d4bdb431b | |||
| 40188c671f | |||
| f396e0e5eb | |||
| 942a8c9c8b | |||
| 414dcdae3e | |||
| 1195a6676f | |||
| 5501f53056 | |||
| 41d6e1c648 | |||
| 5ac7257517 | |||
| 9f4f9702a6 | |||
| 01d169256e | |||
| 7a703ad091 | |||
| ce9273c7e8 | |||
| 653a414ac1 | |||
| 2080db21ca | |||
| 8a933609ee | |||
| ba4a50e234 | |||
| f8e8ce8bc2 | |||
| 0013d4e682 | |||
| 9a97f1178d | |||
| e6a551d84d | |||
| 92921430d8 | |||
| 20c905aae4 | |||
| ec0730ef73 | |||
| e4576b3405 | |||
| c5800970a8 | |||
| 8f88b1c156 | |||
| 1360994a67 | |||
| e00b28d27e | |||
| c5ff5c101d | |||
| b1a1001c49 | |||
| 77d5162e73 | |||
| 2e8cd54ac2 | |||
| 28222c074e | |||
| 1b70766a17 | |||
| cda2d4688c | |||
| 5028582198 | |||
| 5458564cfa | |||
| 7f11373f75 | |||
| f9ca8d139b | |||
| 6f287a1894 | |||
| 6f4251796e | |||
| 5ad349e350 | |||
| ec4e0694a4 | |||
| 6ab8cd5b5b | |||
| b3868bd750 | |||
| ff8181bbfa | |||
| 074ad44bca | |||
| 9a6c34a2d5 | |||
| ce3c0f105e | |||
| 58162027a9 | |||
| 39400cce0a | |||
| 819d795c23 | |||
| d6b5f60b18 | |||
| 1f16a82a8d | |||
| 51fa58b481 | |||
| bed8c561a4 | |||
| fdb2441b1a | |||
| bf5be91868 | |||
| 06956a7a2d | |||
| a584ac2401 | |||
| 513c6a647f | |||
| 5a85aced45 | |||
| 41933b84a0 | |||
| 67aae87754 | |||
| 13346daa6a | |||
| 5e9b841cac | |||
| 5820fe1db3 | |||
| f5c0174473 | |||
| 31b583910e | |||
| fac4cb43a8 | |||
| 0f39d64d72 | |||
| 5fc012f7ba | |||
| f773ca2322 | |||
| 52c56af02c | |||
| 22d6fdf293 | |||
| b1256427a2 | |||
| 026a6066d3 | |||
| 0cd5898107 | |||
| a5d101ff61 | |||
| c1b04d1772 | |||
| 0d58201e3f | |||
| 939121dea6 | |||
| c44d26cc18 | |||
| c047209b86 | |||
| f989e4dc89 | |||
| c8c3ab312b | |||
| dc52dd635b | |||
| 4e97ba6da9 | |||
| c0d26256af | |||
| 7828de74aa | |||
| 01396e739c | |||
| e4c1a056ae | |||
| 613e9c0c54 | |||
| c5a174ba7a | |||
| 72296cf960 | |||
| 265449c5a7 | |||
| 1125b498ec | |||
| a28f3fa660 | |||
| 5a6b7ac4c1 | |||
| 7f988dcf26 | |||
| 7c177d568b | |||
| 12af19edba | |||
| 924bc6e469 | |||
| 4569a9ed13 | |||
| 4ca2f0c08d | |||
| 4a47815be7 | |||
| dd446b3034 | |||
| 0fcc0f798c | |||
| 1637644bdd | |||
| 2b114f225e | |||
| 32746e8dd9 | |||
| d29c1e572e | |||
| bb6dcb3880 | |||
| 3c56813d6b | |||
| 5c0482e972 | |||
| 83f981111a | |||
| 4171768cc9 | |||
| df2acfaa40 | |||
| 533380107b | |||
| 9c2b844290 | |||
| 98c1fea7fd | |||
| 415df211ed | |||
| ac09e059fc | |||
| 268cc7c48b | |||
| 358bd350b5 | |||
| a328ff2f2a | |||
| 46a08e49f5 | |||
| 05cd0b625c | |||
| 5e940187d9 | |||
| 8b196716a4 | |||
| 6025e0f557 | |||
| e00209c99c | |||
| 03e9f6b882 | |||
| fab8c90e92 | |||
| 3b719d0880 | |||
| 45dbe45fb4 | |||
| 76f2a3fbc4 | |||
| 5e761c3e29 | |||
| 6d51da3118 |
+1
-1
@@ -342,6 +342,6 @@ ij_json_wrap_long_lines = false
|
||||
indent_size = 2
|
||||
ij_yaml_keep_indents_on_empty_lines = 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_brackets = true
|
||||
|
||||
@@ -52,6 +52,7 @@ gradle-app.setting
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
*.hprof
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
|
||||
@@ -1,25 +1,46 @@
|
||||
# Terra
|
||||
Terra is a data-driven world generator based on [Gaea](https://github.com/PolyhedralDev/Gaea). Find out more on our
|
||||
[Spigot page](https://www.spigotmc.org/resources/85151/)!
|
||||
|
||||
## Building and running Terra
|
||||
To build, simply run `./gradlew build` on Linux/MacOS, or `gradlew.bat build` on Windows.
|
||||
This will produce a jar in `build/libs` called `Terra-[CURRENT VERSION].jar`.
|
||||
You can put this right into your plugins dir, along with the correct Gaea version.
|
||||
Terra is an incredibly powerful free & open-source data-driven, platform-agnostic world generator. It allows you to create a world exactly
|
||||
to your specifications, with no knowledge of Java required.
|
||||
|
||||
If you would like to test it with a default server config, just run `./gradlew setupServer` or
|
||||
`./gradlew.bat setupServer` to set up the server, then `./gradlew testWithPaper` or `gradlew.bat testWithPaper` to run
|
||||
the server. If you want a clean installation of the server, re-run the `setupServer` task.
|
||||
This will download a default server config from [here](https://github.com/PolyhedralDev/WorldGenTestServer)
|
||||
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.**
|
||||
## Downloads:
|
||||
|
||||
* 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)
|
||||
* 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
|
||||
|
||||
To build, simply run `./gradlew build` (`gradlew.bat build` on Windows). This will build all platforms, and
|
||||
produce JARs in `platforms/<platform>/build/libs`
|
||||
|
||||
### Production JARs:
|
||||
* Bukkit: `Terra-<version>-shaded.jar`
|
||||
* Fabric: `Terra-<version>-shaded-mapped.jar`
|
||||
* Forge: `Terra-<version>-shaded.jar`
|
||||
|
||||
### 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
|
||||
Contributions are welcome! If you want to see a feature in Terra, please, open an issue, or implement it yourself and
|
||||
submit a PR!
|
||||
Join the discord [here](https://discord.gg/PXUEbbF) if you would like to talk more about the project!
|
||||
|
||||
## 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
@@ -1,10 +1,27 @@
|
||||
import com.dfsek.terra.getGitHash
|
||||
|
||||
val versionObj = Version("4", "3", "0", true)
|
||||
val versionObj = Version("5", "3", "3", true)
|
||||
|
||||
allprojects {
|
||||
version = versionObj
|
||||
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.
|
||||
@@ -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.
|
||||
"$major.$minor.$revision-BETA+${getGitHash()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,30 +7,15 @@ import org.gradle.kotlin.dsl.withType
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun Project.configureCommon() {
|
||||
apply(plugin = "java-library")
|
||||
apply(plugin = "maven-publish")
|
||||
apply(plugin = "idea")
|
||||
|
||||
configureDependencies()
|
||||
configureCompilation()
|
||||
configureDistribution()
|
||||
|
||||
version = rootProject.version
|
||||
|
||||
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
|
||||
maxHeapSize = "2G"
|
||||
ignoreFailures = false
|
||||
failFast = true
|
||||
maxParallelForks = 12
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.getGitHash(): String {
|
||||
val stdout = java.io.ByteArrayOutputStream()
|
||||
val stdout = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD")
|
||||
standardOutput = stdout
|
||||
|
||||
@@ -3,12 +3,16 @@ package com.dfsek.terra
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.language.jvm.tasks.ProcessResources
|
||||
|
||||
fun Project.configureCompilation() {
|
||||
apply(plugin = "maven-publish")
|
||||
apply(plugin = "idea")
|
||||
|
||||
configure<JavaPluginConvention> {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
@@ -17,11 +21,40 @@ fun Project.configureCompilation() {
|
||||
tasks.withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
doFirst {
|
||||
options.compilerArgs = mutableListOf("-Xlint:all")
|
||||
options.compilerArgs.add("-Xlint:all")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<ProcessResources> {
|
||||
include("**/*.*")
|
||||
filter<org.apache.tools.ant.filters.ReplaceTokens>(
|
||||
"tokens" to mapOf(
|
||||
"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"]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
tasks.withType<Javadoc> {
|
||||
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
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.invoke
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
|
||||
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 {
|
||||
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://papermc.io/repo/repository/maven-public/") }
|
||||
maven { url = uri("https://maven.fabricmc.net/") }
|
||||
@@ -18,9 +32,7 @@ fun Project.configureDependencies() {
|
||||
|
||||
dependencies {
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.7.0")
|
||||
"testImplementation"("org.yaml:snakeyaml:1.27")
|
||||
"testImplementation"("com.googlecode.json-simple:json-simple:1.1.1")
|
||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
|
||||
"compileOnly"("org.jetbrains:annotations:20.1.0")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-engine:5.7.0")
|
||||
"api"("org.jetbrains:annotations:20.1.0")
|
||||
}
|
||||
}
|
||||
@@ -14,26 +14,8 @@ fun Project.configureDistribution() {
|
||||
apply(plugin = "java-library")
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
|
||||
|
||||
// configurations.create("shaded")
|
||||
|
||||
configurations {
|
||||
val shaded = create("shaded")
|
||||
getByName("compile").extendsFrom(shaded)
|
||||
// shaded.extendsFrom(getByName("compile"))
|
||||
val shadedApi = create("shadedApi")
|
||||
shaded.extendsFrom(shadedApi)
|
||||
getByName("api").extendsFrom(shadedApi)
|
||||
val shadedImplementation = create("shadedImplementation")
|
||||
shaded.extendsFrom(shadedImplementation)
|
||||
getByName("implementation").extendsFrom(shadedImplementation)
|
||||
}
|
||||
|
||||
// tasks.withType<JavaCompile> {
|
||||
// classpath +=
|
||||
// }
|
||||
|
||||
val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
|
||||
group = "terra"
|
||||
doFirst {
|
||||
file("${buildDir}/resources/main/packs/").deleteRecursively()
|
||||
|
||||
@@ -45,21 +27,6 @@ fun Project.configureDistribution() {
|
||||
}
|
||||
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") {
|
||||
// Tell shadow to download the packs
|
||||
dependsOn(downloadDefaultPacks)
|
||||
@@ -69,8 +36,14 @@ fun Project.configureDistribution() {
|
||||
archiveClassifier.set("shaded")
|
||||
setVersion(project.version)
|
||||
relocate("org.apache.commons", "com.dfsek.terra.lib.commons")
|
||||
relocate("parsii", "com.dfsek.terra.lib.parsii")
|
||||
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
|
||||
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()
|
||||
}
|
||||
convention.getPlugin<BasePluginConvention>().archivesBaseName = project.name
|
||||
|
||||
+12
-5
@@ -1,11 +1,14 @@
|
||||
import com.dfsek.terra.configureCommon
|
||||
import com.dfsek.terra.configureCompilation
|
||||
import com.dfsek.terra.configureDependencies
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
idea
|
||||
}
|
||||
|
||||
configureCommon()
|
||||
configureCompilation()
|
||||
configureDependencies()
|
||||
|
||||
group = "com.dfsek.terra.common"
|
||||
|
||||
@@ -14,14 +17,18 @@ dependencies {
|
||||
"shadedApi"("commons-io:commons-io:2.4")
|
||||
|
||||
"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"("org.yaml:snakeyaml:1.27")
|
||||
"shadedApi"("org.ow2.asm:asm:9.0")
|
||||
"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")
|
||||
|
||||
"shadedApi"("com.google.guava:guava:30.0-jre")
|
||||
"compileOnly"("com.google.guava:guava:30.0-jre")
|
||||
|
||||
"testImplementation"("com.google.guava:guava:30.0-jre")
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class AddonClassLoader extends URLClassLoader {
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public AddonClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
public AddonClassLoader(URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Set<Class<? extends TerraAddon>> fetchAddonClasses(File file) throws IOException {
|
||||
JarFile jarFile = new JarFile(file);
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
|
||||
AddonClassLoader loader = new AddonClassLoader(new URL[] {file.toURI().toURL()}, AddonClassLoader.class.getClassLoader());
|
||||
|
||||
Set<Class<? extends TerraAddon>> set = new HashSet<>();
|
||||
while(entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
|
||||
if(entry.isDirectory() || !entry.getName().endsWith(".class")) continue;
|
||||
String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.');
|
||||
|
||||
try {
|
||||
Class<?> clazz = loader.loadClass(className);
|
||||
|
||||
Addon addon = clazz.getAnnotation(Addon.class);
|
||||
|
||||
if(addon == null) continue;
|
||||
|
||||
if(!TerraAddon.class.isAssignableFrom(clazz))
|
||||
throw new IllegalArgumentException("Addon class \"" + clazz + "\" must extend TerraAddon.");
|
||||
|
||||
set.add((Class<? extends TerraAddon>) clazz);
|
||||
} catch(ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e); // this should literally never happen, if it does something is very wrong
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.addon.exception.AddonLoadException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AddonPool {
|
||||
private final Map<String, PreLoadAddon> pool = new HashMap<>();
|
||||
|
||||
public void add(PreLoadAddon addon) throws AddonLoadException {
|
||||
if(pool.containsKey(addon.getId())) {
|
||||
String message = "Duplicate addon ID: " +
|
||||
addon.getId() + "; original ID from file: " +
|
||||
pool.get(addon.getId()).getFile().getAbsolutePath() +
|
||||
", class: " +
|
||||
pool.get(addon.getId()).getAddonClass().getCanonicalName() +
|
||||
"Duplicate ID from file: " +
|
||||
addon.getFile().getAbsolutePath() +
|
||||
", class: " +
|
||||
addon.getAddonClass().getCanonicalName();
|
||||
throw new AddonLoadException(message);
|
||||
}
|
||||
pool.put(addon.getId(), addon);
|
||||
}
|
||||
|
||||
public PreLoadAddon get(String id) {
|
||||
return pool.get(id);
|
||||
}
|
||||
|
||||
public void buildAll() throws AddonLoadException {
|
||||
for(PreLoadAddon value : pool.values()) {
|
||||
value.rebuildDependencies(this, value, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<PreLoadAddon> getAddons() {
|
||||
return new HashSet<>(pool.values());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.dfsek.terra.addon;
|
||||
|
||||
import com.dfsek.terra.addon.exception.AddonLoadException;
|
||||
import com.dfsek.terra.addon.exception.CircularDependencyException;
|
||||
import com.dfsek.terra.addon.exception.DependencyMissingException;
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
import com.dfsek.terra.api.addons.annotations.Depends;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PreLoadAddon {
|
||||
private final List<PreLoadAddon> depends = new ArrayList<>();
|
||||
private final Class<? extends TerraAddon> addonClass;
|
||||
private final String id;
|
||||
private final String[] dependencies;
|
||||
private final File file;
|
||||
|
||||
public PreLoadAddon(Class<? extends TerraAddon> addonClass, File file) {
|
||||
this.addonClass = addonClass;
|
||||
this.id = addonClass.getAnnotation(Addon.class).value();
|
||||
this.file = file;
|
||||
Depends depends = addonClass.getAnnotation(Depends.class);
|
||||
this.dependencies = depends == null ? new String[] {} : depends.value();
|
||||
}
|
||||
|
||||
public List<PreLoadAddon> getDepends() {
|
||||
return depends;
|
||||
}
|
||||
|
||||
public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException {
|
||||
if(this.equals(origin) && !levelG1)
|
||||
throw new CircularDependencyException("Detected circular dependency in addon \"" + id + "\", dependencies: " + Arrays.toString(dependencies));
|
||||
|
||||
for(String dependency : dependencies) {
|
||||
PreLoadAddon preLoadAddon = pool.get(dependency);
|
||||
if(preLoadAddon == null)
|
||||
throw new DependencyMissingException("Dependency " + dependency + " was not found. Please install " + dependency + " to use " + id + ".");
|
||||
depends.add(preLoadAddon);
|
||||
preLoadAddon.rebuildDependencies(pool, origin, false);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Class<? extends TerraAddon> getAddonClass() {
|
||||
return addonClass;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class AddonLoadException extends Exception {
|
||||
private static final long serialVersionUID = -4949084729296580176L;
|
||||
|
||||
public AddonLoadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AddonLoadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class CircularDependencyException extends AddonLoadException {
|
||||
private static final long serialVersionUID = 7398510879124125121L;
|
||||
|
||||
public CircularDependencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CircularDependencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addon.exception;
|
||||
|
||||
public class DependencyMissingException extends AddonLoadException {
|
||||
private static final long serialVersionUID = -8419489102208521583L;
|
||||
|
||||
public DependencyMissingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DependencyMissingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.dfsek.terra.api;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.EventManager;
|
||||
import com.dfsek.terra.api.platform.handle.ItemHandle;
|
||||
import com.dfsek.terra.api.platform.handle.WorldHandle;
|
||||
import com.dfsek.terra.api.platform.modloader.Mod;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.api.registry.CheckedRegistry;
|
||||
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.Logger;
|
||||
import com.dfsek.terra.config.PluginConfig;
|
||||
import com.dfsek.terra.config.lang.Language;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
import com.dfsek.terra.profiler.Profiler;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* Represents a Terra mod/plugin instance.
|
||||
*/
|
||||
public interface TerraPlugin extends LoaderRegistrar {
|
||||
WorldHandle getWorldHandle();
|
||||
|
||||
TerraWorld getWorld(World world);
|
||||
|
||||
Logger logger();
|
||||
|
||||
PluginConfig getTerraConfig();
|
||||
|
||||
File getDataFolder();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
Language getLanguage();
|
||||
|
||||
CheckedRegistry<ConfigPack> getConfigRegistry();
|
||||
|
||||
LockedRegistry<TerraAddon> getAddons();
|
||||
|
||||
boolean reload();
|
||||
|
||||
ItemHandle getItemHandle();
|
||||
|
||||
void saveDefaultConfig();
|
||||
|
||||
String platformName();
|
||||
|
||||
DebugLogger getDebugLogger();
|
||||
|
||||
EventManager getEventManager();
|
||||
|
||||
default String getVersion() {
|
||||
return "@VERSION@";
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task that may or may not be thread safe, depending on platform.
|
||||
* <p>
|
||||
* Allows platforms to define what code is safe to be run asynchronously.
|
||||
*
|
||||
* @param task Task to be run.
|
||||
*/
|
||||
default void runPossiblyUnsafeTask(Runnable task) {
|
||||
task.run();
|
||||
}
|
||||
|
||||
Profiler getProfiler();
|
||||
|
||||
default JarFile getModJar() throws URISyntaxException, IOException {
|
||||
return JarUtil.getJarFile();
|
||||
}
|
||||
|
||||
default Set<Mod> getMods() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.dfsek.terra.api.addons;
|
||||
|
||||
|
||||
import com.dfsek.terra.api.addons.annotations.Addon;
|
||||
import com.dfsek.terra.api.addons.annotations.Author;
|
||||
import com.dfsek.terra.api.addons.annotations.Version;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents an entry point for an addon. Implementations must be annotated with {@link Addon}.
|
||||
*/
|
||||
public abstract class TerraAddon {
|
||||
/**
|
||||
* Gets the version of this addon.
|
||||
*
|
||||
* @return Addon version.
|
||||
*/
|
||||
public final @NotNull String getVersion() {
|
||||
Version version = getClass().getAnnotation(Version.class);
|
||||
return version == null ? "0.1.0" : version.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author of this addon.
|
||||
*
|
||||
* @return Addon author.
|
||||
*/
|
||||
public final @NotNull String getAuthor() {
|
||||
Author author = getClass().getAnnotation(Author.class);
|
||||
return author == null ? "Anon Y. Mous" : author.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name (ID) of this addon.
|
||||
*
|
||||
* @return Addon ID.
|
||||
*/
|
||||
public final @NotNull String getName() {
|
||||
Addon addon = getClass().getAnnotation(Addon.class);
|
||||
if(addon == null)
|
||||
throw new IllegalStateException("Addon annotation not present"); // This should never happen; the presence of this annotation is checked by the addon loader.
|
||||
return addon.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked immediately after an addon is loaded.
|
||||
*/
|
||||
public abstract void initialize();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that the annotated class is an entry point for a Terra addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Addon {
|
||||
/**
|
||||
* @return The ID of the addon.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies the author of an addon.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Author {
|
||||
/**
|
||||
* @return Name of the addon author.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies dependencies of an addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Depends {
|
||||
/**
|
||||
* @return All addons this addon is dependent upon.
|
||||
*/
|
||||
@NotNull String[] value();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.addons.annotations;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Optional annotation that specifies the version of an addon.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Version {
|
||||
/**
|
||||
* @return Version of the addon.
|
||||
*/
|
||||
@NotNull String value();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommandManager {
|
||||
void execute(String command, CommandSender sender, List<String> args) throws CommandException;
|
||||
|
||||
void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException;
|
||||
|
||||
List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException;
|
||||
|
||||
int getMaxArgumentDepth();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public interface CommandTemplate {
|
||||
void execute(CommandSender sender);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ExecutionState {
|
||||
private final Set<String> switches = new HashSet<>();
|
||||
private final Map<String, String> args = new HashMap<>();
|
||||
private final CommandSender sender;
|
||||
|
||||
protected ExecutionState(CommandSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
protected void addSwitch(String flag) {
|
||||
switches.add(flag);
|
||||
}
|
||||
|
||||
protected void addArgument(String arg, String value) {
|
||||
args.put(arg, value);
|
||||
}
|
||||
|
||||
public String getArgument(String argument) {
|
||||
return args.get(argument);
|
||||
}
|
||||
|
||||
public boolean hasSwitch(String flag) {
|
||||
return switches.contains(flag);
|
||||
}
|
||||
|
||||
public CommandSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.dfsek.terra.api.command;
|
||||
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.command.annotation.Argument;
|
||||
import com.dfsek.terra.api.command.annotation.Command;
|
||||
import com.dfsek.terra.api.command.annotation.Subcommand;
|
||||
import com.dfsek.terra.api.command.annotation.Switch;
|
||||
import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget;
|
||||
import com.dfsek.terra.api.command.annotation.inject.SwitchTarget;
|
||||
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.command.arg.ArgumentParser;
|
||||
import com.dfsek.terra.api.command.exception.CommandException;
|
||||
import com.dfsek.terra.api.command.exception.ExecutionException;
|
||||
import com.dfsek.terra.api.command.exception.InvalidArgumentsException;
|
||||
import com.dfsek.terra.api.command.exception.MalformedCommandException;
|
||||
import com.dfsek.terra.api.command.exception.SwitchFormatException;
|
||||
import com.dfsek.terra.api.command.tab.TabCompleter;
|
||||
import com.dfsek.terra.api.injection.Injector;
|
||||
import com.dfsek.terra.api.injection.exception.InjectionException;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
import com.dfsek.terra.api.platform.entity.Player;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TerraCommandManager implements CommandManager {
|
||||
private final Map<String, CommandHolder> commands = new HashMap<>();
|
||||
private final Injector<TerraPlugin> pluginInjector;
|
||||
private final TerraPlugin main;
|
||||
|
||||
public TerraCommandManager(TerraPlugin main) {
|
||||
this.main = main;
|
||||
this.pluginInjector = new Injector<>(main);
|
||||
pluginInjector.addExplicitTarget(TerraPlugin.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String commandName, CommandSender sender, List<String> argsIn) throws CommandException {
|
||||
if(!commands.containsKey(commandName)) throw new InvalidArgumentsException("No such command \"" + commandName + "\"");
|
||||
execute(commands.get(commandName), sender, new ArrayList<>(argsIn));
|
||||
}
|
||||
|
||||
private void execute(CommandHolder commandHolder, CommandSender sender, List<String> args) throws CommandException {
|
||||
Class<? extends CommandTemplate> commandClass = commandHolder.clazz;
|
||||
|
||||
if(commandClass.isAnnotationPresent(DebugCommand.class) && !main.isDebug()) {
|
||||
sender.sendMessage("Command must be executed with debug mode enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(commandClass.isAnnotationPresent(PlayerCommand.class) && !(sender instanceof Player)) {
|
||||
sender.sendMessage("Command must be executed by player.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(commandClass.isAnnotationPresent(WorldCommand.class) && (!(sender instanceof Player) || !(((Player) sender).getWorld()).isTerraWorld())) {
|
||||
sender.sendMessage("Command must be executed in a Terra world.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> ogArgs = new ArrayList<>(args);
|
||||
|
||||
ExecutionState state = new ExecutionState(sender);
|
||||
|
||||
if(!commandClass.isAnnotationPresent(Command.class)) {
|
||||
invoke(commandClass, state, commandHolder);
|
||||
return;
|
||||
}
|
||||
|
||||
Command command = commandClass.getAnnotation(Command.class);
|
||||
|
||||
if(command.arguments().length == 0 && command.subcommands().length == 0) {
|
||||
if(args.isEmpty()) {
|
||||
invoke(commandClass, state, commandHolder);
|
||||
return;
|
||||
} else throw new InvalidArgumentsException("Expected 0 arguments, found " + args.size());
|
||||
}
|
||||
|
||||
if(!args.isEmpty() && commandHolder.subcommands.containsKey(args.get(0))) {
|
||||
String c = args.get(0);
|
||||
args.remove(0);
|
||||
execute(commandHolder.subcommands.get(c), sender, args);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean req = true;
|
||||
for(Argument argument : command.arguments()) {
|
||||
if(!req && argument.required()) {
|
||||
throw new MalformedCommandException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments()));
|
||||
}
|
||||
req = argument.required();
|
||||
|
||||
if(args.isEmpty()) {
|
||||
if(req) throw new InvalidArgumentsException("Invalid arguments: " + ogArgs + ", usage: " + command.usage());
|
||||
break;
|
||||
}
|
||||
|
||||
String arg = args.get(0);
|
||||
|
||||
if(arg.startsWith("-")) { // switches have started.
|
||||
if(req) throw new InvalidArgumentsException("Switches must come after arguments.");
|
||||
break;
|
||||
}
|
||||
|
||||
state.addArgument(argument.value(), args.remove(0));
|
||||
}
|
||||
|
||||
while(!args.isEmpty()) {
|
||||
String aSwitch = args.remove(0);
|
||||
if(!aSwitch.startsWith("-")) throw new SwitchFormatException("Invalid switch \"" + aSwitch + "\"");
|
||||
|
||||
String val = aSwitch.substring(1); // remove dash
|
||||
|
||||
if(!commandHolder.switches.containsKey(val)) throw new SwitchFormatException("No such switch \"" + aSwitch + "\"");
|
||||
|
||||
state.addSwitch(commandHolder.switches.get(val));
|
||||
}
|
||||
|
||||
invoke(commandClass, state, commandHolder);
|
||||
}
|
||||
|
||||
private void invoke(Class<? extends CommandTemplate> clazz, ExecutionState state, CommandHolder holder) throws CommandException {
|
||||
try {
|
||||
CommandTemplate template = clazz.getConstructor().newInstance();
|
||||
|
||||
pluginInjector.inject(template);
|
||||
|
||||
for(Field field : ReflectionUtil.getFields(clazz)) {
|
||||
if(field.isAnnotationPresent(ArgumentTarget.class)) {
|
||||
ArgumentTarget argumentTarget = field.getAnnotation(ArgumentTarget.class);
|
||||
if(!holder.argumentMap.containsKey(argumentTarget.value())) {
|
||||
throw new MalformedCommandException("Argument Target specifies nonexistent argument \"" + argumentTarget.value() + "\"");
|
||||
}
|
||||
|
||||
String argument = argumentTarget.value();
|
||||
|
||||
ArgumentParser<?> argumentParser = holder.argumentMap.get(argumentTarget.value()).argumentParser().getConstructor().newInstance();
|
||||
|
||||
pluginInjector.inject(argumentParser);
|
||||
|
||||
field.setAccessible(true);
|
||||
String value = state.getArgument(argument);
|
||||
|
||||
if(value == null) value = holder.argumentMap.get(argumentTarget.value()).defaultValue();
|
||||
|
||||
field.set(template, argumentParser.parse(state.getSender(), value));
|
||||
}
|
||||
if(field.isAnnotationPresent(SwitchTarget.class)) {
|
||||
SwitchTarget switchTarget = field.getAnnotation(SwitchTarget.class);
|
||||
if(!holder.switches.containsValue(switchTarget.value())) {
|
||||
throw new MalformedCommandException("Switch Target specifies nonexistent switch \"" + switchTarget.value() + "\"");
|
||||
}
|
||||
|
||||
if(!(field.getType() == boolean.class)) {
|
||||
throw new MalformedCommandException("Switch Target must be of type boolean.");
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
field.setBoolean(template, state.hasSwitch(switchTarget.value()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
template.execute(state.getSender());
|
||||
} catch(Throwable e) {
|
||||
throw new ExecutionException("Failed to execute command: " + e.getMessage(), e);
|
||||
}
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
|
||||
throw new MalformedCommandException("Unable to reflectively instantiate command: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
|
||||
commands.put(name, new CommandHolder(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(String command, CommandSender sender, List<String> args) throws CommandException {
|
||||
if(args.isEmpty()) return new ArrayList<>(commands.keySet()).stream().sorted(String::compareTo).collect(Collectors.toList());
|
||||
if(!commands.containsKey(command)) return Collections.emptyList();
|
||||
return tabComplete(commands.get(command), sender, new ArrayList<>(args)).stream().filter(s -> s.toLowerCase().startsWith(args.get(args.size() - 1).toLowerCase())).sorted(String::compareTo).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxArgumentDepth() {
|
||||
int max = 0;
|
||||
for(CommandHolder value : commands.values()) {
|
||||
max = FastMath.max(getMaxArgumentDepth(value), max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private int getMaxArgumentDepth(CommandHolder holder) {
|
||||
int max = 0;
|
||||
max = FastMath.max(holder.arguments.size() + holder.switchList.size(), max);
|
||||
for(CommandHolder value : holder.subcommands.values()) {
|
||||
max = FastMath.max(max, getMaxArgumentDepth(value) + 1);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private List<String> tabComplete(CommandHolder holder, CommandSender sender, List<String> args) throws CommandException {
|
||||
if(args.isEmpty()) return Collections.emptyList();
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if(args.size() == 1) {
|
||||
completions.addAll(holder.subcommands.keySet());
|
||||
}
|
||||
|
||||
if(holder.subcommands.containsKey(args.get(0))) {
|
||||
List<String> newArgs = new ArrayList<>(args);
|
||||
newArgs.remove(0);
|
||||
completions.addAll(tabComplete(holder.subcommands.get(args.get(0)), sender, newArgs));
|
||||
}
|
||||
try {
|
||||
if(args.size() <= holder.arguments.size()) {
|
||||
TabCompleter completer = holder.arguments.get(args.size() - 1).tabCompleter().getConstructor().newInstance();
|
||||
pluginInjector.inject(completer);
|
||||
completions.addAll(completer.complete(sender));
|
||||
}
|
||||
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InjectionException e) {
|
||||
throw new MalformedCommandException("Unable to reflectively instantiate tab-completer: ", e);
|
||||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-processes command metadata.
|
||||
*/
|
||||
private static final class CommandHolder {
|
||||
private final Class<? extends CommandTemplate> clazz;
|
||||
private final Map<String, CommandHolder> subcommands = new HashMap<>();
|
||||
private final Map<String, String> switches = new HashMap<>();
|
||||
private final List<Argument> arguments;
|
||||
private final List<Switch> switchList;
|
||||
private final Map<String, Argument> argumentMap = new HashMap<>();
|
||||
|
||||
private CommandHolder(Class<? extends CommandTemplate> clazz) throws MalformedCommandException {
|
||||
this.clazz = clazz;
|
||||
if(clazz.isAnnotationPresent(Command.class)) {
|
||||
Command command = clazz.getAnnotation(Command.class);
|
||||
for(Subcommand subcommand : command.subcommands()) {
|
||||
if(subcommands.containsKey(subcommand.value()))
|
||||
throw new MalformedCommandException("Duplicate subcommand: " + subcommand);
|
||||
CommandHolder holder = new CommandHolder(subcommand.clazz());
|
||||
subcommands.put(subcommand.value(), holder);
|
||||
for(String alias : subcommand.aliases()) {
|
||||
subcommands.put(alias, holder);
|
||||
}
|
||||
}
|
||||
for(Switch aSwitch : command.switches()) {
|
||||
if(switches.containsKey(aSwitch.value())) throw new MalformedCommandException("Duplicate switch: " + aSwitch);
|
||||
switches.put(aSwitch.value(), aSwitch.value());
|
||||
for(String alias : aSwitch.aliases()) {
|
||||
switches.put(alias, aSwitch.value());
|
||||
}
|
||||
}
|
||||
for(Argument argument : command.arguments()) {
|
||||
if(argumentMap.containsKey(argument.value())) throw new MalformedCommandException("Duplicate argument: " + argument);
|
||||
argumentMap.put(argument.value(), argument);
|
||||
}
|
||||
arguments = Arrays.asList(command.arguments());
|
||||
switchList = Arrays.asList(command.switches());
|
||||
} else {
|
||||
arguments = Collections.emptyList();
|
||||
switchList = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import com.dfsek.terra.api.command.arg.ArgumentParser;
|
||||
import com.dfsek.terra.api.command.arg.StringArgumentParser;
|
||||
import com.dfsek.terra.api.command.tab.NothingCompleter;
|
||||
import com.dfsek.terra.api.command.tab.TabCompleter;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Argument {
|
||||
String value();
|
||||
|
||||
boolean required() default true;
|
||||
|
||||
Class<? extends TabCompleter> tabCompleter() default NothingCompleter.class;
|
||||
|
||||
Class<? extends ArgumentParser<?>> argumentParser() default StringArgumentParser.class;
|
||||
|
||||
String defaultValue() default "";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Command {
|
||||
Argument[] arguments() default {};
|
||||
|
||||
Switch[] switches() default {};
|
||||
|
||||
Subcommand[] subcommands() default {};
|
||||
|
||||
String usage() default "";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import com.dfsek.terra.api.command.CommandTemplate;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface Subcommand {
|
||||
String value();
|
||||
|
||||
String[] aliases() default {};
|
||||
|
||||
Class<? extends CommandTemplate> clazz();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface Switch {
|
||||
String value();
|
||||
|
||||
String[] aliases() default {};
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.command.annotation.inject;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ArgumentTarget {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.command.annotation.inject;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SwitchTarget {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Command may only be executed with debug mode enabled.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DebugCommand {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks command as player-only
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerCommand {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.command.annotation.type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Command may only be executed in a Terra world.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WorldCommand {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public interface ArgumentParser<T> {
|
||||
T parse(CommandSender sender, String arg);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class DoubleArgumentParser implements ArgumentParser<Double> {
|
||||
@Override
|
||||
public Double parse(CommandSender sender, String arg) {
|
||||
return arg == null ? null : Double.parseDouble(arg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class IntegerArgumentParser implements ArgumentParser<Integer> {
|
||||
@Override
|
||||
public Integer parse(CommandSender sender, String arg) {
|
||||
return arg == null ? null : Integer.parseInt(arg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.dfsek.terra.api.command.arg;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
public class StringArgumentParser implements ArgumentParser<String> {
|
||||
@Override
|
||||
public String parse(CommandSender sender, String arg) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public abstract class CommandException extends Exception {
|
||||
private static final long serialVersionUID = -2955328495045879822L;
|
||||
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class ExecutionException extends CommandException {
|
||||
private static final long serialVersionUID = -6345523475880607959L;
|
||||
|
||||
public ExecutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExecutionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class InvalidArgumentsException extends CommandException {
|
||||
private static final long serialVersionUID = 7563619667472569824L;
|
||||
|
||||
public InvalidArgumentsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidArgumentsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
/**
|
||||
* Thrown when command is incorrectly defined.
|
||||
*/
|
||||
public class MalformedCommandException extends CommandException {
|
||||
private static final long serialVersionUID = -5417760860407895496L;
|
||||
|
||||
public MalformedCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MalformedCommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.exception;
|
||||
|
||||
public class SwitchFormatException extends CommandException {
|
||||
private static final long serialVersionUID = -965858989317844628L;
|
||||
|
||||
public SwitchFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SwitchFormatException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.command.tab;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class NothingCompleter implements TabCompleter {
|
||||
@Override
|
||||
public List<String> complete(CommandSender sender) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.dfsek.terra.api.command.tab;
|
||||
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TabCompleter {
|
||||
List<String> complete(CommandSender sender);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.dfsek.terra.api.core;
|
||||
|
||||
import com.dfsek.terra.api.LoaderRegistrar;
|
||||
import com.dfsek.terra.api.core.event.EventManager;
|
||||
import com.dfsek.terra.api.platform.handle.ItemHandle;
|
||||
import com.dfsek.terra.api.platform.handle.WorldHandle;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.config.PluginConfig;
|
||||
import com.dfsek.terra.config.lang.Language;
|
||||
import com.dfsek.terra.debug.DebugLogger;
|
||||
import com.dfsek.terra.registry.ConfigRegistry;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public interface TerraPlugin extends LoaderRegistrar {
|
||||
WorldHandle getWorldHandle();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
TerraWorld getWorld(World world);
|
||||
|
||||
Logger getLogger();
|
||||
|
||||
PluginConfig getTerraConfig();
|
||||
|
||||
File getDataFolder();
|
||||
|
||||
boolean isDebug();
|
||||
|
||||
Language getLanguage();
|
||||
|
||||
ConfigRegistry getRegistry();
|
||||
|
||||
void reload();
|
||||
|
||||
ItemHandle getItemHandle();
|
||||
|
||||
void saveDefaultConfig();
|
||||
|
||||
String platformName();
|
||||
|
||||
|
||||
DebugLogger getDebugLogger();
|
||||
|
||||
EventManager getEventManager();
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
public interface EventListener {
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
|
||||
public interface EventManager {
|
||||
/**
|
||||
* Call an event, and return the execution status.
|
||||
* @param event Event to pass to all registered EventListeners.
|
||||
* @return False if the event is cancellable and has been cancelled, otherwise true.
|
||||
*/
|
||||
boolean callEvent(Event event);
|
||||
|
||||
void registerListener(EventListener listener);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events;
|
||||
|
||||
/**
|
||||
* Events that implement this interface may be cancelled.
|
||||
*
|
||||
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
|
||||
*/
|
||||
public interface Cancellable extends Event {
|
||||
boolean isCancelled();
|
||||
void setCancelled();
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events;
|
||||
|
||||
public interface Event {
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
public abstract class ConfigPackLoadEvent implements Event {
|
||||
private final ConfigPack pack;
|
||||
|
||||
public ConfigPackLoadEvent(ConfigPack pack) {
|
||||
this.pack = pack;
|
||||
}
|
||||
|
||||
public ConfigPack getPack() {
|
||||
return pack;
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* Called when a config pack has finished loading.
|
||||
*/
|
||||
public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent {
|
||||
public ConfigPackPostLoadEvent(ConfigPack pack) {
|
||||
super(pack);
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events.config;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent {
|
||||
public ConfigPackPreLoadEvent(ConfigPack pack) {
|
||||
super(pack);
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.dfsek.terra.api.core.event.events.world;
|
||||
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
/**
|
||||
* Called upon initialization of a TerraWorld.
|
||||
*/
|
||||
public class TerraWorldLoadEvent implements Event {
|
||||
private final TerraWorld world;
|
||||
|
||||
public TerraWorldLoadEvent(TerraWorld world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public TerraWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
|
||||
/**
|
||||
* Marker interface for a class that contains event listener methods.
|
||||
*
|
||||
* @see Event
|
||||
* @see EventManager
|
||||
*/
|
||||
public interface EventListener {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
|
||||
/**
|
||||
* Manages event registration and triggering.
|
||||
*/
|
||||
public interface EventManager {
|
||||
/**
|
||||
* Call an event, and return the execution status.
|
||||
*
|
||||
* @param event Event to pass to all registered EventListeners.
|
||||
* @return False if the event is cancellable and has been cancelled, otherwise true.
|
||||
*/
|
||||
boolean callEvent(Event event);
|
||||
|
||||
/**
|
||||
* Register an {@link EventListener} under an {@link TerraAddon}.
|
||||
*
|
||||
* @param addon Addon to register listener for.
|
||||
* @param listener Listener to register.
|
||||
*/
|
||||
void registerListener(TerraAddon addon, EventListener listener);
|
||||
}
|
||||
+33
-15
@@ -1,9 +1,12 @@
|
||||
package com.dfsek.terra.api.core.event;
|
||||
package com.dfsek.terra.api.event;
|
||||
|
||||
import com.dfsek.terra.api.core.TerraPlugin;
|
||||
import com.dfsek.terra.api.core.event.annotations.Priority;
|
||||
import com.dfsek.terra.api.core.event.events.Cancellable;
|
||||
import com.dfsek.terra.api.core.event.events.Event;
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.addons.TerraAddon;
|
||||
import com.dfsek.terra.api.event.annotations.Global;
|
||||
import com.dfsek.terra.api.event.annotations.Priority;
|
||||
import com.dfsek.terra.api.event.events.Cancellable;
|
||||
import com.dfsek.terra.api.event.events.Event;
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -29,19 +32,30 @@ public class TerraEventManager implements EventManager {
|
||||
public boolean callEvent(Event event) {
|
||||
listeners.getOrDefault(event.getClass(), Collections.emptyList()).forEach(listenerHolder -> {
|
||||
try {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
if(event instanceof PackEvent && !listenerHolder.global) {
|
||||
PackEvent packEvent = (PackEvent) event;
|
||||
if(packEvent
|
||||
.getPack()
|
||||
.getTemplate()
|
||||
.getAddons()
|
||||
.contains(listenerHolder.addon)) {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} else {
|
||||
listenerHolder.method.invoke(listenerHolder.listener, event);
|
||||
}
|
||||
} catch(InvocationTargetException e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
e.getTargetException().printStackTrace(new PrintWriter(writer));
|
||||
main.getLogger().warning("Exception occurred during event handling:");
|
||||
main.getLogger().warning(writer.toString());
|
||||
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
main.logger().warning("Exception occurred during event handling:");
|
||||
main.logger().warning(writer.toString());
|
||||
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
} catch(Exception e) {
|
||||
StringWriter writer = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(writer));
|
||||
main.getLogger().warning("Exception occurred during event handling:");
|
||||
main.getLogger().warning(writer.toString());
|
||||
main.getLogger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
main.logger().warning("Exception occurred during event handling:");
|
||||
main.logger().warning(writer.toString());
|
||||
main.logger().warning("Report this to the maintainers of " + listenerHolder.method.getName());
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -51,7 +65,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void registerListener(EventListener listener) {
|
||||
public void registerListener(TerraAddon addon, EventListener listener) {
|
||||
Class<? extends EventListener> listenerClass = listener.getClass();
|
||||
Method[] methods = ReflectionUtil.getMethods(listenerClass);
|
||||
|
||||
@@ -68,7 +82,7 @@ public class TerraEventManager implements EventManager {
|
||||
|
||||
List<ListenerHolder> holders = listeners.computeIfAbsent((Class<? extends Event>) eventParam, e -> new ArrayList<>());
|
||||
|
||||
holders.add(new ListenerHolder(method, listener, priority));
|
||||
holders.add(new ListenerHolder(method, listener, priority, addon, method.getAnnotation(Global.class) != null));
|
||||
|
||||
holders.sort(Comparator.comparingInt(ListenerHolder::getPriority)); // Sort priorities.
|
||||
}
|
||||
@@ -78,11 +92,15 @@ public class TerraEventManager implements EventManager {
|
||||
private final Method method;
|
||||
private final EventListener listener;
|
||||
private final int priority;
|
||||
private final TerraAddon addon;
|
||||
private final boolean global;
|
||||
|
||||
private ListenerHolder(Method method, EventListener listener, int priority) {
|
||||
private ListenerHolder(Method method, EventListener listener, int priority, TerraAddon addon, boolean global) {
|
||||
this.method = method;
|
||||
this.listener = listener;
|
||||
this.priority = priority;
|
||||
this.addon = addon;
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.dfsek.terra.api.event.annotations;
|
||||
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that an event handler is to handle all {@link PackEvent}s, regardless of whether the pack
|
||||
* depends on the addon's listener.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Global {
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package com.dfsek.terra.api.core.event.annotations;
|
||||
package com.dfsek.terra.api.event.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -12,11 +12,11 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Priority {
|
||||
/**
|
||||
* Highest possible priority. Listeners with this priority will always be invoked first.
|
||||
* Highest possible priority. Listeners with this priority will always be invoked last.
|
||||
*/
|
||||
int HIGHEST = Integer.MAX_VALUE;
|
||||
/**
|
||||
* Lowest possible priority. Listeners with this priority will always be invoked last.
|
||||
* Lowest possible priority. Listeners with this priority will always be invoked first.
|
||||
*/
|
||||
int LOWEST = Integer.MIN_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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
/**
|
||||
* Events that implement this interface may be cancelled.
|
||||
* <p>
|
||||
* Cancelling an event is assumed to stop the execution of whatever action triggered the event.
|
||||
*/
|
||||
public interface Cancellable extends Event {
|
||||
/**
|
||||
* Get the cancellation status of the event.
|
||||
*
|
||||
* @return Whether event is cancelled.
|
||||
*/
|
||||
boolean isCancelled();
|
||||
|
||||
/**
|
||||
* Set the cancellation status of the event.
|
||||
*
|
||||
* @param cancelled Whether event is cancelled.
|
||||
*/
|
||||
void setCancelled(boolean cancelled);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
/**
|
||||
* An event that addons may listen to.
|
||||
*/
|
||||
public interface Event {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.dfsek.terra.api.event.events;
|
||||
|
||||
import com.dfsek.terra.api.event.annotations.Global;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* An event with functionality directly linked to a {@link ConfigPack}.
|
||||
* <p>
|
||||
* PackEvents are only invoked when the pack specifies the addon in its
|
||||
* {@code addon} key (or when the listener is annotated {@link Global}).
|
||||
*/
|
||||
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
|
||||
public interface PackEvent extends Event {
|
||||
/**
|
||||
* Get the {@link ConfigPack} associated with this event.
|
||||
*
|
||||
* @return ConfigPack associated with the event.
|
||||
*/
|
||||
ConfigPack getPack();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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.config.fileloaders.Loader;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* An event related to the loading process of config packs.
|
||||
*/
|
||||
public abstract class ConfigPackLoadEvent implements PackEvent {
|
||||
private final ConfigPack pack;
|
||||
private final ExceptionalConsumer<ConfigTemplate> configLoader;
|
||||
private final Loader loader;
|
||||
|
||||
public ConfigPackLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader, Loader loader) {
|
||||
this.pack = pack;
|
||||
this.configLoader = configLoader;
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigPack getPack() {
|
||||
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;
|
||||
}
|
||||
|
||||
public Loader getLoader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.event.events.config;
|
||||
|
||||
import com.dfsek.tectonic.config.ConfigTemplate;
|
||||
import com.dfsek.terra.config.fileloaders.Loader;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
|
||||
/**
|
||||
* Called when a config pack has finished loading.
|
||||
*/
|
||||
public class ConfigPackPostLoadEvent extends ConfigPackLoadEvent {
|
||||
public ConfigPackPostLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configTemplateLoader, Loader loader) {
|
||||
super(pack, configTemplateLoader, loader);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.event.events.config;
|
||||
|
||||
import com.dfsek.tectonic.config.ConfigTemplate;
|
||||
import com.dfsek.terra.config.fileloaders.Loader;
|
||||
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.
|
||||
*/
|
||||
public class ConfigPackPreLoadEvent extends ConfigPackLoadEvent {
|
||||
public ConfigPackPreLoadEvent(ConfigPack pack, ExceptionalConsumer<ConfigTemplate> configLoader, Loader loader) {
|
||||
super(pack, configLoader, loader);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.dfsek.terra.api.event.events.world;
|
||||
|
||||
import com.dfsek.terra.api.event.events.PackEvent;
|
||||
import com.dfsek.terra.config.pack.ConfigPack;
|
||||
import com.dfsek.terra.config.pack.WorldConfig;
|
||||
import com.dfsek.terra.world.TerraWorld;
|
||||
|
||||
/**
|
||||
* Called upon initialization of a TerraWorld.
|
||||
*/
|
||||
public class TerraWorldLoadEvent implements PackEvent {
|
||||
private final TerraWorld world;
|
||||
private final ConfigPack pack;
|
||||
|
||||
public TerraWorldLoadEvent(TerraWorld world, ConfigPack pack) {
|
||||
this.world = world;
|
||||
this.pack = pack;
|
||||
}
|
||||
|
||||
public TerraWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public ConfigPack getPack() {
|
||||
return pack;
|
||||
}
|
||||
|
||||
public WorldConfig getWorldConfig() {
|
||||
return world.getConfig();
|
||||
}
|
||||
}
|
||||
+45
@@ -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;
|
||||
}
|
||||
}
|
||||
+80
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.dfsek.terra.api.injection;
|
||||
|
||||
import com.dfsek.terra.api.injection.annotations.Inject;
|
||||
import com.dfsek.terra.api.injection.exception.InjectionException;
|
||||
import com.dfsek.terra.api.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Dynamic dependency injector.
|
||||
* <p>
|
||||
* Stores an object to inject, and injects it into objects passed to {@link #inject(Object)}.
|
||||
*
|
||||
* @param <T> Type of object to inject.
|
||||
*/
|
||||
public class Injector<T> {
|
||||
private final T value;
|
||||
private final Set<Class<? extends T>> targets = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Instantiate an Injector with a value to inject
|
||||
*
|
||||
* @param value Value to inject
|
||||
*/
|
||||
public Injector(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an explicit class as a target. Useful for applications where subclasses may cause issues with DI.
|
||||
*
|
||||
* @param target Target class type.
|
||||
*/
|
||||
public void addExplicitTarget(Class<? extends T> target) {
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the stored object into an object.
|
||||
* <p>
|
||||
* Injects the stored object into any non-static, non-final fields
|
||||
* annotated with {@link Inject},
|
||||
* with type matching the stored object or any explicit targets
|
||||
* ({@link #addExplicitTarget(Class)}.
|
||||
*
|
||||
* @param object Object to inject into
|
||||
* @throws InjectionException If:
|
||||
* <ul>
|
||||
* <li>Matching field annotated with {@link Inject} is final</li>
|
||||
* <li>Matching field annotated with {@link Inject} is static</li>
|
||||
* <li>A reflective access exception occurs</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void inject(Object object) throws InjectionException {
|
||||
for(Field field : ReflectionUtil.getFields(object.getClass())) {
|
||||
Inject inject = field.getAnnotation(Inject.class);
|
||||
if(inject == null) continue;
|
||||
if(value.getClass().equals(field.getType()) || targets.contains(field.getType())) {
|
||||
int mod = field.getModifiers();
|
||||
if(Modifier.isFinal(mod)) {
|
||||
throw new InjectionException("Attempted to inject final field: " + field);
|
||||
}
|
||||
if(Modifier.isStatic(mod)) {
|
||||
throw new InjectionException("Attempted to inject static field: " + field);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(object, value);
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new InjectionException("Failed to inject field: " + field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.injection.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies that a field is a target for dependency injection.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Inject {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.api.injection.exception;
|
||||
|
||||
import com.dfsek.terra.api.injection.Injector;
|
||||
|
||||
/**
|
||||
* Thrown when dynamic dependency injection cannot be completed by an {@link Injector}.
|
||||
*/
|
||||
public class InjectionException extends Exception {
|
||||
private static final long serialVersionUID = -6929631447064215387L;
|
||||
|
||||
public InjectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InjectionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math;
|
||||
|
||||
import com.dfsek.terra.api.util.FastRandom;
|
||||
import com.dfsek.terra.world.generation.math.Sampler;
|
||||
import com.dfsek.terra.world.generation.math.samplers.Sampler;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.dfsek.terra.api.math;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.NoiseSampler;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ProbabilityCollection<E> {
|
||||
private final Set<E> cont = new HashSet<>();
|
||||
private Object[] array = new Object[0];
|
||||
private int size;
|
||||
|
||||
public com.dfsek.terra.api.math.ProbabilityCollection<E> add(E item, int probability) {
|
||||
if(!cont.contains(item)) size++;
|
||||
cont.add(item);
|
||||
int oldLength = array.length;
|
||||
Object[] newArray = new Object[array.length + probability];
|
||||
System.arraycopy(array, 0, newArray, 0, array.length); // Expand array.
|
||||
array = newArray;
|
||||
for(int i = oldLength; i < array.length; i++) array[i] = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
public E get(Random r) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[r.nextInt(array.length)];
|
||||
}
|
||||
|
||||
public E get(NoiseSampler n, double x, double y, double z) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, y, z), array.length)];
|
||||
}
|
||||
|
||||
public E get(NoiseSampler n, double x, double z) {
|
||||
if(array.length == 0) return null;
|
||||
return (E) array[MathUtil.normalizeIndex(n.getNoise(x, z), array.length)];
|
||||
}
|
||||
|
||||
public int getTotalProbability() {
|
||||
return array.length;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Set<E> getContents() {
|
||||
return new HashSet<>(cont);
|
||||
}
|
||||
|
||||
public static final class Singleton<T> extends ProbabilityCollection<T> {
|
||||
private final T single;
|
||||
|
||||
public Singleton(T single) {
|
||||
this.single = single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProbabilityCollection<T> add(T item, int probability) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Random r) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(NoiseSampler n, double x, double y, double z) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(NoiseSampler n, double x, double z) {
|
||||
return single;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalProbability() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> getContents() {
|
||||
return Collections.singleton(single);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ public class Range implements Iterable<Integer> {
|
||||
return max;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range setMax(int max) {
|
||||
public Range setMax(int max) {
|
||||
this.max = max;
|
||||
return this;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class Range implements Iterable<Integer> {
|
||||
return min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range setMin(int min) {
|
||||
public Range setMin(int min) {
|
||||
this.min = min;
|
||||
return this;
|
||||
}
|
||||
@@ -42,35 +42,35 @@ public class Range implements Iterable<Integer> {
|
||||
return max - min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range multiply(int mult) {
|
||||
public Range multiply(int mult) {
|
||||
min *= mult;
|
||||
max *= mult;
|
||||
return this;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range reflect(int pt) {
|
||||
return new com.dfsek.terra.api.math.Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
|
||||
public Range reflect(int pt) {
|
||||
return new Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
|
||||
}
|
||||
|
||||
public int get(Random r) {
|
||||
return r.nextInt((max - min) + 1) + min;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range intersects(com.dfsek.terra.api.math.Range other) {
|
||||
public Range intersects(com.dfsek.terra.api.math.Range other) {
|
||||
try {
|
||||
return new com.dfsek.terra.api.math.Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
|
||||
return new Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
|
||||
} catch(IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range add(int add) {
|
||||
public Range add(int add) {
|
||||
this.min += add;
|
||||
this.max += add;
|
||||
return this;
|
||||
}
|
||||
|
||||
public com.dfsek.terra.api.math.Range sub(int sub) {
|
||||
public Range sub(int sub) {
|
||||
this.min -= sub;
|
||||
this.max -= sub;
|
||||
return this;
|
||||
@@ -89,7 +89,7 @@ public class Range implements Iterable<Integer> {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof com.dfsek.terra.api.math.Range)) return false;
|
||||
com.dfsek.terra.api.math.Range other = (com.dfsek.terra.api.math.Range) obj;
|
||||
Range other = (com.dfsek.terra.api.math.Range) obj;
|
||||
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
|
||||
}
|
||||
|
||||
@@ -100,10 +100,10 @@ public class Range implements Iterable<Integer> {
|
||||
}
|
||||
|
||||
private static class RangeIterator implements Iterator<Integer> {
|
||||
private final com.dfsek.terra.api.math.Range m;
|
||||
private final Range m;
|
||||
private Integer current;
|
||||
|
||||
public RangeIterator(com.dfsek.terra.api.math.Range m) {
|
||||
public RangeIterator(Range m) {
|
||||
this.m = m;
|
||||
current = m.getMin();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.dfsek.terra.config.loaders.config.function.FunctionTemplate;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sampler implementation using Paralithic expression
|
||||
* Sampler3D implementation using Paralithic expression
|
||||
*/
|
||||
public class ExpressionSampler implements NoiseSampler {
|
||||
private final Expression expression;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise;
|
||||
|
||||
/**
|
||||
* Sampler implementation that returns a constant.
|
||||
* Sampler3D implementation that returns a constant.
|
||||
*/
|
||||
public class ConstantSampler extends NoiseFunction {
|
||||
private final double constant;
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.samplers.noise.random.WhiteNoiseSampler;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
public class GaborNoiseSampler extends NoiseFunction {
|
||||
private final WhiteNoiseSampler rand;
|
||||
private double k = 1.0;
|
||||
private double a = 0.1;
|
||||
private double f0 = 0.625;
|
||||
private double kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / a);
|
||||
private double omega0 = Math.PI * 0.25;
|
||||
private boolean isotropic = true;
|
||||
private double impulsesPerKernel = 64d;
|
||||
private double impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
|
||||
|
||||
private double impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
|
||||
private double g = FastMath.exp(-impulsesPerCell);
|
||||
|
||||
|
||||
public GaborNoiseSampler(int seed) {
|
||||
super(seed);
|
||||
rand = new WhiteNoiseSampler(seed);
|
||||
}
|
||||
|
||||
public void setIsotropic(boolean isotropic) {
|
||||
this.isotropic = isotropic;
|
||||
}
|
||||
|
||||
public void setImpulsesPerKernel(double impulsesPerKernel) {
|
||||
this.impulsesPerKernel = impulsesPerKernel;
|
||||
recalculateRadiusAndDensity();
|
||||
}
|
||||
|
||||
public void setA(double a) {
|
||||
this.a = a;
|
||||
recalculateRadiusAndDensity();
|
||||
}
|
||||
|
||||
public void setFrequency0(double f0) {
|
||||
this.f0 = f0;
|
||||
}
|
||||
|
||||
public void setRotation(double omega0) {
|
||||
this.omega0 = Math.PI * omega0;
|
||||
}
|
||||
|
||||
public void setDeviation(double k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
private void recalculateRadiusAndDensity() {
|
||||
kernelRadius = (FastMath.sqrt(-FastMath.log(0.05) / Math.PI) / this.a);
|
||||
impulseDensity = (impulsesPerKernel / (Math.PI * kernelRadius * kernelRadius));
|
||||
impulsesPerCell = impulseDensity * kernelRadius * kernelRadius;
|
||||
g = FastMath.exp(-impulsesPerCell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double z) {
|
||||
return gaborNoise(seed, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
return gaborNoise(seed, x, z);
|
||||
}
|
||||
|
||||
private double gaborNoise(int seed, double x, double y) {
|
||||
x /= kernelRadius;
|
||||
y /= kernelRadius;
|
||||
int xi = fastFloor(x);
|
||||
int yi = fastFloor(y);
|
||||
double xf = x - xi;
|
||||
double yf = y - yi;
|
||||
double noise = 0;
|
||||
for(int dx = -1; dx <= 1; dx++) {
|
||||
for(int dz = -1; dz <= 1; dz++) {
|
||||
noise += calculateCell(seed, xi + dx, yi + dz, xf - dx, yf - dz);
|
||||
}
|
||||
}
|
||||
return noise;
|
||||
}
|
||||
|
||||
private double calculateCell(int seed, int xi, int yi, double x, double y) {
|
||||
long mashedSeed = murmur64(31L * xi + yi) + seed;
|
||||
|
||||
double gaussianSource = (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
|
||||
int impulses = 0;
|
||||
while(gaussianSource > g) {
|
||||
impulses++;
|
||||
gaussianSource *= (rand.getNoiseRaw(mashedSeed++) + 1) / 2;
|
||||
}
|
||||
|
||||
double noise = 0;
|
||||
for(int i = 0; i < impulses; i++) {
|
||||
noise += rand.getNoiseRaw(mashedSeed++) * gabor(isotropic ? (rand.getNoiseRaw(mashedSeed++) + 1) * Math.PI : omega0, x * kernelRadius, y * kernelRadius);
|
||||
}
|
||||
return noise;
|
||||
}
|
||||
|
||||
private double gabor(double omega_0, double x, double y) {
|
||||
return k * (FastMath.exp(-Math.PI * (a * a) * (x * x + y * y)) * fastCos(2 * Math.PI * f0 * (x * fastCos(omega_0) + y * fastSin(omega_0))));
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return f >= 0 ? (int) f : (int) f - 1;
|
||||
}
|
||||
|
||||
static final int precision = 100;
|
||||
|
||||
protected static int hash(int seed, int xPrimed, int yPrimed, int zPrimed) {
|
||||
int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed;
|
||||
|
||||
@@ -36,6 +38,8 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return hash;
|
||||
}
|
||||
|
||||
static final int modulus = 360 * precision;
|
||||
|
||||
protected static int fastRound(double f) {
|
||||
return f >= 0 ? (int) (f + 0.5f) : (int) (f - 0.5);
|
||||
}
|
||||
@@ -73,6 +77,48 @@ public abstract class NoiseFunction implements NoiseSampler {
|
||||
return FastMath.sqrt(f);
|
||||
}
|
||||
|
||||
static final double[] sin = new double[360 * 100]; // lookup table
|
||||
|
||||
static {
|
||||
for(int i = 0; i < sin.length; i++) {
|
||||
sin[i] = (float) Math.sin((double) (i) / (precision));
|
||||
}
|
||||
}
|
||||
|
||||
protected static int fastCeil(double f) {
|
||||
int i = (int) f;
|
||||
if(i < f) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Murmur64 hashing function
|
||||
*
|
||||
* @param h Input value
|
||||
* @return Hashed value
|
||||
*/
|
||||
protected static long murmur64(long h) {
|
||||
h ^= h >>> 33;
|
||||
h *= 0xff51afd7ed558ccdL;
|
||||
h ^= h >>> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53L;
|
||||
h ^= h >>> 33;
|
||||
return h;
|
||||
}
|
||||
|
||||
private static double sinLookup(int a) {
|
||||
return a >= 0 ? sin[a % (modulus)] : -sin[-a % (modulus)];
|
||||
}
|
||||
|
||||
protected static double fastSin(double a) {
|
||||
return sinLookup((int) (a * precision + 0.5f));
|
||||
}
|
||||
|
||||
protected static double fastCos(double a) {
|
||||
return sinLookup((int) ((a + Math.PI / 2) * precision + 0.5f));
|
||||
}
|
||||
|
||||
|
||||
public void setSeed(int seed) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
+25
-22
@@ -12,38 +12,41 @@ public class WhiteNoiseSampler extends NoiseFunction {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
public double getNoiseRaw(long seed) {
|
||||
return (Double.longBitsToDouble((murmur64(seed) & 0x000fffffffffffffL) | POSITIVE_POW1) - 1.5) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
|
||||
long base = (murmur64(hash) & 0x000fffffffffffffL)
|
||||
| POSITIVE_POW1; // Sign and exponent
|
||||
return (Double.longBitsToDouble(base) - 1.5) * 2;
|
||||
return (getNoiseUnmapped(seed, x, y) - 1.5) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
return (getNoiseUnmapped(seed, x, y, z) - 1.5) * 2;
|
||||
}
|
||||
|
||||
public double getNoiseUnmapped(int seed, double x, double y, double z) {
|
||||
long base = ((randomBits(seed, x, y, z)) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
|
||||
return Double.longBitsToDouble(base);
|
||||
}
|
||||
|
||||
public double getNoiseUnmapped(int seed, double x, double y) {
|
||||
long base = (randomBits(seed, x, y) & 0x000fffffffffffffL) | POSITIVE_POW1; // Sign and exponent
|
||||
return Double.longBitsToDouble(base);
|
||||
}
|
||||
|
||||
public long randomBits(int seed, double x, double y, double z) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
|
||||
long base = ((murmur64(hash)) & 0x000fffffffffffffL)
|
||||
| POSITIVE_POW1; // Sign and exponent
|
||||
return (Double.longBitsToDouble(base) - 1.5) * 2;
|
||||
return murmur64(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Murmur64 hashing function
|
||||
*
|
||||
* @param h Input value
|
||||
* @return Hashed value
|
||||
*/
|
||||
private static long murmur64(long h) {
|
||||
h ^= h >>> 33;
|
||||
h *= 0xff51afd7ed558ccdL;
|
||||
h ^= h >>> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53L;
|
||||
h ^= h >>> 33;
|
||||
return h;
|
||||
public long randomBits(int seed, double x, double y) {
|
||||
long hashX = Double.doubleToRawLongBits(x) ^ seed;
|
||||
long hashZ = Double.doubleToRawLongBits(y) ^ seed;
|
||||
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
|
||||
return murmur64(hash);
|
||||
}
|
||||
}
|
||||
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
package com.dfsek.terra.api.math.noise.samplers.noise.simplex;
|
||||
|
||||
public class SimplexSampler extends SimplexStyleSampler {
|
||||
private static final Double2[] GRAD_2D = {
|
||||
new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1),
|
||||
new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0),
|
||||
};
|
||||
private static final Double3[] GRAD_3D = {
|
||||
new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0),
|
||||
new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1),
|
||||
new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1),
|
||||
new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1),
|
||||
};
|
||||
|
||||
private static final double F2 = 1.0 / 2.0;
|
||||
private static final double F3 = (1.0 / 3.0);
|
||||
private static final double G2 = 1.0 / 4.0;
|
||||
private static final double G3 = (1.0 / 6.0);
|
||||
private static final double G33 = G3 * 3 - 1;
|
||||
|
||||
private static final int X_PRIME = 1619;
|
||||
private static final int Y_PRIME = 31337;
|
||||
private static final int Z_PRIME = 6971;
|
||||
|
||||
|
||||
public SimplexSampler(int seed) {
|
||||
super(seed);
|
||||
}
|
||||
|
||||
private static double gradCoord3D(int seed, int x, int y, int z, double xd, double yd, double zd) {
|
||||
int hash = seed;
|
||||
hash ^= X_PRIME * x;
|
||||
hash ^= Y_PRIME * y;
|
||||
hash ^= Z_PRIME * z;
|
||||
|
||||
hash = hash * hash * hash * 60493;
|
||||
hash = (hash >> 13) ^ hash;
|
||||
|
||||
Double3 g = GRAD_3D[hash & 15];
|
||||
|
||||
return xd * g.x + yd * g.y + zd * g.z;
|
||||
}
|
||||
|
||||
private static double gradCoord2D(int seed, int x, int y, double xd, double yd) {
|
||||
int hash = seed;
|
||||
hash ^= X_PRIME * x;
|
||||
hash ^= Y_PRIME * y;
|
||||
|
||||
hash = hash * hash * hash * 60493;
|
||||
hash = (hash >> 13) ^ hash;
|
||||
|
||||
Double2 g = GRAD_2D[hash & 7];
|
||||
|
||||
return xd * g.x + yd * g.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y) {
|
||||
double t = (x + y) * F2;
|
||||
int i = fastFloor(x + t);
|
||||
int j = fastFloor(y + t);
|
||||
|
||||
t = (i + j) * G2;
|
||||
double X0 = i - t;
|
||||
double Y0 = j - t;
|
||||
|
||||
double x0 = x - X0;
|
||||
double y0 = y - Y0;
|
||||
|
||||
int i1, j1;
|
||||
if(x0 > y0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
} else {
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
}
|
||||
|
||||
double x1 = x0 - i1 + G2;
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1 + F2;
|
||||
double y2 = y0 - 1 + F2;
|
||||
|
||||
double n0, n1, n2;
|
||||
|
||||
t = 0.5 - x0 * x0 - y0 * y0;
|
||||
if(t < 0) {
|
||||
n0 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n0 = t * t * gradCoord2D(seed, i, j, x0, y0);
|
||||
}
|
||||
|
||||
t = 0.5 - x1 * x1 - y1 * y1;
|
||||
if(t < 0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1);
|
||||
}
|
||||
|
||||
t = 0.5 - x2 * x2 - y2 * y2;
|
||||
if(t < 0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2);
|
||||
}
|
||||
|
||||
return 50 * (n0 + n1 + n2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNoiseRaw(int seed, double x, double y, double z) {
|
||||
double t = (x + y + z) * F3;
|
||||
int i = fastFloor(x + t);
|
||||
int j = fastFloor(y + t);
|
||||
int k = fastFloor(z + t);
|
||||
|
||||
t = (i + j + k) * G3;
|
||||
double x0 = x - (i - t);
|
||||
double y0 = y - (j - t);
|
||||
double z0 = z - (k - t);
|
||||
|
||||
int i1, j1, k1;
|
||||
int i2, j2, k2;
|
||||
|
||||
if(x0 >= y0) {
|
||||
if(y0 >= z0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
} else if(x0 >= z0) {
|
||||
i1 = 1;
|
||||
j1 = 0;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
} else // x0 < z0
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 1;
|
||||
j2 = 0;
|
||||
k2 = 1;
|
||||
}
|
||||
} else // x0 < y0
|
||||
{
|
||||
if(y0 < z0) {
|
||||
i1 = 0;
|
||||
j1 = 0;
|
||||
k1 = 1;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} else if(x0 < z0) {
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 0;
|
||||
j2 = 1;
|
||||
k2 = 1;
|
||||
} else // x0 >= z0
|
||||
{
|
||||
i1 = 0;
|
||||
j1 = 1;
|
||||
k1 = 0;
|
||||
i2 = 1;
|
||||
j2 = 1;
|
||||
k2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double x1 = x0 - i1 + G3;
|
||||
double y1 = y0 - j1 + G3;
|
||||
double z1 = z0 - k1 + G3;
|
||||
double x2 = x0 - i2 + F3;
|
||||
double y2 = y0 - j2 + F3;
|
||||
double z2 = z0 - k2 + F3;
|
||||
double x3 = x0 + G33;
|
||||
double y3 = y0 + G33;
|
||||
double z3 = z0 + G33;
|
||||
|
||||
double n0, n1, n2, n3;
|
||||
|
||||
t = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
|
||||
if(t < 0) n0 = 0;
|
||||
else {
|
||||
t *= t;
|
||||
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0);
|
||||
}
|
||||
|
||||
t = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
|
||||
if(t < 0) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1);
|
||||
}
|
||||
|
||||
t = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
|
||||
if(t < 0) {
|
||||
n2 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2);
|
||||
}
|
||||
|
||||
t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
|
||||
if(t < 0) {
|
||||
n3 = 0;
|
||||
} else {
|
||||
t *= t;
|
||||
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3);
|
||||
}
|
||||
|
||||
return 32 * (n0 + n1 + n2 + n3);
|
||||
}
|
||||
|
||||
private static class Double2 {
|
||||
public final double x, y;
|
||||
|
||||
public Double2(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Double3 {
|
||||
public final double x, y, z;
|
||||
|
||||
public Double3(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.dfsek.terra.api.math.paralithic.noise;
|
||||
|
||||
import com.dfsek.terra.api.math.noise.NoiseSampler;
|
||||
import com.dfsek.terra.util.hash.HashMapDoubleDouble;
|
||||
import com.dfsek.terra.api.util.hash.HashMapDoubleDouble;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -170,4 +170,9 @@ public class Location implements Cloneable {
|
||||
public String toString() {
|
||||
return "[" + world + ": (" + getX() + ", " + getY() + ", " + getZ() + ")]";
|
||||
}
|
||||
|
||||
public Location multiply(double v) {
|
||||
vector.multiply(v);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.dfsek.terra.api.platform;
|
||||
|
||||
import com.dfsek.terra.api.platform.entity.Entity;
|
||||
|
||||
public interface Player extends Entity {
|
||||
}
|
||||
@@ -11,7 +11,9 @@ public interface Block extends Handle {
|
||||
|
||||
BlockState getState();
|
||||
|
||||
Block getRelative(BlockFace face);
|
||||
default Block getRelative(BlockFace face) {
|
||||
return getRelative(face, 1);
|
||||
}
|
||||
|
||||
Block getRelative(BlockFace face, int len);
|
||||
|
||||
@@ -19,7 +21,9 @@ public interface Block extends Handle {
|
||||
|
||||
Location getLocation();
|
||||
|
||||
MaterialData getType();
|
||||
default BlockType getType() {
|
||||
return getBlockData().getBlockType();
|
||||
}
|
||||
|
||||
int getX();
|
||||
|
||||
|
||||
@@ -3,11 +3,16 @@ package com.dfsek.terra.api.platform.block;
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface BlockData extends Cloneable, Handle {
|
||||
MaterialData getMaterial();
|
||||
|
||||
boolean matches(MaterialData materialData);
|
||||
BlockType getBlockType();
|
||||
|
||||
boolean matches(BlockData other);
|
||||
|
||||
BlockData clone();
|
||||
|
||||
String getAsString();
|
||||
|
||||
boolean isAir();
|
||||
|
||||
boolean isStructureVoid();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.dfsek.terra.api.platform.block;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface BlockType extends Handle {
|
||||
BlockData getDefaultData();
|
||||
|
||||
boolean isSolid();
|
||||
|
||||
boolean isWater();
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.dfsek.terra.api.platform.block;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
public interface MaterialData extends Handle {
|
||||
boolean matches(MaterialData other);
|
||||
|
||||
boolean matches(BlockData other);
|
||||
|
||||
boolean isSolid();
|
||||
|
||||
boolean isAir();
|
||||
|
||||
double getMaxDurability();
|
||||
|
||||
BlockData createBlockData();
|
||||
}
|
||||
@@ -3,7 +3,12 @@ package com.dfsek.terra.api.platform.entity;
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.platform.CommandSender;
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
|
||||
public interface Entity extends Handle, CommandSender {
|
||||
Location getLocation();
|
||||
|
||||
void setLocation(Location location);
|
||||
|
||||
World getWorld();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.dfsek.terra.api.platform.entity;
|
||||
|
||||
public interface Player extends Entity {
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.dfsek.terra.api.platform.handle;
|
||||
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.inventory.ItemStack;
|
||||
import com.dfsek.terra.api.platform.inventory.Item;
|
||||
import com.dfsek.terra.api.platform.inventory.item.Enchantment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface ItemHandle {
|
||||
ItemStack newItemStack(MaterialData material, int amount);
|
||||
|
||||
Item createItem(String data);
|
||||
|
||||
Enchantment getEnchantment(String id);
|
||||
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
package com.dfsek.terra.api.platform.handle;
|
||||
|
||||
import com.dfsek.terra.api.platform.block.Block;
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.platform.block.BlockData;
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.entity.EntityType;
|
||||
import com.dfsek.terra.api.platform.entity.Player;
|
||||
import com.dfsek.terra.api.util.generic.pair.Pair;
|
||||
|
||||
/**
|
||||
* Interface to be implemented for world manipulation.
|
||||
*/
|
||||
public interface WorldHandle {
|
||||
void setBlockData(Block block, BlockData data, boolean physics);
|
||||
|
||||
BlockData getBlockData(Block block);
|
||||
|
||||
MaterialData getType(Block block);
|
||||
|
||||
BlockData createBlockData(String data);
|
||||
|
||||
MaterialData createMaterialData(String data);
|
||||
|
||||
EntityType getEntity(String id);
|
||||
|
||||
/**
|
||||
* Get the locations selected by a player. (Usually via WorldEdit)
|
||||
*
|
||||
* @param player Player to get locations for
|
||||
* @return Pair of locations.
|
||||
*/
|
||||
default Pair<Location, Location> getSelectedLocation(Player player) {
|
||||
throw new UnsupportedOperationException("Cannot get selection on this platform.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.dfsek.terra.api.platform.inventory;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
|
||||
/**
|
||||
* An inventory item.
|
||||
*/
|
||||
public interface Item extends Handle {
|
||||
ItemStack newItemStack(int amount);
|
||||
|
||||
double getMaxDurability();
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
package com.dfsek.terra.api.platform.inventory;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.block.MaterialData;
|
||||
import com.dfsek.terra.api.platform.inventory.item.Damageable;
|
||||
import com.dfsek.terra.api.platform.inventory.item.ItemMeta;
|
||||
|
||||
public interface ItemStack extends Handle, Cloneable {
|
||||
public interface ItemStack extends Handle {
|
||||
int getAmount();
|
||||
|
||||
void setAmount(int i);
|
||||
|
||||
MaterialData getType();
|
||||
|
||||
ItemStack clone();
|
||||
Item getType();
|
||||
|
||||
ItemMeta getItemMeta();
|
||||
|
||||
void setItemMeta(ItemMeta meta);
|
||||
|
||||
default boolean isDamageable() {
|
||||
return getItemMeta() instanceof Damageable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.dfsek.terra.api.platform.modloader;
|
||||
|
||||
public interface Mod {
|
||||
String getID();
|
||||
|
||||
String getVersion();
|
||||
|
||||
String getName();
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
/**
|
||||
* API for platform implementations. Mostly interfaces to be implemented by platform delegates.
|
||||
* <p>
|
||||
* Interfaces in this package generally should <b>not</b> be implemented by addons.
|
||||
*/
|
||||
package com.dfsek.terra.api.platform;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.api.platform.world;
|
||||
|
||||
|
||||
import com.dfsek.terra.api.math.vector.Location;
|
||||
import com.dfsek.terra.api.util.collections.MaterialSet;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public interface Tree {
|
||||
boolean plant(Location l, Random r);
|
||||
|
||||
MaterialSet getSpawnable();
|
||||
}
|
||||
@@ -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.EntityType;
|
||||
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.util.UUID;
|
||||
@@ -17,19 +19,27 @@ public interface World extends Handle {
|
||||
|
||||
ChunkGenerator getGenerator();
|
||||
|
||||
String getName();
|
||||
|
||||
UUID getUID();
|
||||
|
||||
boolean isChunkGenerated(int x, int z);
|
||||
|
||||
Chunk getChunkAt(int x, int z);
|
||||
|
||||
File getWorldFolder();
|
||||
default Chunk getChunkAt(Location location) {
|
||||
return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||
}
|
||||
|
||||
Block getBlockAt(int x, int y, int z);
|
||||
|
||||
Block getBlockAt(Location l);
|
||||
default Block getBlockAt(Location l) {
|
||||
return getBlockAt(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
||||
}
|
||||
|
||||
Entity spawnEntity(Location location, EntityType entityType);
|
||||
|
||||
int getMinHeight();
|
||||
|
||||
default boolean isTerraWorld() {
|
||||
return getGenerator().getHandle() instanceof GeneratorWrapper;
|
||||
}
|
||||
|
||||
default TerraChunkGenerator getTerraGenerator() {
|
||||
return ((GeneratorWrapper) getGenerator().getHandle()).getHandle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.dfsek.terra.api.platform.world.generator;
|
||||
|
||||
import com.dfsek.terra.api.platform.world.ChunkAccess;
|
||||
|
||||
public interface ChunkData extends ChunkAccess {
|
||||
/**
|
||||
* Get the maximum height for the chunk.
|
||||
* <p>
|
||||
* Setting blocks at or above this height will do nothing.
|
||||
*
|
||||
* @return the maximum height
|
||||
*/
|
||||
int getMaxHeight();
|
||||
}
|
||||
@@ -1,43 +1,7 @@
|
||||
package com.dfsek.terra.api.platform.world.generator;
|
||||
|
||||
import com.dfsek.terra.api.platform.Handle;
|
||||
import com.dfsek.terra.api.platform.world.BiomeGrid;
|
||||
import com.dfsek.terra.api.platform.world.ChunkAccess;
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public interface ChunkGenerator extends Handle {
|
||||
boolean isParallelCapable();
|
||||
|
||||
boolean shouldGenerateCaves();
|
||||
|
||||
boolean shouldGenerateDecorations();
|
||||
|
||||
boolean shouldGenerateMobs();
|
||||
|
||||
boolean shouldGenerateStructures();
|
||||
|
||||
ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome);
|
||||
|
||||
|
||||
List<BlockPopulator> getDefaultPopulators(World world);
|
||||
|
||||
@Nullable
|
||||
TerraChunkGenerator getTerraGenerator();
|
||||
|
||||
interface ChunkData extends ChunkAccess {
|
||||
/**
|
||||
* Get the maximum height for the chunk.
|
||||
* <p>
|
||||
* Setting blocks at or above this height will do nothing.
|
||||
*
|
||||
* @return the maximum height
|
||||
*/
|
||||
int getMaxHeight();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user