Compare commits
260
Commits
forge/1.16
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f8bf11660
|
||
|
|
f8dcf33fcb
|
||
|
|
116976d8e8 | ||
|
|
800a4fa743 | ||
|
|
3124d52e3b | ||
|
|
b973781649 | ||
|
|
107bb79474 | ||
|
|
f35cb20714 | ||
|
|
aab04df0b6 | ||
|
|
eda3874ba7 | ||
|
|
609fea09f6 | ||
|
|
d3956d688b | ||
|
|
e46c1c3abe | ||
|
|
ca668d2434 | ||
|
|
af621921e9 | ||
|
|
9f165e647d | ||
|
|
4cff7a4711 | ||
|
|
a60f083e08 | ||
|
|
74f6ab4866 | ||
|
|
ea68abc3cc | ||
|
|
73813786c0 | ||
|
|
7b5594c196 | ||
|
|
87a13cf729 | ||
|
|
324f042f77 | ||
|
|
83a9e8b8c3 | ||
|
|
2a58fdaf80 | ||
|
|
62c437dcc8 | ||
|
|
a2e8736c14 | ||
|
|
b93468e9f6 | ||
|
|
9947f37a26 | ||
|
|
ce93af7c81 | ||
|
|
f132397ae6 | ||
|
|
c15c600b45 | ||
|
|
f59d7c4759 | ||
|
|
76b678aec4 | ||
|
|
e51f093ce8 | ||
|
|
10f1478474 | ||
|
|
29c92a8dc8 | ||
|
|
9e447d4a2a | ||
|
|
1726479648 | ||
|
|
117c03fb46 | ||
|
|
4d562aa62c | ||
|
|
e398cc2d8e | ||
|
|
e6137925f3 | ||
|
|
f08fb59aa3 | ||
|
|
f9429cd717 | ||
|
|
fc7e1b0bae | ||
|
|
c833f27a92 | ||
|
|
5975a9f5f3 | ||
|
|
0f65dbc19b | ||
|
|
646043cdf0 | ||
|
|
caef3640d1 | ||
|
|
fe130e8b8e | ||
|
|
586a14e409 | ||
|
|
213c5ce763 | ||
|
|
cadf998c32 | ||
|
|
886be957cd | ||
|
|
580f0072e3 | ||
|
|
5079beb68e | ||
|
|
f609ab4a34 | ||
|
|
ee02cec5ac | ||
|
|
e2d8f7b89b | ||
|
|
f12a5a9258 | ||
|
|
709f2a5376 | ||
|
|
329937a7f7 | ||
|
|
61ca4e7bbf | ||
|
|
6edaeaa0b0 | ||
|
|
176928cff2 | ||
|
|
1cdd07b2d5 | ||
|
|
ebd263954a | ||
|
|
b487702986 | ||
|
|
3597ec3465 | ||
|
|
33b6080ccd | ||
|
|
bfb30e58cc | ||
|
|
4a329e8029 | ||
|
|
f435293368 | ||
|
|
3666d58e88 | ||
|
|
38e99a8703 | ||
|
|
2ff6a5dddf | ||
|
|
b1d088bef6 | ||
|
|
c5f15db2b8 | ||
|
|
98baadfe32 | ||
|
|
d4c0af18aa | ||
|
|
6df5aa1e1e | ||
|
|
410ddd106f | ||
|
|
5d83912c3b | ||
|
|
953196facb | ||
|
|
9474b9144c | ||
|
|
b347572f84 | ||
|
|
2a3f089861 | ||
|
|
f8b7d0fba5 | ||
|
|
96bb1ea7e2 | ||
|
|
9ee34d3983 | ||
|
|
dbc51fbedf | ||
|
|
0f914e461f | ||
|
|
66179d4a45 | ||
|
|
75640077b4 | ||
|
|
b7cbd6775b | ||
|
|
f912195a26 | ||
|
|
02923bee53 | ||
|
|
bda5898a35 | ||
|
|
5eecf8b671 | ||
|
|
e2b76451ab | ||
|
|
51c2dae5f9 | ||
|
|
c17ad230ca | ||
|
|
b4d2748f94 | ||
|
|
6922cd49f5 | ||
|
|
469dd57931 | ||
|
|
447c3daca0 | ||
|
|
cdd1d92fcb | ||
|
|
2776307e51 | ||
|
|
c25ea97301 | ||
|
|
eeaf3cccac | ||
|
|
0e0d5395b2 | ||
|
|
4e70b40e26 | ||
|
|
0cc7d22190 | ||
|
|
8473fee636 | ||
|
|
47a228f14a | ||
|
|
304b29b762 | ||
|
|
a6ac1dc4f7 | ||
|
|
b86fb33cbb | ||
|
|
c727a8b9f6 | ||
|
|
9a6ddb3b00 | ||
|
|
57fbe2c3eb | ||
|
|
1ac89047f5 | ||
|
|
18c3a53db5 | ||
|
|
21a9bd35cc | ||
|
|
423daa0e84 | ||
|
|
4dac670207 | ||
|
|
7c28e1b17c | ||
|
|
142af0c2c8 | ||
|
|
c356a9e008 | ||
|
|
9ba9e407b0 | ||
|
|
7464d503e6 | ||
|
|
e076408af9 | ||
|
|
00478faa4e | ||
|
|
0983ca3a8e | ||
|
|
8f49d24569 | ||
|
|
0d17535530 | ||
|
|
a605fa9e1a | ||
|
|
6a010c5c57 | ||
|
|
062a556a70 | ||
|
|
c0c03a16a1 | ||
|
|
d813a08b62 | ||
|
|
8783e099b1 | ||
|
|
f7669ca67f | ||
|
|
3dce0ff86f | ||
|
|
cd4ee39655 | ||
|
|
309d3e185f | ||
|
|
20c749ed61 | ||
|
|
d85e81c936 | ||
|
|
064a0bbb25 | ||
|
|
8b643a9f2d | ||
|
|
a18d68b919 | ||
|
|
d77595ce07 | ||
|
|
95825514b8 | ||
|
|
2811dd732b | ||
|
|
9a61676c4f | ||
|
|
91427b80a7 | ||
|
|
dda4c845d4 | ||
|
|
ff39433836 | ||
|
|
ffb35e9059 | ||
|
|
2a4db272ea | ||
|
|
a3ad11188c | ||
|
|
a780ed5c8a | ||
|
|
1feaea45e4 | ||
|
|
0e6a974173 | ||
|
|
6b0d09bf46 | ||
|
|
41a7749afc | ||
|
|
e5e377176c | ||
|
|
1544a30715 | ||
|
|
79888db1f6 | ||
|
|
e34a49aee1 | ||
|
|
4708dbd47b | ||
|
|
a2951bed7b | ||
|
|
c84435efc1 | ||
|
|
ba7d2cb57f | ||
|
|
75e227b7dc | ||
|
|
b4c361f4f5 | ||
|
|
71e8c11fca | ||
|
|
b09a9d6e53 | ||
|
|
0db4a98c6d | ||
|
|
73c8628d39 | ||
|
|
fe1dac6977 | ||
|
|
a986ef17b0 | ||
|
|
91c9d9d335 | ||
|
|
bcdfe9918e | ||
|
|
807f24afbc | ||
|
|
41c4df3ca5 | ||
|
|
5af88bb328 | ||
|
|
67d4667057 | ||
|
|
7ccb15c834 | ||
|
|
9c1ee4601c | ||
|
|
39b04da630 | ||
|
|
1508169353 | ||
|
|
f73aba5d0d | ||
|
|
69655a4530 | ||
|
|
9a63393ec5 | ||
|
|
1fc2e036f5 | ||
|
|
867e3c2b2c | ||
|
|
7b92f22201 | ||
|
|
517be4692e | ||
|
|
8b38275da5 | ||
|
|
5abcdc3bbf | ||
|
|
150c7f2ee5 | ||
|
|
199d4faa2b | ||
|
|
4c71c18b12 | ||
|
|
c696d61fbe | ||
|
|
7b1254c6b6 | ||
|
|
3c680f4bbe | ||
|
|
b758dc0d16 | ||
|
|
e1a0c5c4f3 | ||
|
|
27d8d445ad | ||
|
|
93b2ffe082 | ||
|
|
ab592e7dc3 | ||
|
|
4b722138dd | ||
|
|
59a209f20e | ||
|
|
31b9889e67 | ||
|
|
99141b6cfd | ||
|
|
8a81e62c85 | ||
|
|
5613f94e01 | ||
|
|
3ee15e5cad | ||
|
|
6c5f8b59d8 | ||
|
|
10a508495b | ||
|
|
fdc69bab13 | ||
|
|
4391b21153 | ||
|
|
e73bb16451 | ||
|
|
5b17f2d979 | ||
|
|
650127c068 | ||
|
|
32072f3161 | ||
|
|
f534c46fb4 | ||
|
|
8e00001d21 | ||
|
|
a225c1251b | ||
|
|
53181d90e7 | ||
|
|
4b4f6249bf | ||
|
|
cb4f905ed9 | ||
|
|
0191428555 | ||
|
|
83731b0c33 | ||
|
|
c7f0e12418 | ||
|
|
eb9d3413f4 | ||
|
|
61c3383246 | ||
|
|
7cdf34bcab | ||
|
|
4cd8ac5f12 | ||
|
|
3574cdd9bc | ||
|
|
0ed5de2fb1 | ||
|
|
e19b46f282 | ||
|
|
d8aa40af23 | ||
|
|
0ed37bebea | ||
|
|
9fbe8ef271 | ||
|
|
66b38acce0 | ||
|
|
eb83576310 | ||
|
|
71da25cc68 | ||
|
|
8b60a8e13f | ||
|
|
2615e89817 | ||
|
|
76f378de2a | ||
|
|
bc5bc30b54 | ||
|
|
1fcd160666 | ||
|
|
e1b41d7005 | ||
|
|
a9cf9b5fa7 | ||
|
|
c55e573e17 |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
+17
-17
@@ -6,42 +6,42 @@ jobs:
|
||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- name: Gradle wrapper validation
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '16'
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
- name: Build
|
||||
run: ./gradlew build
|
||||
- name: Upload Bukkit
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Bukkit
|
||||
path: "bukkit/build/libs/Chunky-*.jar"
|
||||
- name: Upload Fabric
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Fabric
|
||||
path: "fabric/build/libs/Chunky-*.jar"
|
||||
- name: Upload Forge
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Forge
|
||||
path: "forge/build/libs/Chunky-*.jar"
|
||||
- name: Upload NeoForge
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NeoForge
|
||||
path: "neoforge/build/libs/Chunky-*.jar"
|
||||
- name: Upload Sponge
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Sponge
|
||||
path: "sponge/build/libs/Chunky-*.jar"
|
||||
@@ -0,0 +1,39 @@
|
||||
name: Deploy Javadoc to Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["javadoc"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: './docs/'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Chunky</name>
|
||||
<comment>Pre-generates chunks, quickly, efficiently, and safely</comment>
|
||||
<projects/>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments/>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<linkedResources/>
|
||||
<filteredResources/>
|
||||
</projectDescription>
|
||||
@@ -1,7 +1,26 @@
|
||||
## Chunky
|
||||
# Chunky
|
||||
|
||||
Pre-generates chunks, quickly, efficiently, and safely
|
||||

|
||||
|
||||
[Spigot Resource Page](https://www.spigotmc.org/resources/chunky.81534/)
|
||||
Minecraft chunk pre-generator, supporting a wide variety of server implementations including Spigot, Paper, Fabric, Forge, NeoForge, and Sponge.
|
||||
|
||||
[Wiki](https://github.com/pop4959/Chunky/wiki/Commands)
|
||||
Pre-generate chunks, quickly, efficiently, and safely!
|
||||
|
||||
## Getting Started
|
||||
|
||||
- [Installing](https://github.com/pop4959/Chunky/wiki/Installing)
|
||||
- [Pre-generating Chunks](https://github.com/pop4959/Chunky/wiki/Pregeneration)
|
||||
- [Command Reference](https://github.com/pop4959/Chunky/wiki/Commands)
|
||||
|
||||
## Support
|
||||
|
||||
For questions about Chunky, first try the [Wiki](https://github.com/pop4959/Chunky/wiki) to see if your question is already answered there.
|
||||
|
||||
If you can't find what you're looking for, visit us in the #chunky channel on our [Discord server](https://discord.gg/ZwVJukcNQG).
|
||||
|
||||
## Special Thanks
|
||||
|
||||
[](https://www.yourkit.com/)
|
||||
|
||||
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/),
|
||||
and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). We thank them for granting us open source project licenses so that we can continue to deliver the best performance possible.
|
||||
+54
-25
@@ -1,73 +1,102 @@
|
||||
import java.io.ByteArrayOutputStream
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
|
||||
plugins {
|
||||
eclipse
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
id("com.github.johnrengelman.shadow") version "7.0.0"
|
||||
id("com.gradleup.shadow") version "8.3.9" apply false
|
||||
}
|
||||
|
||||
subprojects {
|
||||
plugins.apply("java-library")
|
||||
plugins.apply("maven-publish")
|
||||
plugins.apply("com.github.johnrengelman.shadow")
|
||||
plugins.apply("com.gradleup.shadow")
|
||||
|
||||
group = "${project.property("group")}"
|
||||
group = project.property("group") as String
|
||||
version = "${project.property("version")}.${commitsSinceLastTag()}"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(8))
|
||||
}
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release = 17
|
||||
options.compilerArgs.add("-Xlint:none")
|
||||
}
|
||||
jar {
|
||||
|
||||
withType<AbstractArchiveTask> {
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
}
|
||||
|
||||
named<Jar>("jar") {
|
||||
archiveClassifier.set("noshade")
|
||||
}
|
||||
shadowJar {
|
||||
|
||||
named<ShadowJar>("shadowJar") {
|
||||
archiveClassifier.set("")
|
||||
archiveFileName.set("${rootProject.name.capitalize()}-${project.version}.jar")
|
||||
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
|
||||
isEnableRelocation = true
|
||||
relocationPrefix = "${project.group}.${rootProject.name.lowercase()}.shadow"
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
|
||||
named("build") {
|
||||
dependsOn("shadowJar")
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
if (project.hasProperty("mavenUsername") && project.hasProperty("mavenPassword")) {
|
||||
if (hasProperty("mavenUsername") && hasProperty("mavenPassword")) {
|
||||
maven {
|
||||
credentials {
|
||||
username = "${project.property("mavenUsername")}"
|
||||
password = "${project.property("mavenPassword")}"
|
||||
username = project.property("mavenUsername") as String
|
||||
password = project.property("mavenPassword") as String
|
||||
}
|
||||
url = uri("https://repo.codemc.io/repository/maven-releases/")
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = "${project.group}"
|
||||
artifactId = project.name
|
||||
version = "${project.version}"
|
||||
from(components["java"])
|
||||
}
|
||||
publications.create<MavenPublication>("maven") {
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name
|
||||
version = project.version.toString()
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
val pluginArtifact by tasks.registering(Sync::class) {
|
||||
dependsOn(":Chunky-bukkit:shadowJar")
|
||||
from(project(":Chunky-bukkit").tasks.named("shadowJar"))
|
||||
into(layout.buildDirectory.dir("libs"))
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn(pluginArtifact)
|
||||
}
|
||||
|
||||
fun commitsSinceLastTag(): String {
|
||||
val tagDescription = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "describe", "--tags")
|
||||
standardOutput = tagDescription
|
||||
}
|
||||
if (tagDescription.toString().indexOf('-') < 0) {
|
||||
return "0"
|
||||
}
|
||||
return tagDescription.toString().split('-')[1]
|
||||
val desc = tagDescription.toString().trim()
|
||||
return if ('-' !in desc) "0" else desc.split('-')[1]
|
||||
}
|
||||
+23
-9
@@ -1,19 +1,30 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://papermc.io/repo/repository/maven-public")
|
||||
maven("https://repo.purpurmc.org/snapshots")
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(group = "org.spigotmc", name = "spigot-api", version = "1.17.1-R0.1-SNAPSHOT")
|
||||
implementation(group = "io.papermc", name = "paperlib", version = "1.0.6")
|
||||
implementation(group = "org.bstats", name = "bstats-bukkit", version = "2.2.1")
|
||||
implementation(project(":chunky-common"))
|
||||
compileOnly(group = "org.purpurmc.purpur", name = "purpur-api", version = "1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly(group = "com.github.Puremin0rez", name = "WorldBorder", version = "1.19") {
|
||||
isTransitive = false
|
||||
}
|
||||
implementation(group = "org.bstats", name = "bstats-bukkit", version = "3.0.2")
|
||||
implementation(project(":Chunky-common"))
|
||||
implementation(project(":Chunky-paper"))
|
||||
implementation(project(":Chunky-folia"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching("plugin.yml") {
|
||||
expand(
|
||||
"name" to rootProject.name.capitalize(),
|
||||
"name" to project.property("artifactName"),
|
||||
"version" to project.version,
|
||||
"group" to project.group,
|
||||
"author" to project.property("author"),
|
||||
@@ -22,10 +33,13 @@ tasks {
|
||||
}
|
||||
}
|
||||
shadowJar {
|
||||
archiveClassifier.set("")
|
||||
isEnableRelocation = false
|
||||
minimize {
|
||||
exclude(project(":chunky-common"))
|
||||
exclude(project(":Chunky-common"))
|
||||
exclude(project(":Chunky-paper"))
|
||||
exclude(project(":Chunky-folia"))
|
||||
}
|
||||
relocate("io.papermc.lib", "${project.group}.${rootProject.name}.lib.paperlib")
|
||||
relocate("org.bstats", "${project.group}.${rootProject.name}.lib.bstats")
|
||||
relocate("org.bstats", "${project.group}.${rootProject.name.lowercase()}.lib.bstats")
|
||||
}
|
||||
}
|
||||
@@ -1,95 +1,123 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.api.ChunkyAPI;
|
||||
import org.popcraft.chunky.command.ChunkyCommand;
|
||||
import org.popcraft.chunky.command.CommandArguments;
|
||||
import org.popcraft.chunky.command.CommandLiteral;
|
||||
import org.popcraft.chunky.integration.WorldBorderIntegration;
|
||||
import org.popcraft.chunky.platform.BukkitConfig;
|
||||
import org.popcraft.chunky.platform.BukkitPlayer;
|
||||
import org.popcraft.chunky.platform.BukkitSender;
|
||||
import org.popcraft.chunky.platform.BukkitServer;
|
||||
import org.popcraft.chunky.platform.Folia;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
import org.popcraft.chunky.util.Version;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public final class ChunkyBukkit extends JavaPlugin {
|
||||
public final class ChunkyBukkit extends JavaPlugin implements Listener {
|
||||
private static final String COMMAND_PERMISSION_KEY = "chunky.command.";
|
||||
private Chunky chunky;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.chunky = new Chunky(new BukkitServer(this), new BukkitConfig(this));
|
||||
final Version currentVersion = Version.getCurrentMinecraftVersion();
|
||||
if (Version.v1_13_2.isEqualTo(currentVersion) && !PaperLib.isPaper()) {
|
||||
getLogger().severe(() -> translate("error_version_spigot"));
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
} else if (currentVersion.isValid() && Version.v1_13_2.isHigherThan(currentVersion)) {
|
||||
getLogger().severe(() -> translate("error_version"));
|
||||
final Version currentVersion = new Version(Bukkit.getBukkitVersion(), true);
|
||||
if (currentVersion.isValid() && Version.MINECRAFT_1_13_2.isHigherThan(currentVersion)) {
|
||||
getLogger().severe(() -> translate(TranslationKey.ERROR_VERSION));
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
getServer().getServicesManager().register(Chunky.class, chunky, this, ServicePriority.Normal);
|
||||
getServer().getServicesManager().register(ChunkyAPI.class, chunky.getApi(), this, ServicePriority.Normal);
|
||||
if (chunky.getConfig().getContinueOnRestart()) {
|
||||
getServer().getScheduler().scheduleSyncDelayedTask(this, () -> chunky.getCommands().get("continue").execute(chunky.getServer().getConsoleSender(), new String[]{}));
|
||||
final Runnable continueTask = () -> chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
|
||||
if (Folia.isFolia()) {
|
||||
Folia.onServerInit(this, continueTask);
|
||||
} else {
|
||||
getServer().getScheduler().scheduleSyncDelayedTask(this, continueTask);
|
||||
}
|
||||
}
|
||||
if (getServer().getPluginManager().getPlugin("WorldBorder") != null) {
|
||||
chunky.getServer().getIntegrations().put("border", new WorldBorderIntegration());
|
||||
}
|
||||
final Metrics metrics = new Metrics(this, 8211);
|
||||
metrics.addCustomChart(new SimplePie("language", () -> chunky.getConfig().getLanguage()));
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
chunky.disable();
|
||||
HandlerList.unregisterAll((Plugin) this);
|
||||
if (chunky != null) {
|
||||
chunky.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
Sender bukkitSender = new BukkitSender(sender);
|
||||
Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) {
|
||||
final Sender bukkitSender = sender instanceof final Player player ? new BukkitPlayer(player) : new BukkitSender(sender);
|
||||
final Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
final CommandArguments arguments = CommandArguments.of(Arrays.copyOfRange(args, Math.min(1, args.length), args.length));
|
||||
if (args.length > 0 && commands.containsKey(args[0].toLowerCase())) {
|
||||
if (sender.hasPermission("chunky.command." + args[0].toLowerCase())) {
|
||||
commands.get(args[0].toLowerCase()).execute(bukkitSender, args);
|
||||
if (sender.hasPermission(COMMAND_PERMISSION_KEY + args[0].toLowerCase())) {
|
||||
commands.get(args[0].toLowerCase()).execute(bukkitSender, arguments);
|
||||
} else {
|
||||
bukkitSender.sendMessage("command_no_permission");
|
||||
bukkitSender.sendMessage(TranslationKey.COMMAND_NO_PERMISSION);
|
||||
}
|
||||
} else {
|
||||
commands.get("help").execute(bukkitSender, new String[]{});
|
||||
commands.get(CommandLiteral.HELP).execute(bukkitSender, arguments);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) {
|
||||
if (args.length < 1) {
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
final Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
if (args.length == 1) {
|
||||
commands.keySet().stream().filter(name -> sender.hasPermission("chunky.command." + name)).forEach(suggestions::add);
|
||||
} else if (commands.containsKey(args[0].toLowerCase()) && sender.hasPermission("chunky.command." + args[0].toLowerCase())) {
|
||||
suggestions.addAll(commands.get(args[0].toLowerCase()).tabSuggestions(new BukkitSender(sender), args));
|
||||
commands.keySet().stream().filter(name -> sender.hasPermission(COMMAND_PERMISSION_KEY + name)).forEach(suggestions::add);
|
||||
} else if (commands.containsKey(args[0].toLowerCase()) && sender.hasPermission(COMMAND_PERMISSION_KEY + args[0].toLowerCase())) {
|
||||
final CommandArguments arguments = CommandArguments.of(Arrays.copyOfRange(args, 1, args.length));
|
||||
suggestions.addAll(commands.get(args[0].toLowerCase()).suggestions(arguments));
|
||||
}
|
||||
return suggestions.stream()
|
||||
.filter(s -> s.toLowerCase().contains(args[args.length - 1].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Chunky getChunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldInit(final WorldInitEvent event) {
|
||||
chunky.getRegionCache().clear(event.getWorld().getName());
|
||||
}
|
||||
}
|
||||
+13
-12
@@ -3,21 +3,22 @@ package org.popcraft.chunky.integration;
|
||||
import com.wimbli.WorldBorder.BorderData;
|
||||
import com.wimbli.WorldBorder.Config;
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public class WorldBorderIntegration implements BorderIntegration {
|
||||
@Override
|
||||
public boolean hasBorder(String world) {
|
||||
public boolean hasBorder(final String world) {
|
||||
return Config.Border(world) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Border getBorder(String world) {
|
||||
public Border getBorder(final String world) {
|
||||
return new Border() {
|
||||
@Override
|
||||
public Coordinate getCenter() {
|
||||
BorderData borderData = Config.Border(world);
|
||||
return new Coordinate(borderData.getX(), borderData.getZ());
|
||||
public Vector2 getCenter() {
|
||||
final BorderData borderData = Config.Border(world);
|
||||
return Vector2.of(borderData.getX(), borderData.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,15 +33,15 @@ public class WorldBorderIntegration implements BorderIntegration {
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
BorderData borderData = Config.Border(world);
|
||||
double radiusX = getRadiusX();
|
||||
double radiusZ = getRadiusZ();
|
||||
boolean round = borderData.getShape() == null ? Config.ShapeRound() : borderData.getShape();
|
||||
final BorderData borderData = Config.Border(world);
|
||||
final double radiusX = getRadiusX();
|
||||
final double radiusZ = getRadiusZ();
|
||||
final boolean round = borderData.getShape() == null ? Config.ShapeRound() : borderData.getShape();
|
||||
final String shape;
|
||||
if (radiusX == radiusZ) {
|
||||
shape = round ? "circle" : "square";
|
||||
shape = round ? ShapeType.CIRCLE : ShapeType.SQUARE;
|
||||
} else {
|
||||
shape = round ? "ellipse" : "rectangle";
|
||||
shape = round ? ShapeType.ELLIPSE : ShapeType.RECTANGLE;
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.WorldBorder;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public class BukkitBorder implements Border {
|
||||
final WorldBorder worldBorder;
|
||||
|
||||
public BukkitBorder(WorldBorder worldBorder) {
|
||||
public BukkitBorder(final WorldBorder worldBorder) {
|
||||
this.worldBorder = worldBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getCenter() {
|
||||
return new Coordinate(worldBorder.getCenter().getX(), worldBorder.getCenter().getZ());
|
||||
public Vector2 getCenter() {
|
||||
return Vector2.of(worldBorder.getCenter().getX(), worldBorder.getCenter().getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -27,6 +28,6 @@ public class BukkitBorder implements Border {
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
return "square";
|
||||
return ShapeType.SQUARE;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,28 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.FileConfigurationOptions;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BukkitConfig implements Config {
|
||||
private static final String TASKS_KEY = "tasks.";
|
||||
private static final List<String> HEADER = Arrays.asList("Chunky Configuration", "https://github.com/pop4959/Chunky/wiki/Configuration");
|
||||
private final ChunkyBukkit plugin;
|
||||
|
||||
public BukkitConfig(ChunkyBukkit plugin) {
|
||||
public BukkitConfig(final ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.getConfig().options().copyDefaults(true);
|
||||
plugin.getConfig().options().copyHeader(true);
|
||||
final FileConfigurationOptions options = plugin.getConfig().options();
|
||||
options.copyDefaults(true);
|
||||
try {
|
||||
FileConfigurationOptions.class.getMethod("header", String.class).invoke(options, String.join("\n", HEADER));
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
options.setHeader(HEADER);
|
||||
}
|
||||
plugin.saveConfig();
|
||||
Translator.setLanguage(getLanguage());
|
||||
}
|
||||
@@ -29,78 +32,6 @@ public class BukkitConfig implements Config {
|
||||
return plugin.getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Optional<GenerationTask> loadTask(World world) {
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
if (config.getConfigurationSection(TASKS_KEY + world.getName()) == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String worldKey = TASKS_KEY + world.getName() + ".";
|
||||
if (config.getBoolean(worldKey + "cancelled", false)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
double radiusX = config.getDouble(worldKey + "radius", 500);
|
||||
double radiusZ = config.getDouble(worldKey + "z-radius", radiusX);
|
||||
Selection.Builder selection = Selection.builder(world)
|
||||
.centerX(config.getDouble(worldKey + "x-center", 0))
|
||||
.centerZ(config.getDouble(worldKey + "z-center", 0))
|
||||
.radiusX(radiusX)
|
||||
.radiusZ(radiusZ)
|
||||
.pattern(config.getString(worldKey + "iterator", "loop"))
|
||||
.shape(config.getString(worldKey + "shape", "square"));
|
||||
long count = config.getLong(worldKey + "count", 0);
|
||||
long time = config.getLong(worldKey + "time", 0);
|
||||
return Optional.of(new GenerationTask(plugin.getChunky(), selection.build(), count, time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<GenerationTask> loadTasks() {
|
||||
List<GenerationTask> generationTasks = new ArrayList<>();
|
||||
plugin.getChunky().getServer().getWorlds().forEach(world -> loadTask(world).ifPresent(generationTasks::add));
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveTask(GenerationTask generationTask) {
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
Selection selection = generationTask.getSelection();
|
||||
String worldKey = TASKS_KEY + selection.world().getName() + ".";
|
||||
String shape = generationTask.getShape().name();
|
||||
config.set(worldKey + "cancelled", generationTask.isCancelled());
|
||||
config.set(worldKey + "radius", selection.radiusX());
|
||||
if ("rectangle".equals(shape) || "ellipse".equals(shape)) {
|
||||
config.set(worldKey + "z-radius", selection.radiusZ());
|
||||
}
|
||||
config.set(worldKey + "x-center", selection.centerX());
|
||||
config.set(worldKey + "z-center", selection.centerZ());
|
||||
config.set(worldKey + "iterator", generationTask.getChunkIterator().name());
|
||||
config.set(worldKey + "shape", shape);
|
||||
config.set(worldKey + "count", generationTask.getCount());
|
||||
config.set(worldKey + "time", generationTask.getTotalTime());
|
||||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveTasks() {
|
||||
plugin.getChunky().getGenerationTasks().values().forEach(this::saveTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTask(World world) {
|
||||
loadTask(world).ifPresent(generationTask -> {
|
||||
generationTask.stop(true);
|
||||
saveTask(generationTask);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void cancelTasks() {
|
||||
loadTasks().forEach(generationTask -> {
|
||||
generationTask.stop(true);
|
||||
saveTask(generationTask);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return plugin.getConfig().getInt("version", 0);
|
||||
@@ -116,6 +47,31 @@ public class BukkitConfig implements Config {
|
||||
return plugin.getConfig().getBoolean("continue-on-restart", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForceLoadExistingChunks() {
|
||||
return plugin.getConfig().getBoolean("force-load-existing-chunks", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSilent() {
|
||||
return plugin.getConfig().getBoolean("silent", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSilent(final boolean silent) {
|
||||
plugin.getConfig().set("silent", silent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval() {
|
||||
return plugin.getConfig().getInt("update-interval", 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateInterval(final int updateInterval) {
|
||||
plugin.getConfig().set("update-interval", updateInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
plugin.reloadConfig();
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translateKey;
|
||||
|
||||
public class BukkitPlayer extends BukkitSender implements Player {
|
||||
private static final boolean ACTION_BAR_SUPPORTED;
|
||||
private final JavaPlugin plugin = JavaPlugin.getPlugin(ChunkyBukkit.class);
|
||||
|
||||
static {
|
||||
boolean barSupported;
|
||||
try {
|
||||
org.bukkit.entity.Player.class.getMethod("spigot");
|
||||
barSupported = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
barSupported = false;
|
||||
}
|
||||
ACTION_BAR_SUPPORTED = barSupported;
|
||||
}
|
||||
|
||||
final org.bukkit.entity.Player player;
|
||||
|
||||
public BukkitPlayer(final org.bukkit.entity.Player player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return new BukkitWorld(player.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
final org.bukkit.Location location = player.getLocation();
|
||||
return new Location(getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Override
|
||||
public void teleport(final Location location) {
|
||||
final org.bukkit.World world = Bukkit.getWorld(location.getWorld().getName());
|
||||
final org.bukkit.Location loc = new org.bukkit.Location(world, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
final Entity vehicle = player.getVehicle();
|
||||
if (vehicle == null) {
|
||||
teleportAsync(player, loc);
|
||||
} else {
|
||||
final List<Entity> passengers = new ArrayList<>(vehicle.getPassengers());
|
||||
vehicle.eject();
|
||||
teleportAsync(player, loc).thenAcceptAsync(ignored -> {
|
||||
vehicle.teleport(player);
|
||||
for (final Entity passenger : passengers) {
|
||||
passenger.teleport(player);
|
||||
if (passenger instanceof final org.bukkit.entity.Player playerPassenger) {
|
||||
playerPassenger.hideEntity(plugin, vehicle);
|
||||
playerPassenger.showEntity(plugin, vehicle);
|
||||
}
|
||||
vehicle.addPassenger(passenger);
|
||||
}
|
||||
}, command -> plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, command, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> teleportAsync(final Entity entity, final org.bukkit.Location location) {
|
||||
if (Paper.isPaper()) {
|
||||
return Paper.teleportAsync(entity, location);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(entity.teleport(location));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendActionBar(final String key) {
|
||||
if (ACTION_BAR_SUPPORTED) {
|
||||
final String message = formatColored(translateKey(key, false));
|
||||
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
|
||||
} else {
|
||||
this.sendMessage(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,35 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translateKey;
|
||||
|
||||
public class BukkitSender implements Sender {
|
||||
final CommandSender sender;
|
||||
private static final Pattern RGB_PATTERN = Pattern.compile("&#[0-9a-fA-F]{6}");
|
||||
private static final boolean RGB_COLORS_SUPPORTED;
|
||||
|
||||
public BukkitSender(CommandSender sender) {
|
||||
static {
|
||||
boolean rgbSupported;
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.api.ChatColor");
|
||||
ChatColor.class.getMethod("of", String.class);
|
||||
rgbSupported = true;
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
rgbSupported = false;
|
||||
}
|
||||
RGB_COLORS_SUPPORTED = rgbSupported;
|
||||
}
|
||||
|
||||
private final CommandSender sender;
|
||||
|
||||
public BukkitSender(final CommandSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
@@ -26,17 +44,37 @@ public class BukkitSender implements Sender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getCoordinate() {
|
||||
if (sender instanceof Player) {
|
||||
Location location = ((Player) sender).getLocation();
|
||||
return new Coordinate(location.getBlockX(), location.getBlockZ());
|
||||
} else {
|
||||
return new Coordinate(0, 0);
|
||||
}
|
||||
public World getWorld() {
|
||||
return new BukkitWorld(Bukkit.getWorlds().get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String key, boolean prefixed, Object... args) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', translateKey(key, prefixed, args)));
|
||||
public Location getLocation() {
|
||||
return new Location(getWorld(), 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String key, final boolean prefixed, final Object... args) {
|
||||
sender.sendMessage(formatColored(translateKey(key, prefixed, args)));
|
||||
}
|
||||
|
||||
protected String formatColored(final String message) {
|
||||
String coloredMessage = message;
|
||||
if (RGB_COLORS_SUPPORTED) {
|
||||
Matcher rgbMatcher = RGB_PATTERN.matcher(message);
|
||||
while (rgbMatcher.find()) {
|
||||
final ChatColor rgbColor = ChatColor.of(rgbMatcher.group().substring(1));
|
||||
final String messageStart = coloredMessage.substring(0, rgbMatcher.start());
|
||||
final String messageEnd = coloredMessage.substring(rgbMatcher.end());
|
||||
coloredMessage = messageStart + rgbColor + messageEnd;
|
||||
rgbMatcher = RGB_PATTERN.matcher(coloredMessage);
|
||||
}
|
||||
}
|
||||
return org.bukkit.ChatColor.translateAlternateColorCodes('&', coloredMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.integration.Integration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -14,7 +14,7 @@ public class BukkitServer implements Server {
|
||||
private final ChunkyBukkit plugin;
|
||||
private final Map<String, Integration> integrations;
|
||||
|
||||
public BukkitServer(ChunkyBukkit plugin) {
|
||||
public BukkitServer(final ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
this.integrations = new HashMap<>();
|
||||
}
|
||||
@@ -25,21 +25,38 @@ public class BukkitServer implements Server {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<World> getWorld(String name) {
|
||||
org.bukkit.World world = Bukkit.getWorld(name);
|
||||
public Optional<World> getWorld(final String name) {
|
||||
final org.bukkit.World world = plugin.getServer().getWorld(name);
|
||||
return Optional.ofNullable(world == null ? null : new BukkitWorld(world));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<World> getWorlds() {
|
||||
List<World> worlds = new ArrayList<>();
|
||||
Bukkit.getWorlds().forEach(world -> worlds.add(new BukkitWorld(world)));
|
||||
final List<World> worlds = new ArrayList<>();
|
||||
plugin.getServer().getWorlds().forEach(world -> worlds.add(new BukkitWorld(world)));
|
||||
return worlds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getConsoleSender() {
|
||||
return new BukkitSender(Bukkit.getConsoleSender());
|
||||
public int getMaxWorldSize() {
|
||||
return plugin.getServer().getMaxWorldSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getConsole() {
|
||||
return new BukkitSender(plugin.getServer().getConsoleSender());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Player> getPlayers() {
|
||||
final Collection<Player> players = new ArrayList<>();
|
||||
plugin.getServer().getOnlinePlayers().forEach(player -> players.add(new BukkitPlayer(player)));
|
||||
return players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Player> getPlayer(final String name) {
|
||||
return Optional.ofNullable(plugin.getServer().getPlayer(name)).map(BukkitPlayer::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,22 +1,44 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Location;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BukkitWorld implements World {
|
||||
private static final boolean IS_GENERATED_SUPPORTED;
|
||||
private static final int TICKING_LOAD_DURATION = Input.tryInteger(System.getProperty("chunky.tickingLoadDuration")).orElse(0);
|
||||
private static final boolean AWAIT_TICKET_REMOVAL = Boolean.getBoolean("chunky.awaitTicketRemoval");
|
||||
private final JavaPlugin plugin = JavaPlugin.getPlugin(ChunkyBukkit.class);
|
||||
private final org.bukkit.World world;
|
||||
private final Border worldBorder;
|
||||
|
||||
public BukkitWorld(org.bukkit.World world) {
|
||||
static {
|
||||
boolean isGeneratedSupported;
|
||||
try {
|
||||
Chunk.class.getMethod("isGenerated");
|
||||
isGeneratedSupported = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
isGeneratedSupported = false;
|
||||
}
|
||||
IS_GENERATED_SUPPORTED = isGeneratedSupported;
|
||||
}
|
||||
|
||||
public BukkitWorld(final org.bukkit.World world) {
|
||||
this.world = world;
|
||||
this.worldBorder = new BukkitBorder(world.getWorldBorder());
|
||||
}
|
||||
@@ -27,15 +49,59 @@ public class BukkitWorld implements World {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkGenerated(int x, int z) {
|
||||
return PaperLib.isPaper() && PaperLib.isChunkGenerated(world, x, z);
|
||||
public String getKey() {
|
||||
return world.getKey().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> getChunkAtAsync(int x, int z) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
PaperLib.getChunkAtAsync(world, x, z).thenRun(() -> future.complete(null));
|
||||
return future;
|
||||
public CompletableFuture<Boolean> isChunkGenerated(final int x, final int z) {
|
||||
if (Paper.isPaper()) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return world.isChunkGenerated(x, z);
|
||||
} catch (CompletionException e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (IS_GENERATED_SUPPORTED) {
|
||||
return CompletableFuture.completedFuture(world.getChunkAt(x, z, false).isGenerated());
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> getChunkAtAsync(final int x, final int z) {
|
||||
final CompletableFuture<Void> chunkFuture;
|
||||
if (Paper.isPaper()) {
|
||||
chunkFuture = CompletableFuture.allOf(Paper.getChunkAtAsync(world, x, z));
|
||||
} else {
|
||||
chunkFuture = CompletableFuture.allOf(CompletableFuture.completedFuture(world.getChunkAt(x, z)));
|
||||
}
|
||||
if (TICKING_LOAD_DURATION > 0) {
|
||||
final CompletableFuture<Void> removeTicketFuture = new CompletableFuture<>();
|
||||
chunkFuture.thenAccept(ignored -> {
|
||||
final Runnable addTicketTask = () -> world.addPluginChunkTicket(x, z, plugin);
|
||||
final Runnable removeTicketTask = () -> {
|
||||
world.removePluginChunkTicket(x, z, plugin);
|
||||
removeTicketFuture.complete(null);
|
||||
};
|
||||
if (Folia.isFolia()) {
|
||||
final org.bukkit.Location location = new org.bukkit.Location(world, x << 4, 0, z << 4);
|
||||
Folia.schedule(plugin, location, addTicketTask);
|
||||
CompletableFuture.runAsync(() -> Folia.schedule(plugin, location, removeTicketTask), CompletableFuture.delayedExecutor(TICKING_LOAD_DURATION, TimeUnit.SECONDS));
|
||||
} else {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, addTicketTask);
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, removeTicketTask, TICKING_LOAD_DURATION * 20L);
|
||||
}
|
||||
});
|
||||
if (AWAIT_TICKET_REMOVAL) {
|
||||
return removeTicketFuture;
|
||||
}
|
||||
}
|
||||
return chunkFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,9 +115,9 @@ public class BukkitWorld implements World {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getSpawnCoordinate() {
|
||||
Location spawnLocation = world.getSpawnLocation();
|
||||
return new Coordinate(spawnLocation.getX(), spawnLocation.getZ());
|
||||
public Location getSpawn() {
|
||||
final org.bukkit.Location spawnLocation = world.getSpawnLocation();
|
||||
return new Location(this, spawnLocation.getX(), spawnLocation.getY(), spawnLocation.getZ(), spawnLocation.getYaw(), spawnLocation.getPitch());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,42 +126,81 @@ public class BukkitWorld implements World {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getEntitiesDirectory() {
|
||||
return getDirectory("entities");
|
||||
public int getElevation(final int x, final int z) {
|
||||
if (Folia.isFolia()) {
|
||||
final org.bukkit.Location location = new org.bukkit.Location(world, x, 0, z);
|
||||
return CompletableFuture
|
||||
.supplyAsync(() -> getElevationForLocation(x, z), command -> Folia.schedule(plugin, location, command))
|
||||
.join();
|
||||
} else {
|
||||
return getElevationForLocation(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getPOIDirectory() {
|
||||
return getDirectory("poi");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getRegionDirectory() {
|
||||
return getDirectory("region");
|
||||
}
|
||||
|
||||
private Optional<Path> getDirectory(final String name) {
|
||||
if (name != null) {
|
||||
try (Stream<Path> paths = Files.walk(world.getWorldFolder().toPath())) {
|
||||
return paths.filter(Files::isDirectory)
|
||||
.filter(path -> name.equals(path.getFileName().toString()))
|
||||
.findFirst();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
private int getElevationForLocation(final int x, final int z) {
|
||||
final int height = world.getHighestBlockYAt(x, z) + 1;
|
||||
final int logicalHeight = world.getLogicalHeight();
|
||||
if (height >= logicalHeight) {
|
||||
Block block = world.getBlockAt(x, logicalHeight, z);
|
||||
int air = 0;
|
||||
while (block.getY() > world.getMinHeight()) {
|
||||
block = block.getRelative(BlockFace.DOWN);
|
||||
final Material type = block.getType();
|
||||
if (type.isSolid() && air > 1) {
|
||||
return block.getY() + 1;
|
||||
}
|
||||
air = type.isAir() ? air + 1 : 0;
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
return world.equals(((BukkitWorld) o).world);
|
||||
public int getMaxElevation() {
|
||||
return world.getLogicalHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return world.hashCode();
|
||||
public void playEffect(final Player player, final String effect) {
|
||||
final Effect effectType;
|
||||
try {
|
||||
effectType = Effect.valueOf(effect.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
final Location location = player.getLocation();
|
||||
final org.bukkit.Location bukkitLocation = new org.bukkit.Location(world, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
world.playEffect(bukkitLocation, effectType, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSound(final Player player, final String sound) {
|
||||
final Sound soundType;
|
||||
try {
|
||||
soundType = Sound.valueOf(sound.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
final Location location = player.getLocation();
|
||||
final org.bukkit.Location bukkitLocation = new org.bukkit.Location(world, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
world.playSound(bukkitLocation, soundType, 2f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> getDirectory(final String name) {
|
||||
if (name == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final org.bukkit.World.Environment environment = world.getEnvironment();
|
||||
final String parent;
|
||||
if (org.bukkit.World.Environment.NETHER.equals(environment)) {
|
||||
parent = "DIM-1";
|
||||
} else if (org.bukkit.World.Environment.THE_END.equals(environment)) {
|
||||
parent = "DIM1";
|
||||
} else {
|
||||
parent = "";
|
||||
}
|
||||
final Path directory = world.getWorldFolder().toPath().resolve(parent).normalize().resolve(name);
|
||||
return Files.isDirectory(directory) ? Optional.of(directory) : Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
# Chunky Configuration
|
||||
# https://github.com/pop4959/Chunky/wiki/Configuration
|
||||
version: 1
|
||||
|
||||
version: 2
|
||||
language: en
|
||||
continue-on-restart: false
|
||||
continue-on-restart: true
|
||||
force-load-existing-chunks: false
|
||||
silent: false
|
||||
update-interval: 1
|
||||
@@ -2,7 +2,8 @@ name: ${name}
|
||||
version: ${version}
|
||||
main: ${group}.chunky.ChunkyBukkit
|
||||
api-version: 1.13
|
||||
softdepend: [dynmap, BlueMap, Pl3xMap, WorldBorder]
|
||||
folia-supported: true
|
||||
softdepend: [dynmap, BlueMap, squaremap, WorldBorder]
|
||||
load: POSTWORLD
|
||||
authors: [${author}]
|
||||
description: ${description}
|
||||
@@ -16,6 +17,9 @@ commands:
|
||||
permissions:
|
||||
chunky.command:
|
||||
default: op
|
||||
chunky.command.border:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.cancel:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
@@ -31,9 +35,6 @@ permissions:
|
||||
chunky.command.corners:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.trim:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.help:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
@@ -55,6 +56,9 @@ permissions:
|
||||
chunky.command.reload:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.selection:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.shape:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
@@ -67,6 +71,9 @@ permissions:
|
||||
chunky.command.start:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.trim:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.worldborder:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
@@ -89,6 +96,7 @@ permissions:
|
||||
- chunky.command.quiet
|
||||
- chunky.command.radius
|
||||
- chunky.command.reload
|
||||
- chunky.command.selection
|
||||
- chunky.command.shape
|
||||
- chunky.command.silent
|
||||
- chunky.command.spawn
|
||||
|
||||
+23
-13
@@ -1,16 +1,26 @@
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||
maven("https://repo.mikeprimm.com")
|
||||
maven("https://repo.pl3x.net")
|
||||
maven("https://jitpack.io")
|
||||
dependencies {
|
||||
compileOnly(group = "com.google.code.gson", name = "gson", version = "2.8.9")
|
||||
testImplementation(group = "junit", name = "junit", version = "4.13.2")
|
||||
testImplementation(group = "com.google.code.gson", name = "gson", version = "2.8.9")
|
||||
implementation(project(":Chunky-nbt"))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(group = "us.dynmap", name = "dynmap-api", version = "3.1")
|
||||
compileOnly(group = "com.github.BlueMap-Minecraft", name = "BlueMapAPI", version = "v1.6.0")
|
||||
compileOnly(group = "net.pl3x.map", name = "pl3xmap-api", version = "1.0.0-SNAPSHOT")
|
||||
compileOnly(group = "com.github.Brettflan", name = "WorldBorder", version = "c0d1772418")
|
||||
testImplementation(group = "junit", name = "junit", version = "4.13.2")
|
||||
testImplementation(group = "com.google.code.gson", name = "gson", version = "2.8.7")
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching("version.properties") {
|
||||
expand(
|
||||
"version" to project.version
|
||||
)
|
||||
}
|
||||
}
|
||||
javadoc {
|
||||
sourceSets {
|
||||
main {
|
||||
allJava
|
||||
}
|
||||
}
|
||||
setDestinationDir(rootProject.projectDir.resolve("docs/chunky/javadoc"))
|
||||
include("org/popcraft/chunky/api/**")
|
||||
exclude("org/popcraft/chunky/api/ChunkyAPIImpl.java")
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,40 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.command.*;
|
||||
import org.popcraft.chunky.api.ChunkyAPI;
|
||||
import org.popcraft.chunky.api.ChunkyAPIImpl;
|
||||
import org.popcraft.chunky.command.CancelCommand;
|
||||
import org.popcraft.chunky.command.CenterCommand;
|
||||
import org.popcraft.chunky.command.ChunkyCommand;
|
||||
import org.popcraft.chunky.command.CommandLiteral;
|
||||
import org.popcraft.chunky.command.ConfirmCommand;
|
||||
import org.popcraft.chunky.command.ContinueCommand;
|
||||
import org.popcraft.chunky.command.CornersCommand;
|
||||
import org.popcraft.chunky.command.HelpCommand;
|
||||
import org.popcraft.chunky.command.PatternCommand;
|
||||
import org.popcraft.chunky.command.PauseCommand;
|
||||
import org.popcraft.chunky.command.ProgressCommand;
|
||||
import org.popcraft.chunky.command.QuietCommand;
|
||||
import org.popcraft.chunky.command.RadiusCommand;
|
||||
import org.popcraft.chunky.command.ReloadCommand;
|
||||
import org.popcraft.chunky.command.SelectionCommand;
|
||||
import org.popcraft.chunky.command.ShapeCommand;
|
||||
import org.popcraft.chunky.command.SilentCommand;
|
||||
import org.popcraft.chunky.command.SpawnCommand;
|
||||
import org.popcraft.chunky.command.StartCommand;
|
||||
import org.popcraft.chunky.command.TrimCommand;
|
||||
import org.popcraft.chunky.command.WorldBorderCommand;
|
||||
import org.popcraft.chunky.command.WorldCommand;
|
||||
import org.popcraft.chunky.event.EventBus;
|
||||
import org.popcraft.chunky.platform.Config;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.Server;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.PendingAction;
|
||||
import org.popcraft.chunky.util.RegionCache;
|
||||
import org.popcraft.chunky.util.TaskLoader;
|
||||
import org.popcraft.chunky.util.TaskScheduler;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
import org.popcraft.chunky.util.Version;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -24,45 +49,37 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class Chunky {
|
||||
private final Server server;
|
||||
private final Config config;
|
||||
private final TaskLoader taskLoader;
|
||||
private final EventBus eventBus;
|
||||
private final Selection.Builder selection;
|
||||
private final TaskScheduler scheduler = new TaskScheduler();
|
||||
private final Map<World, GenerationTask> generationTasks = new ConcurrentHashMap<>();
|
||||
private final Options options = new Options();
|
||||
private final Map<String, GenerationTask> generationTasks = new ConcurrentHashMap<>();
|
||||
private final Map<String, TrimCommand.Task> trimTasks = new ConcurrentHashMap<>();
|
||||
private final Map<String, PendingAction> pendingActions = new HashMap<>();
|
||||
private final RegionCache regionCache = new RegionCache();
|
||||
private final double limit;
|
||||
private final Version version;
|
||||
private final Map<String, ChunkyCommand> commands;
|
||||
private final ChunkyAPI api;
|
||||
|
||||
public Chunky(Server server, Config config) {
|
||||
public Chunky(final Server server, final Config config) {
|
||||
this.server = server;
|
||||
this.config = config;
|
||||
this.selection = Selection.builder(server.getWorlds().get(0));
|
||||
this.taskLoader = new TaskLoader(this);
|
||||
this.eventBus = new EventBus();
|
||||
this.selection = Selection.builder(this, server.getWorlds().get(0));
|
||||
this.limit = loadLimit().orElse(Double.MAX_VALUE);
|
||||
this.version = loadVersion();
|
||||
this.commands = loadCommands();
|
||||
this.api = new ChunkyAPIImpl(this);
|
||||
ChunkyProvider.register(this);
|
||||
}
|
||||
|
||||
private Map<String, ChunkyCommand> loadCommands() {
|
||||
final Map<String, ChunkyCommand> commandMap = new HashMap<>();
|
||||
commandMap.put("cancel", new CancelCommand(this));
|
||||
commandMap.put("center", new CenterCommand(this));
|
||||
commandMap.put("confirm", new ConfirmCommand(this));
|
||||
commandMap.put("continue", new ContinueCommand(this));
|
||||
commandMap.put("corners", new CornersCommand(this));
|
||||
commandMap.put("help", new HelpCommand(this));
|
||||
commandMap.put("pattern", new PatternCommand(this));
|
||||
commandMap.put("pause", new PauseCommand(this));
|
||||
commandMap.put("progress", new ProgressCommand(this));
|
||||
commandMap.put("quiet", new QuietCommand(this));
|
||||
commandMap.put("radius", new RadiusCommand(this));
|
||||
commandMap.put("reload", new ReloadCommand(this));
|
||||
commandMap.put("shape", new ShapeCommand(this));
|
||||
commandMap.put("silent", new SilentCommand(this));
|
||||
commandMap.put("spawn", new SpawnCommand(this));
|
||||
commandMap.put("start", new StartCommand(this));
|
||||
commandMap.put("trim", new TrimCommand(this));
|
||||
commandMap.put("worldborder", new WorldBorderCommand(this));
|
||||
commandMap.put("world", new WorldCommand(this));
|
||||
return commandMap;
|
||||
public void disable() {
|
||||
taskLoader.saveTasks();
|
||||
getGenerationTasks().values().forEach(generationTask -> generationTask.stop(false));
|
||||
getScheduler().cancelTasks();
|
||||
ChunkyProvider.unregister();
|
||||
}
|
||||
|
||||
private Optional<Double> loadLimit() {
|
||||
@@ -76,10 +93,39 @@ public class Chunky {
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
getConfig().saveTasks();
|
||||
getGenerationTasks().values().forEach(generationTask -> generationTask.stop(false));
|
||||
getScheduler().cancelTasks();
|
||||
private Version loadVersion() {
|
||||
try (final InputStream input = getClass().getClassLoader().getResourceAsStream("version.properties")) {
|
||||
final Properties properties = new Properties();
|
||||
properties.load(input);
|
||||
return new Version(properties.getProperty("version"));
|
||||
} catch (IOException e) {
|
||||
return Version.INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ChunkyCommand> loadCommands() {
|
||||
final Map<String, ChunkyCommand> commandMap = new HashMap<>();
|
||||
commandMap.put(CommandLiteral.CANCEL, new CancelCommand(this));
|
||||
commandMap.put(CommandLiteral.CENTER, new CenterCommand(this));
|
||||
commandMap.put(CommandLiteral.CONFIRM, new ConfirmCommand(this));
|
||||
commandMap.put(CommandLiteral.CONTINUE, new ContinueCommand(this));
|
||||
commandMap.put(CommandLiteral.CORNERS, new CornersCommand(this));
|
||||
commandMap.put(CommandLiteral.HELP, new HelpCommand(this));
|
||||
commandMap.put(CommandLiteral.PATTERN, new PatternCommand(this));
|
||||
commandMap.put(CommandLiteral.PAUSE, new PauseCommand(this));
|
||||
commandMap.put(CommandLiteral.PROGRESS, new ProgressCommand(this));
|
||||
commandMap.put(CommandLiteral.QUIET, new QuietCommand(this));
|
||||
commandMap.put(CommandLiteral.RADIUS, new RadiusCommand(this));
|
||||
commandMap.put(CommandLiteral.RELOAD, new ReloadCommand(this));
|
||||
commandMap.put(CommandLiteral.SELECTION, new SelectionCommand(this));
|
||||
commandMap.put(CommandLiteral.SHAPE, new ShapeCommand(this));
|
||||
commandMap.put(CommandLiteral.SILENT, new SilentCommand(this));
|
||||
commandMap.put(CommandLiteral.SPAWN, new SpawnCommand(this));
|
||||
commandMap.put(CommandLiteral.START, new StartCommand(this));
|
||||
commandMap.put(CommandLiteral.TRIM, new TrimCommand(this));
|
||||
commandMap.put(CommandLiteral.WORLDBORDER, new WorldBorderCommand(this));
|
||||
commandMap.put(CommandLiteral.WORLD, new WorldCommand(this));
|
||||
return commandMap;
|
||||
}
|
||||
|
||||
public TaskScheduler getScheduler() {
|
||||
@@ -94,10 +140,22 @@ public class Chunky {
|
||||
return config;
|
||||
}
|
||||
|
||||
public Map<World, GenerationTask> getGenerationTasks() {
|
||||
public TaskLoader getTaskLoader() {
|
||||
return taskLoader;
|
||||
}
|
||||
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
public Map<String, GenerationTask> getGenerationTasks() {
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
public Map<String, TrimCommand.Task> getTrimTasks() {
|
||||
return trimTasks;
|
||||
}
|
||||
|
||||
public Map<String, ChunkyCommand> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
@@ -106,21 +164,17 @@ public class Chunky {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public Options getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public Optional<Runnable> getPendingAction(Sender sender) {
|
||||
public Optional<Runnable> getPendingAction(final Sender sender) {
|
||||
pendingActions.values().removeIf(PendingAction::hasExpired);
|
||||
PendingAction pendingAction = pendingActions.remove(sender.getName());
|
||||
final PendingAction pendingAction = pendingActions.remove(sender.getName());
|
||||
return Optional.ofNullable(pendingAction).map(PendingAction::getAction);
|
||||
}
|
||||
|
||||
public void setPendingAction(Sender sender, Runnable action) {
|
||||
public void setPendingAction(final Sender sender, final Runnable action) {
|
||||
pendingActions.put(sender.getName(), new PendingAction(action));
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
public void setLanguage(final String language) {
|
||||
Translator.setLanguage(language);
|
||||
}
|
||||
|
||||
@@ -131,4 +185,12 @@ public class Chunky {
|
||||
public double getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public ChunkyAPI getApi() {
|
||||
return api;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
public final class ChunkyProvider {
|
||||
private static Chunky instance;
|
||||
|
||||
private ChunkyProvider() {
|
||||
throw new UnsupportedOperationException("This class cannot be instantiated.");
|
||||
}
|
||||
|
||||
public static Chunky get() {
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("Chunky is not loaded.");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void register(final Chunky instance) {
|
||||
ChunkyProvider.instance = instance;
|
||||
}
|
||||
|
||||
static void unregister() {
|
||||
ChunkyProvider.instance = null;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,36 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.api.event.task.GenerationCompleteEvent;
|
||||
import org.popcraft.chunky.api.event.task.GenerationProgressEvent;
|
||||
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
|
||||
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
|
||||
import org.popcraft.chunky.iterator.ChunkIterator;
|
||||
import org.popcraft.chunky.iterator.ChunkIteratorFactory;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
import org.popcraft.chunky.shape.ShapeFactory;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Pair;
|
||||
import org.popcraft.chunky.util.RegionCache;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class GenerationTask implements Runnable {
|
||||
private static final int MAX_WORKING = 50;
|
||||
private static final int MAX_WORKING_COUNT = Input.tryInteger(System.getProperty("chunky.maxWorkingCount")).orElse(50);
|
||||
private static final double SAMPLE_INTERVAL = 1000d * Math.max(Input.tryInteger(System.getProperty("chunky.sampleInterval")).orElse(30), 30);
|
||||
private static final double SAMPLE_SUB_INTERVAL = SAMPLE_INTERVAL / 30;
|
||||
private final Chunky chunky;
|
||||
private final Selection selection;
|
||||
private final Shape shape;
|
||||
private final AtomicLong startTime = new AtomicLong();
|
||||
private final AtomicLong printTime = new AtomicLong();
|
||||
private final AtomicLong updateTime = new AtomicLong();
|
||||
private final AtomicLong finishedChunks = new AtomicLong();
|
||||
private final AtomicLong totalChunks = new AtomicLong();
|
||||
private final Deque<Pair<Long, AtomicLong>> updateSamples = new ConcurrentLinkedDeque<>();
|
||||
private final Progress progress;
|
||||
private final RegionCache.WorldState worldState;
|
||||
@@ -30,44 +38,44 @@ public class GenerationTask implements Runnable {
|
||||
private boolean stopped, cancelled;
|
||||
private long prevTime;
|
||||
|
||||
public GenerationTask(Chunky chunky, Selection selection, long count, long time) {
|
||||
public GenerationTask(final Chunky chunky, final Selection selection, final long count, final long time, final boolean cancelled) {
|
||||
this(chunky, selection);
|
||||
this.chunkIterator = ChunkIteratorFactory.getChunkIterator(selection, count);
|
||||
this.finishedChunks.set(count);
|
||||
this.cancelled = cancelled;
|
||||
this.prevTime = time;
|
||||
}
|
||||
|
||||
public GenerationTask(Chunky chunky, Selection selection) {
|
||||
public GenerationTask(final Chunky chunky, final Selection selection) {
|
||||
this.chunky = chunky;
|
||||
this.selection = selection;
|
||||
this.chunkIterator = ChunkIteratorFactory.getChunkIterator(selection);
|
||||
this.shape = ShapeFactory.getShape(selection);
|
||||
this.totalChunks.set(chunkIterator.total());
|
||||
this.progress = new Progress(selection.world().getName());
|
||||
this.worldState = chunky.getRegionCache().getWorld(selection.world().getName());
|
||||
}
|
||||
|
||||
private void update(int chunkX, int chunkZ, boolean loaded) {
|
||||
private synchronized void update(final int chunkX, final int chunkZ, final boolean loaded) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
progress.chunkCount = finishedChunks.addAndGet(1);
|
||||
progress.percentComplete = 100f * progress.chunkCount / totalChunks.get();
|
||||
progress.percentComplete = 100f * progress.chunkCount / chunkIterator.total();
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final Pair<Long, AtomicLong> bin = updateSamples.peekLast();
|
||||
if (loaded) {
|
||||
worldState.setGenerated(chunkX, chunkZ);
|
||||
if (bin != null && currentTime - bin.left() < 2e3) {
|
||||
if (bin != null && currentTime - bin.left() < SAMPLE_SUB_INTERVAL) {
|
||||
bin.right().addAndGet(1);
|
||||
} else if (updateSamples.add(Pair.of(currentTime, new AtomicLong(1)))) {
|
||||
while (!updateSamples.isEmpty() && currentTime - updateSamples.peek().left() > 1e4) {
|
||||
while (!updateSamples.isEmpty() && currentTime - updateSamples.peek().left() > SAMPLE_INTERVAL) {
|
||||
updateSamples.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
final Pair<Long, AtomicLong> oldest = updateSamples.peek();
|
||||
final long oldestTime = oldest == null ? currentTime : oldest.left();
|
||||
final long chunksLeft = totalChunks.get() - finishedChunks.get();
|
||||
final long chunksLeft = chunkIterator.total() - finishedChunks.get();
|
||||
final double timeDiff = (currentTime - oldestTime) / 1e3;
|
||||
if (chunksLeft > 0 && timeDiff < 1e-1) {
|
||||
return;
|
||||
@@ -89,29 +97,43 @@ public class GenerationTask implements Runnable {
|
||||
progress.seconds = time - progress.hours * 3600 - progress.minutes * 60;
|
||||
progress.chunkX = chunkX;
|
||||
progress.chunkZ = chunkZ;
|
||||
if (chunksLeft > 0 && (chunky.getOptions().isSilent() || ((currentTime - printTime.get()) / 1e3) < chunky.getOptions().getQuietInterval())) {
|
||||
chunky.getEventBus().call(new GenerationProgressEvent(progress.world, progress.chunkCount, progress.complete, progress.percentComplete, progress.hours, progress.minutes, progress.seconds, progress.rate, progress.chunkX, progress.chunkZ));
|
||||
if (progress.complete) {
|
||||
progress.sendUpdate(chunky.getServer().getConsole());
|
||||
chunky.getEventBus().call(new GenerationTaskUpdateEvent(this));
|
||||
return;
|
||||
}
|
||||
printTime.set(currentTime);
|
||||
progress.sendUpdate(chunky.getServer().getConsoleSender());
|
||||
final boolean silentMode = chunky.getConfig().isSilent();
|
||||
final boolean updateIntervalElapsed = ((currentTime - updateTime.get()) / 1e3) > chunky.getConfig().getUpdateInterval();
|
||||
if (updateIntervalElapsed) {
|
||||
if (!silentMode) {
|
||||
progress.sendUpdate(chunky.getServer().getConsole());
|
||||
}
|
||||
chunky.getEventBus().call(new GenerationTaskUpdateEvent(this));
|
||||
updateTime.set(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final String poolThreadName = Thread.currentThread().getName();
|
||||
Thread.currentThread().setName(String.format("Chunky-%s Thread", selection.world().getName()));
|
||||
final Semaphore working = new Semaphore(MAX_WORKING);
|
||||
if (!chunkIterator.process()) {
|
||||
stop(true);
|
||||
}
|
||||
final Semaphore working = new Semaphore(MAX_WORKING_COUNT);
|
||||
final boolean forceLoadExistingChunks = chunky.getConfig().isForceLoadExistingChunks();
|
||||
startTime.set(System.currentTimeMillis());
|
||||
while (!stopped && chunkIterator.hasNext()) {
|
||||
final ChunkCoordinate chunk = chunkIterator.next();
|
||||
final int chunkCenterX = (chunk.x << 4) + 8;
|
||||
final int chunkCenterZ = (chunk.z << 4) + 8;
|
||||
if (!shape.isBounding(chunkCenterX, chunkCenterZ) || worldState.isGenerated(chunk.x, chunk.z)) {
|
||||
update(chunk.x, chunk.z, false);
|
||||
final int chunkCenterX = (chunk.x() << 4) + 8;
|
||||
final int chunkCenterZ = (chunk.z() << 4) + 8;
|
||||
if (!shape.isBounding(chunkCenterX, chunkCenterZ)) {
|
||||
update(chunk.x(), chunk.z(), false);
|
||||
continue;
|
||||
}
|
||||
if (selection.world().isChunkGenerated(chunk.x, chunk.z)) {
|
||||
update(chunk.x, chunk.z, true);
|
||||
if (!forceLoadExistingChunks && worldState.isGenerated(chunk.x(), chunk.z())) {
|
||||
update(chunk.x(), chunk.z(), false);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
@@ -121,19 +143,31 @@ public class GenerationTask implements Runnable {
|
||||
stop(cancelled);
|
||||
break;
|
||||
}
|
||||
selection.world().getChunkAtAsync(chunk.x, chunk.z).thenRun(() -> {
|
||||
working.release();
|
||||
update(chunk.x, chunk.z, true);
|
||||
});
|
||||
final CompletableFuture<Boolean> isChunkGenerated = forceLoadExistingChunks ?
|
||||
CompletableFuture.completedFuture(false) :
|
||||
selection.world().isChunkGenerated(chunk.x(), chunk.z());
|
||||
isChunkGenerated
|
||||
.thenCompose(generated -> {
|
||||
if (Boolean.TRUE.equals(generated)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return selection.world().getChunkAtAsync(chunk.x(), chunk.z());
|
||||
}
|
||||
}).whenComplete((ignored, throwable) -> {
|
||||
working.release();
|
||||
update(chunk.x(), chunk.z(), true);
|
||||
});
|
||||
}
|
||||
if (stopped) {
|
||||
chunky.getServer().getConsoleSender().sendMessagePrefixed("task_stopped", selection.world().getName());
|
||||
chunky.getServer().getConsole().sendMessagePrefixed(TranslationKey.TASK_STOPPED, selection.world().getName());
|
||||
} else {
|
||||
cancelled = true;
|
||||
}
|
||||
chunky.getConfig().saveTask(this);
|
||||
chunky.getGenerationTasks().remove(selection.world());
|
||||
chunky.getTaskLoader().saveTask(this);
|
||||
chunky.getGenerationTasks().remove(selection.world().getName());
|
||||
Thread.currentThread().setName(poolThreadName);
|
||||
chunky.getEventBus().call(new GenerationTaskFinishEvent(this));
|
||||
chunky.getEventBus().call(new GenerationCompleteEvent(selection.world().getName()));
|
||||
}
|
||||
|
||||
public void stop(final boolean cancelled) {
|
||||
@@ -141,6 +175,10 @@ public class GenerationTask implements Runnable {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public Chunky getChunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public Selection getSelection() {
|
||||
return selection;
|
||||
}
|
||||
@@ -169,7 +207,8 @@ public class GenerationTask implements Runnable {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public static class Progress {
|
||||
@SuppressWarnings("unused")
|
||||
public static final class Progress {
|
||||
private final String world;
|
||||
private long chunkCount;
|
||||
private boolean complete;
|
||||
@@ -224,9 +263,9 @@ public class GenerationTask implements Runnable {
|
||||
|
||||
public void sendUpdate(final Sender sender) {
|
||||
if (complete) {
|
||||
sender.sendMessagePrefixed("task_done", world, chunkCount, String.format("%.2f", percentComplete), String.format("%01d", hours), String.format("%02d", minutes), String.format("%02d", seconds));
|
||||
sender.sendMessagePrefixed(TranslationKey.TASK_DONE, world, chunkCount, String.format("%.2f", percentComplete), String.format("%01d", hours), String.format("%02d", minutes), String.format("%02d", seconds));
|
||||
} else {
|
||||
sender.sendMessagePrefixed("task_update", world, chunkCount, String.format("%.2f", percentComplete), String.format("%01d", hours), String.format("%02d", minutes), String.format("%02d", seconds), String.format("%.1f", rate), chunkX, chunkZ);
|
||||
sender.sendMessagePrefixed(TranslationKey.TASK_UPDATE, world, chunkCount, String.format("%.2f", percentComplete), String.format("%01d", hours), String.format("%02d", minutes), String.format("%02d", seconds), String.format("%.1f", rate), chunkX, chunkZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
public class Options {
|
||||
private boolean isSilent;
|
||||
private int quietInterval = 1;
|
||||
|
||||
public boolean isSilent() {
|
||||
return this.isSilent;
|
||||
}
|
||||
|
||||
public void setSilent(boolean silent) {
|
||||
this.isSilent = silent;
|
||||
}
|
||||
|
||||
public int getQuietInterval() {
|
||||
return this.quietInterval;
|
||||
}
|
||||
|
||||
public void setQuietInterval(int quietInterval) {
|
||||
this.quietInterval = quietInterval;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,25 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.iterator.PatternType;
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Parameter;
|
||||
|
||||
public class Selection {
|
||||
@SuppressWarnings("unused")
|
||||
public final class Selection {
|
||||
public static final double DEFAULT_CENTER_X = 0d;
|
||||
public static final double DEFAULT_CENTER_Z = 0d;
|
||||
public static final double DEFAULT_RADIUS = 500d;
|
||||
private final Chunky chunky;
|
||||
private final World world;
|
||||
private final double centerX;
|
||||
private final double centerZ;
|
||||
private final double radiusX;
|
||||
private final double radiusZ;
|
||||
private final String pattern;
|
||||
private final Parameter pattern;
|
||||
private final String shape;
|
||||
private final int centerChunkX;
|
||||
private final int centerChunkZ;
|
||||
@@ -18,8 +27,15 @@ public class Selection {
|
||||
private final int radiusChunksZ;
|
||||
private final int diameterChunksX;
|
||||
private final int diameterChunksZ;
|
||||
private final int centerRegionX;
|
||||
private final int centerRegionZ;
|
||||
private final int radiusRegionsX;
|
||||
private final int radiusRegionsZ;
|
||||
private final int diameterRegionsX;
|
||||
private final int diameterRegionsZ;
|
||||
|
||||
private Selection(World world, double centerX, double centerZ, double radiusX, double radiusZ, String pattern, String shape) {
|
||||
private Selection(final Chunky chunky, final World world, final double centerX, final double centerZ, final double radiusX, final double radiusZ, final Parameter pattern, final String shape) {
|
||||
this.chunky = chunky;
|
||||
this.world = world;
|
||||
this.centerX = centerX;
|
||||
this.centerZ = centerZ;
|
||||
@@ -33,6 +49,20 @@ public class Selection {
|
||||
this.radiusChunksZ = (int) Math.ceil(radiusZ / 16f);
|
||||
this.diameterChunksX = 2 * radiusChunksX + 1;
|
||||
this.diameterChunksZ = 2 * radiusChunksZ + 1;
|
||||
this.centerRegionX = centerChunkX >> 5;
|
||||
this.centerRegionZ = centerChunkZ >> 5;
|
||||
this.radiusRegionsX = (int) Math.ceil(radiusChunksX / 32f);
|
||||
this.radiusRegionsZ = (int) Math.ceil(radiusChunksZ / 32f);
|
||||
this.diameterRegionsX = 2 * radiusRegionsX + 1;
|
||||
this.diameterRegionsZ = 2 * radiusRegionsZ + 1;
|
||||
}
|
||||
|
||||
public static Builder builder(final Chunky chunky, final World world) {
|
||||
return new Builder(chunky, world);
|
||||
}
|
||||
|
||||
public Chunky chunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public World world() {
|
||||
@@ -55,7 +85,7 @@ public class Selection {
|
||||
return this.radiusZ;
|
||||
}
|
||||
|
||||
public String pattern() {
|
||||
public Parameter pattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
@@ -87,80 +117,102 @@ public class Selection {
|
||||
return this.diameterChunksZ;
|
||||
}
|
||||
|
||||
public static Builder builder(World world) {
|
||||
return new Builder(world);
|
||||
public int centerRegionX() {
|
||||
return this.centerRegionX;
|
||||
}
|
||||
|
||||
public int centerRegionZ() {
|
||||
return this.centerRegionZ;
|
||||
}
|
||||
|
||||
public int radiusRegionsX() {
|
||||
return this.radiusRegionsX;
|
||||
}
|
||||
|
||||
public int radiusRegionsZ() {
|
||||
return this.radiusRegionsZ;
|
||||
}
|
||||
|
||||
public int diameterRegionsX() {
|
||||
return this.diameterRegionsX;
|
||||
}
|
||||
|
||||
public int diameterRegionsZ() {
|
||||
return this.diameterRegionsZ;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final Chunky chunky;
|
||||
private World world;
|
||||
private double centerX = 0;
|
||||
private double centerZ = 0;
|
||||
private double radiusX = 500;
|
||||
private double radiusZ = 500;
|
||||
private String pattern = "concentric";
|
||||
private String shape = "square";
|
||||
private double centerX = DEFAULT_CENTER_X;
|
||||
private double centerZ = DEFAULT_CENTER_Z;
|
||||
private double radiusX = DEFAULT_RADIUS;
|
||||
private double radiusZ = DEFAULT_RADIUS;
|
||||
private Parameter pattern = Parameter.of(PatternType.REGION);
|
||||
private String shape = ShapeType.SQUARE;
|
||||
|
||||
private Builder(World world) {
|
||||
private Builder(final Chunky chunky, final World world) {
|
||||
this.chunky = chunky;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public Builder world(World world) {
|
||||
public Builder world(final World world) {
|
||||
this.world = world;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder center(double centerX, double centerZ) {
|
||||
public Builder center(final double centerX, final double centerZ) {
|
||||
this.centerX = centerX;
|
||||
this.centerZ = centerZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder centerX(double centerX) {
|
||||
public Builder centerX(final double centerX) {
|
||||
this.centerX = centerX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder centerZ(double centerZ) {
|
||||
public Builder centerZ(final double centerZ) {
|
||||
this.centerZ = centerZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radius(double radius) {
|
||||
public Builder radius(final double radius) {
|
||||
this.radiusX = radius;
|
||||
this.radiusZ = radius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radiusX(double radiusX) {
|
||||
public Builder radiusX(final double radiusX) {
|
||||
this.radiusX = radiusX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radiusZ(double radiusZ) {
|
||||
public Builder radiusZ(final double radiusZ) {
|
||||
this.radiusZ = radiusZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pattern(String pattern) {
|
||||
public Builder pattern(final Parameter pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder shape(String shape) {
|
||||
public Builder shape(final String shape) {
|
||||
this.shape = shape;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder spawn() {
|
||||
Coordinate spawn = world.getSpawnCoordinate();
|
||||
final Location spawn = world.getSpawn();
|
||||
this.centerX = spawn.getX();
|
||||
this.centerZ = spawn.getZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder worldborder() {
|
||||
Border border = world.getWorldBorder();
|
||||
Coordinate center = border.getCenter();
|
||||
final Border border = world.getWorldBorder();
|
||||
final Vector2 center = border.getCenter();
|
||||
this.centerX = center.getX();
|
||||
this.centerZ = center.getZ();
|
||||
this.radiusX = border.getRadiusX();
|
||||
@@ -169,7 +221,7 @@ public class Selection {
|
||||
}
|
||||
|
||||
public Selection build() {
|
||||
return new Selection(world, centerX, centerZ, radiusX, radiusZ, pattern, shape);
|
||||
return new Selection(chunky, world, centerX, centerZ, radiusX, radiusZ, pattern, shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.popcraft.chunky.api;
|
||||
|
||||
import org.popcraft.chunky.api.event.task.GenerationCompleteEvent;
|
||||
import org.popcraft.chunky.api.event.task.GenerationProgressEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The Chunky API
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface ChunkyAPI {
|
||||
/**
|
||||
* Gets the current API version.
|
||||
*
|
||||
* @return The api version
|
||||
*/
|
||||
int version();
|
||||
|
||||
/**
|
||||
* Gets whether a generation task is currently running for a world.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @return If a task is running in that world
|
||||
*/
|
||||
boolean isRunning(final String world);
|
||||
|
||||
/**
|
||||
* Starts a generation task with a given selection in a world.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @param shape The selection shape
|
||||
* @param centerX The center x coordinate
|
||||
* @param centerZ The center z coordinate
|
||||
* @param radiusX The primary radius (x-axis)
|
||||
* @param radiusZ The secondary radius (z-axis) (only used for certain shapes)
|
||||
* @param pattern The generation pattern
|
||||
* @return If the task was created and started successfully
|
||||
*/
|
||||
boolean startTask(final String world, final String shape, final double centerX, final double centerZ, final double radiusX, final double radiusZ, final String pattern);
|
||||
|
||||
/**
|
||||
* Pauses a generation task in a world.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @return If the task was paused
|
||||
*/
|
||||
boolean pauseTask(final String world);
|
||||
|
||||
/**
|
||||
* Continues a generation task in a world.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @return If the task was continued
|
||||
*/
|
||||
boolean continueTask(final String world);
|
||||
|
||||
/**
|
||||
* Cancels a generation task in a world.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @return If the task was cancelled
|
||||
*/
|
||||
boolean cancelTask(final String world);
|
||||
|
||||
/**
|
||||
* Register a listener for generation progress events.
|
||||
*
|
||||
* @param listener The listener
|
||||
*/
|
||||
void onGenerationProgress(final Consumer<GenerationProgressEvent> listener);
|
||||
|
||||
/**
|
||||
* Register a listener for generation complete events.
|
||||
*
|
||||
* @param listener The listener
|
||||
*/
|
||||
void onGenerationComplete(final Consumer<GenerationCompleteEvent> listener);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.popcraft.chunky.api;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.api.event.task.GenerationCompleteEvent;
|
||||
import org.popcraft.chunky.api.event.task.GenerationProgressEvent;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Parameter;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
public class ChunkyAPIImpl implements ChunkyAPI {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ChunkyAPIImpl(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int version() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning(final String world) {
|
||||
return chunky.getGenerationTasks().containsKey(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startTask(final String world, final String shape, final double centerX, final double centerZ, final double radiusX, final double radiusZ, final String pattern) {
|
||||
final World implWorld = Input.tryWorld(chunky, world).orElse(null);
|
||||
if (implWorld == null) {
|
||||
return false;
|
||||
}
|
||||
if (chunky.getGenerationTasks().containsKey(world)) {
|
||||
return false;
|
||||
}
|
||||
final Selection selection = Selection.builder(chunky, implWorld)
|
||||
.shape(shape).center(centerX, centerZ)
|
||||
.radiusX(radiusX).radiusZ(radiusZ)
|
||||
.pattern(Parameter.of(pattern)).build();
|
||||
final GenerationTask task = new GenerationTask(chunky, selection);
|
||||
chunky.getGenerationTasks().put(world, task);
|
||||
chunky.getScheduler().runTask(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pauseTask(final String world) {
|
||||
final GenerationTask task = chunky.getGenerationTasks().get(world);
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
task.stop(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueTask(final String world) {
|
||||
final World implWorld = Input.tryWorld(chunky, world).orElse(null);
|
||||
if (implWorld == null) {
|
||||
return false;
|
||||
}
|
||||
final GenerationTask task = chunky.getTaskLoader().loadTask(implWorld).orElse(null);
|
||||
if (task == null || task.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
if (chunky.getGenerationTasks().containsKey(world)) {
|
||||
return false;
|
||||
}
|
||||
chunky.getGenerationTasks().put(world, task);
|
||||
chunky.getScheduler().runTask(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancelTask(final String world) {
|
||||
final World implWorld = Input.tryWorld(chunky, world).orElse(null);
|
||||
if (implWorld == null) {
|
||||
return false;
|
||||
}
|
||||
if (!chunky.getGenerationTasks().containsKey(world)) {
|
||||
return false;
|
||||
}
|
||||
chunky.getGenerationTasks().remove(world).stop(true);
|
||||
chunky.getTaskLoader().cancelTask(implWorld);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGenerationProgress(final Consumer<GenerationProgressEvent> event) {
|
||||
chunky.getEventBus().subscribe(GenerationProgressEvent.class, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGenerationComplete(final Consumer<GenerationCompleteEvent> event) {
|
||||
chunky.getEventBus().subscribe(GenerationCompleteEvent.class, event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.popcraft.chunky.api.event.task;
|
||||
|
||||
import org.popcraft.chunky.event.Event;
|
||||
|
||||
/**
|
||||
* Event which is fired when a generation task completes.
|
||||
*
|
||||
* @param world The world identifier associated with the completed task
|
||||
*/
|
||||
public record GenerationCompleteEvent(String world) implements Event {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.popcraft.chunky.api.event.task;
|
||||
|
||||
import org.popcraft.chunky.event.Event;
|
||||
|
||||
/**
|
||||
* Event which is fired when a generation task calculates progress.
|
||||
*
|
||||
* @param world The world identifier
|
||||
* @param chunks The number of chunks generated
|
||||
* @param complete If the generation task completed
|
||||
* @param progress The percent progress
|
||||
* @param hours The number of hours elapsed for this task
|
||||
* @param minutes The number of minutes elapsed for this task
|
||||
* @param seconds The number of seconds elapsed for this task
|
||||
* @param rate The current average generation rate
|
||||
* @param x The current chunk's x coordinate
|
||||
* @param z The current chunk's z coordinate
|
||||
*/
|
||||
public record GenerationProgressEvent(String world, long chunks, boolean complete, float progress, long hours,
|
||||
long minutes, long seconds, double rate, long x, long z) implements Event {
|
||||
}
|
||||
@@ -5,59 +5,69 @@ import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CancelCommand extends ChunkyCommand {
|
||||
public CancelCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class CancelCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CancelCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final Map<World, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
if (generationTasks.isEmpty() && chunky.getConfig().loadTasks().isEmpty()) {
|
||||
sender.sendMessagePrefixed("format_cancel_no_tasks");
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
final Map<String, TrimCommand.Task> trimTasks = chunky.getTrimTasks();
|
||||
if (generationTasks.isEmpty()
|
||||
&& chunky.getTaskLoader().loadTasks().stream().allMatch(GenerationTask::isCancelled)
|
||||
&& trimTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
final Runnable cancelAction;
|
||||
if (args.length > 1) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, String.join(" ", Arrays.copyOfRange(args, 1, args.length)));
|
||||
if (!world.isPresent()) {
|
||||
sender.sendMessage("help_cancel");
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, arguments.joined());
|
||||
if (world.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_CANCEL);
|
||||
return;
|
||||
}
|
||||
cancelAction = () -> {
|
||||
sender.sendMessagePrefixed("format_cancel", world.get().getName());
|
||||
chunky.getConfig().cancelTask(world.get());
|
||||
if (chunky.getGenerationTasks().containsKey(world.get())) {
|
||||
chunky.getGenerationTasks().remove(world.get()).stop(true);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL, world.get().getName());
|
||||
chunky.getTaskLoader().cancelTask(world.get());
|
||||
if (chunky.getGenerationTasks().containsKey(world.get().getName())) {
|
||||
chunky.getGenerationTasks().remove(world.get().getName()).stop(true);
|
||||
}
|
||||
if (chunky.getTrimTasks().containsKey(world.get().getName())) {
|
||||
chunky.getTrimTasks().remove(world.get().getName()).setCancelled(true);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
cancelAction = () -> {
|
||||
sender.sendMessagePrefixed("format_cancel_all");
|
||||
chunky.getConfig().cancelTasks();
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL_ALL);
|
||||
chunky.getTaskLoader().cancelTasks();
|
||||
chunky.getGenerationTasks().values().forEach(generationTask -> generationTask.stop(true));
|
||||
chunky.getGenerationTasks().clear();
|
||||
chunky.getScheduler().cancelTasks();
|
||||
chunky.getTrimTasks().values().forEach(trimTask -> trimTask.setCancelled(true));
|
||||
chunky.getTrimTasks().clear();
|
||||
};
|
||||
}
|
||||
chunky.setPendingAction(sender, cancelAction);
|
||||
sender.sendMessagePrefixed("format_cancel_confirm", "/chunky confirm");
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL_CONFIRM, "/chunky confirm");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -2,44 +2,48 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CenterCommand extends ChunkyCommand {
|
||||
public CenterCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class CenterCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CenterCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Optional<Double> newX = Optional.empty();
|
||||
if (args.length > 1) {
|
||||
newX = Input.tryDoubleSuffixed(args[1]);
|
||||
}
|
||||
Optional<Double> newZ = Optional.empty();
|
||||
if (args.length > 2) {
|
||||
newZ = Input.tryDoubleSuffixed(args[2]);
|
||||
}
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<Double> newX = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
final Optional<Double> newZ = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
final double centerX;
|
||||
final double centerZ;
|
||||
if (!newX.isPresent() && !newZ.isPresent()) {
|
||||
Coordinate coordinate = sender.getCoordinate();
|
||||
if (newX.isEmpty() && newZ.isEmpty()) {
|
||||
final Location coordinate = sender.getLocation();
|
||||
centerX = coordinate.getX();
|
||||
centerZ = coordinate.getZ();
|
||||
} else if (newX.isPresent() && newZ.isPresent()) {
|
||||
centerX = newX.get();
|
||||
centerZ = newZ.get();
|
||||
} else {
|
||||
sender.sendMessage("help_center");
|
||||
sender.sendMessage(TranslationKey.HELP_CENTER);
|
||||
return;
|
||||
}
|
||||
if (Input.isPastWorldLimit(centerX) || Input.isPastWorldLimit(centerZ)) {
|
||||
sender.sendMessage("help_center");
|
||||
sender.sendMessage(TranslationKey.HELP_CENTER);
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().center(centerX, centerZ);
|
||||
sender.sendMessagePrefixed("format_center", Formatting.number(centerX), Formatting.number(centerZ));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(centerX), Formatting.number(centerZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,11 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ChunkyCommand {
|
||||
protected final Chunky chunky;
|
||||
public interface ChunkyCommand {
|
||||
void execute(Sender sender, CommandArguments arguments);
|
||||
|
||||
protected ChunkyCommand(Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public abstract void execute(Sender sender, String[] args);
|
||||
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> suggestions(final CommandArguments arguments);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
|
||||
public final class CommandArguments {
|
||||
private final int size;
|
||||
private final Queue<String> args = new LinkedList<>();
|
||||
|
||||
private CommandArguments(final List<String> arguments) {
|
||||
this.size = arguments.size();
|
||||
this.args.addAll(arguments);
|
||||
}
|
||||
|
||||
public static CommandArguments of(final List<String> arguments) {
|
||||
return new CommandArguments(arguments);
|
||||
}
|
||||
|
||||
public static CommandArguments of(final String... arguments) {
|
||||
return new CommandArguments(List.of(arguments));
|
||||
}
|
||||
|
||||
public static CommandArguments empty() {
|
||||
return new CommandArguments(List.of());
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Optional<String> next() {
|
||||
return Optional.ofNullable(args.poll());
|
||||
}
|
||||
|
||||
public List<String> remaining() {
|
||||
final List<String> arguments = new ArrayList<>(args);
|
||||
args.clear();
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public String joined() {
|
||||
return String.join(" ", remaining());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
public final class CommandLiteral {
|
||||
public static final String BORDER = "border";
|
||||
public static final String CANCEL = "cancel";
|
||||
public static final String CENTER = "center";
|
||||
public static final String CENTER_X = "centerX";
|
||||
public static final String CENTER_Z = "centerZ";
|
||||
public static final String CHUNKY = "chunky";
|
||||
public static final String CONFIRM = "confirm";
|
||||
public static final String CONTINUE = "continue";
|
||||
public static final String CORNERS = "corners";
|
||||
public static final String HELP = "help";
|
||||
public static final String INHABITED = "inhabited";
|
||||
public static final String INTERVAL = "interval";
|
||||
public static final String PAGE = "page";
|
||||
public static final String PATTERN = "pattern";
|
||||
public static final String PAUSE = "pause";
|
||||
public static final String PROGRESS = "progress";
|
||||
public static final String QUIET = "quiet";
|
||||
public static final String RADIUS = "radius";
|
||||
public static final String RADIUS_X = "radiusX";
|
||||
public static final String RADIUS_Z = "radiusZ";
|
||||
public static final String RELOAD = "reload";
|
||||
public static final String SELECTION = "selection";
|
||||
public static final String SHAPE = "shape";
|
||||
public static final String SILENT = "silent";
|
||||
public static final String SPAWN = "spawn";
|
||||
public static final String START = "start";
|
||||
public static final String TRIM = "trim";
|
||||
public static final String TRIM_MODE = "trimMode";
|
||||
public static final String WORLDBORDER = "worldborder";
|
||||
public static final String WORLD = "world";
|
||||
public static final String X = "x";
|
||||
public static final String X1 = "x1";
|
||||
public static final String X2 = "x2";
|
||||
public static final String Z = "z";
|
||||
public static final String Z1 = "z1";
|
||||
public static final String Z2 = "z2";
|
||||
public static final String ADD = "add";
|
||||
public static final String BYPASS = "bypass";
|
||||
public static final String LIST = "list";
|
||||
public static final String LOAD = "load";
|
||||
public static final String REMOVE = "remove";
|
||||
public static final String WRAP = "wrap";
|
||||
public static final String PLAYER = "player";
|
||||
public static final String VALUE = "value";
|
||||
public static final String TYPE = "type";
|
||||
|
||||
private CommandLiteral() {
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,30 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfirmCommand extends ChunkyCommand {
|
||||
public ConfirmCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class ConfirmCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ConfirmCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Optional<Runnable> pendingAction = chunky.getPendingAction(sender);
|
||||
if (!pendingAction.isPresent()) {
|
||||
sender.sendMessagePrefixed("format_confirm");
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<Runnable> pendingAction = chunky.getPendingAction(sender);
|
||||
if (pendingAction.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CONFIRM);
|
||||
return;
|
||||
}
|
||||
pendingAction.get().run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -5,55 +5,57 @@ import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ContinueCommand extends ChunkyCommand {
|
||||
public ContinueCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class ContinueCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ContinueCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final List<GenerationTask> loadTasks;
|
||||
if (args.length > 1) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, String.join(" ", Arrays.copyOfRange(args, 1, args.length)));
|
||||
if (!world.isPresent()) {
|
||||
sender.sendMessage("help_continue");
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, arguments.joined());
|
||||
if (world.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_CONTINUE);
|
||||
return;
|
||||
}
|
||||
loadTasks = chunky.getConfig().loadTask(world.get()).map(Collections::singletonList).orElse(Collections.emptyList());
|
||||
loadTasks = chunky.getTaskLoader().loadTask(world.get()).map(List::of).orElse(List.of());
|
||||
} else {
|
||||
loadTasks = chunky.getConfig().loadTasks();
|
||||
loadTasks = chunky.getTaskLoader().loadTasks();
|
||||
}
|
||||
if (loadTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed("format_continue_no_tasks");
|
||||
if (loadTasks.stream().allMatch(GenerationTask::isCancelled)) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CONTINUE_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
final Map<World, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
loadTasks.forEach(generationTask -> {
|
||||
World world = generationTask.getSelection().world();
|
||||
if (!generationTasks.containsKey(world)) {
|
||||
generationTasks.put(world, generationTask);
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
loadTasks.stream().filter(task -> !task.isCancelled()).forEach(generationTask -> {
|
||||
final World world = generationTask.getSelection().world();
|
||||
if (!generationTasks.containsKey(world.getName())) {
|
||||
generationTasks.put(world.getName(), generationTask);
|
||||
chunky.getScheduler().runTask(generationTask);
|
||||
sender.sendMessagePrefixed("format_continue", world.getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CONTINUE, world.getName());
|
||||
} else {
|
||||
sender.sendMessagePrefixed("format_started_already", world.getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_STARTED_ALREADY, world.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -2,49 +2,55 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CornersCommand extends ChunkyCommand {
|
||||
public CornersCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class CornersCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CornersCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 5) {
|
||||
sender.sendMessage("help_corners");
|
||||
return;
|
||||
}
|
||||
Optional<Double> x1 = Input.tryDoubleSuffixed(args[1]);
|
||||
Optional<Double> z1 = Input.tryDoubleSuffixed(args[2]);
|
||||
Optional<Double> x2 = Input.tryDoubleSuffixed(args[3]);
|
||||
Optional<Double> z2 = Input.tryDoubleSuffixed(args[4]);
|
||||
if (!x1.isPresent() || !z1.isPresent() || !x2.isPresent() || !z2.isPresent()) {
|
||||
sender.sendMessage("help_corners");
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<Double> x1 = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
final Optional<Double> z1 = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
final Optional<Double> x2 = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
final Optional<Double> z2 = arguments.next().flatMap(Input::tryDoubleSuffixed);
|
||||
if (x1.isEmpty() || z1.isEmpty() || x2.isEmpty() || z2.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_CORNERS);
|
||||
return;
|
||||
}
|
||||
if (Input.isPastWorldLimit(x1.get()) || Input.isPastWorldLimit(z1.get()) || Input.isPastWorldLimit(x2.get()) || Input.isPastWorldLimit(z2.get())) {
|
||||
sender.sendMessage("help_corners");
|
||||
sender.sendMessage(TranslationKey.HELP_CORNERS);
|
||||
return;
|
||||
}
|
||||
double centerX = (x1.get() + x2.get()) / 2d;
|
||||
double centerZ = (z1.get() + z2.get()) / 2d;
|
||||
double radiusX = Math.abs(x1.get() - x2.get()) / 2d;
|
||||
double radiusZ = Math.abs(z1.get() - z2.get()) / 2d;
|
||||
final double centerX = (x1.get() + x2.get()) / 2d;
|
||||
final double centerZ = (z1.get() + z2.get()) / 2d;
|
||||
final double radiusX = Math.abs(x1.get() - x2.get()) / 2d;
|
||||
final double radiusZ = Math.abs(z1.get() - z2.get()) / 2d;
|
||||
chunky.getSelection().center(centerX, centerZ).radiusX(radiusX).radiusZ(radiusZ);
|
||||
sender.sendMessagePrefixed("format_center", Formatting.number(centerX), Formatting.number(centerZ));
|
||||
String shape;
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(centerX), Formatting.number(centerZ));
|
||||
final String shape;
|
||||
if (radiusX == radiusZ) {
|
||||
sender.sendMessagePrefixed("format_radius", Formatting.number(radiusX));
|
||||
shape = "square";
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX));
|
||||
shape = ShapeType.SQUARE;
|
||||
} else {
|
||||
sender.sendMessagePrefixed("format_radii", Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
shape = "rectangle";
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
shape = ShapeType.RECTANGLE;
|
||||
}
|
||||
chunky.getSelection().shape(shape);
|
||||
sender.sendMessagePrefixed("format_shape", shape);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -3,52 +3,73 @@ package org.popcraft.chunky.command;
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class HelpCommand extends ChunkyCommand {
|
||||
private final List<String> helpMessages;
|
||||
public class HelpCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
private final List<String> helpCommands = List.of(
|
||||
CommandLiteral.START,
|
||||
CommandLiteral.PAUSE,
|
||||
CommandLiteral.CONTINUE,
|
||||
CommandLiteral.CANCEL,
|
||||
CommandLiteral.WORLD,
|
||||
CommandLiteral.WORLDBORDER,
|
||||
CommandLiteral.CENTER,
|
||||
CommandLiteral.SPAWN,
|
||||
CommandLiteral.RADIUS,
|
||||
CommandLiteral.CORNERS,
|
||||
CommandLiteral.SHAPE,
|
||||
CommandLiteral.PATTERN,
|
||||
CommandLiteral.SILENT,
|
||||
CommandLiteral.QUIET,
|
||||
CommandLiteral.TRIM,
|
||||
CommandLiteral.SELECTION,
|
||||
CommandLiteral.PROGRESS,
|
||||
CommandLiteral.BORDER,
|
||||
CommandLiteral.RELOAD
|
||||
);
|
||||
|
||||
public HelpCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
this.helpMessages = new ArrayList<>();
|
||||
helpMessages.add("help_start");
|
||||
helpMessages.add("help_pause");
|
||||
helpMessages.add("help_continue");
|
||||
helpMessages.add("help_cancel");
|
||||
helpMessages.add("help_world");
|
||||
helpMessages.add("help_worldborder");
|
||||
helpMessages.add("help_center");
|
||||
helpMessages.add("help_spawn");
|
||||
helpMessages.add("help_radius");
|
||||
helpMessages.add("help_corners");
|
||||
helpMessages.add("help_shape");
|
||||
helpMessages.add("help_pattern");
|
||||
helpMessages.add("help_silent");
|
||||
helpMessages.add("help_quiet");
|
||||
helpMessages.add("help_reload");
|
||||
public HelpCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final StringBuilder help = new StringBuilder();
|
||||
if (sender.isPlayer()) {
|
||||
int pageIndexLast = helpMessages.size() / 8;
|
||||
int pageIndex = (args.length < 2 ? 0 : Math.max(0, Input.tryInteger(args[1]).orElse(1) - 1)) % (pageIndexLast + 1);
|
||||
int helpIndexFirst = 8 * pageIndex;
|
||||
int helpIndexLast = Math.min(helpIndexFirst + 8, helpMessages.size());
|
||||
for (int i = helpIndexFirst; i < helpIndexLast; ++i) {
|
||||
help.append('\n').append(translate(helpMessages.get(i)));
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final List<String> visibleCommands = new ArrayList<>();
|
||||
for (final String command : helpCommands) {
|
||||
if (chunky.getCommands().containsKey(command)) {
|
||||
visibleCommands.add(command);
|
||||
}
|
||||
if (pageIndex != pageIndexLast) {
|
||||
help.append('\n').append(translate("help_more", "/chunky help " + (pageIndex + 2)));
|
||||
}
|
||||
} else {
|
||||
helpMessages.forEach(message -> help.append('\n').append(translate(message)));
|
||||
}
|
||||
sender.sendMessage("help_menu", help.toString());
|
||||
final int visibleCommandCount = visibleCommands.size();
|
||||
final StringBuilder help = new StringBuilder();
|
||||
final int pageIndexLast = visibleCommandCount / 8;
|
||||
final int pageIndex = (arguments.size() < 1 ? 0 : Math.max(0, arguments.next().flatMap(Input::tryInteger).orElse(1) - 1)) % (pageIndexLast + 1);
|
||||
final int helpIndexFirst;
|
||||
final int helpIndexLast;
|
||||
if (sender.isPlayer()) {
|
||||
helpIndexFirst = 8 * pageIndex;
|
||||
helpIndexLast = Math.min(helpIndexFirst + 8, visibleCommandCount);
|
||||
} else {
|
||||
helpIndexFirst = 0;
|
||||
helpIndexLast = visibleCommandCount;
|
||||
}
|
||||
for (int i = helpIndexFirst; i < helpIndexLast; ++i) {
|
||||
help.append('\n').append(translate("help_" + visibleCommands.get(i)));
|
||||
}
|
||||
if (sender.isPlayer() && pageIndex != pageIndexLast) {
|
||||
help.append('\n').append(translate(TranslationKey.HELP_MORE, "/chunky help " + (pageIndex + 2)));
|
||||
}
|
||||
sender.sendMessage(TranslationKey.HELP_MENU, help.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,47 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.iterator.PatternType;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Parameter;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PatternCommand extends ChunkyCommand {
|
||||
private static final List<String> PATTERNS = Arrays.asList("concentric", "loop", "spiral");
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public PatternCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
}
|
||||
public class PatternCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("help_pattern");
|
||||
return;
|
||||
}
|
||||
String pattern = args[1].toLowerCase();
|
||||
if (!PATTERNS.contains(pattern)) {
|
||||
sender.sendMessage("help_pattern");
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().pattern(pattern);
|
||||
sender.sendMessagePrefixed("format_pattern", pattern);
|
||||
public PatternCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
return PATTERNS;
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<String> optionalType = arguments.next().flatMap(Input::tryPattern);
|
||||
if (optionalType.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_PATTERN);
|
||||
return;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
final String type = optionalType.get();
|
||||
final Optional<String> value = arguments.next();
|
||||
if (PatternType.CSV.equals(type) && value.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_PATTERN);
|
||||
return;
|
||||
}
|
||||
final Parameter pattern = Parameter.of(type, value.orElse(null));
|
||||
chunky.getSelection().pattern(pattern);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PATTERN, translate("pattern_" + pattern.getType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
return PatternType.ALL;
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -5,48 +5,50 @@ import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PauseCommand extends ChunkyCommand {
|
||||
public PauseCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class PauseCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public PauseCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final Map<World, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
if (generationTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed("format_pause_no_tasks");
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
if (args.length > 1) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, String.join(" ", Arrays.copyOfRange(args, 1, args.length)));
|
||||
if (!world.isPresent() || !generationTasks.containsKey(world.get())) {
|
||||
sender.sendMessage("help_pause");
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, arguments.joined());
|
||||
if (world.isEmpty() || !generationTasks.containsKey(world.get().getName())) {
|
||||
sender.sendMessage(TranslationKey.HELP_PAUSE);
|
||||
} else {
|
||||
generationTasks.get(world.get()).stop(false);
|
||||
sender.sendMessagePrefixed("format_pause", world.get().getName());
|
||||
generationTasks.get(world.get().getName()).stop(false);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, world.get().getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (GenerationTask generationTask : chunky.getGenerationTasks().values()) {
|
||||
generationTask.stop(false);
|
||||
sender.sendMessagePrefixed("format_pause", generationTask.getSelection().world().getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, generationTask.getSelection().world().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -4,25 +4,34 @@ import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProgressCommand extends ChunkyCommand {
|
||||
public ProgressCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class ProgressCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ProgressCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final Map<World, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
if (generationTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed("format_progress_no_tasks");
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PROGRESS_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
for (World world : chunky.getServer().getWorlds()) {
|
||||
if (generationTasks.containsKey(world)) {
|
||||
generationTasks.get(world).getProgress().sendUpdate(sender);
|
||||
if (generationTasks.containsKey(world.getName())) {
|
||||
generationTasks.get(world.getName()).getProgress().sendUpdate(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,32 @@ package org.popcraft.chunky.command;
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class QuietCommand extends ChunkyCommand {
|
||||
public QuietCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class QuietCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public QuietCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Optional<Integer> newQuiet = Optional.empty();
|
||||
if (args.length > 1) {
|
||||
newQuiet = Input.tryInteger(args[1]);
|
||||
}
|
||||
if (!newQuiet.isPresent()) {
|
||||
sender.sendMessage("help_quiet");
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<Integer> newQuiet = arguments.next().flatMap(Input::tryInteger);
|
||||
if (newQuiet.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_QUIET);
|
||||
return;
|
||||
}
|
||||
int quietInterval = Math.max(0, newQuiet.get());
|
||||
chunky.getOptions().setQuietInterval(quietInterval);
|
||||
sender.sendMessagePrefixed("format_quiet", quietInterval);
|
||||
final int quietInterval = Math.max(0, newQuiet.get());
|
||||
chunky.getConfig().setUpdateInterval(quietInterval);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_QUIET, quietInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -5,48 +5,56 @@ import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RadiusCommand extends ChunkyCommand {
|
||||
public RadiusCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class RadiusCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public RadiusCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("help_radius");
|
||||
return;
|
||||
}
|
||||
final Optional<Integer> signX = Input.trySign(args[1]);
|
||||
final Optional<Double> newRadiusX = Input.tryDoubleSuffixed(signX.isPresent() ? args[1].substring(1) : args[1]);
|
||||
if (!newRadiusX.isPresent() || newRadiusX.get() < 0 || Input.isPastWorldLimit(newRadiusX.get())) {
|
||||
sender.sendMessage("help_radius");
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<String> newX = arguments.next();
|
||||
final Optional<Integer> signX = newX.flatMap(Input::trySign);
|
||||
final Optional<Double> newRadiusX = newX.map(x -> signX.isPresent() ? x.substring(1) : x).flatMap(Input::tryDoubleSuffixed);
|
||||
if (newRadiusX.isEmpty() || newRadiusX.get() < 0 || Input.isPastWorldLimit(newRadiusX.get())) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
final Selection current = chunky.getSelection().build();
|
||||
double radiusX = signX.map(sign -> current.radiusX() + sign * newRadiusX.get()).orElseGet(newRadiusX::get);
|
||||
final double radiusX = signX.map(sign -> current.radiusX() + sign * newRadiusX.get()).orElseGet(newRadiusX::get);
|
||||
if (radiusX < 0 || Input.isPastWorldLimit(radiusX)) {
|
||||
sender.sendMessage("help_radius");
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
if (args.length > 2) {
|
||||
Optional<Integer> signZ = Input.trySign(args[2]);
|
||||
Optional<Double> newRadiusZ = Input.tryDoubleSuffixed(signZ.isPresent() ? args[2].substring(1) : args[2]);
|
||||
if (!newRadiusZ.isPresent() || newRadiusZ.get() < 0 || Input.isPastWorldLimit(newRadiusZ.get())) {
|
||||
sender.sendMessage("help_radius");
|
||||
final Optional<String> newZ = arguments.next();
|
||||
if (newZ.isPresent()) {
|
||||
final Optional<Integer> signZ = newZ.flatMap(Input::trySign);
|
||||
final Optional<Double> newRadiusZ = newZ.map(z -> signZ.isPresent() ? z.substring(1) : z).flatMap(Input::tryDoubleSuffixed);
|
||||
if (newRadiusZ.isEmpty() || newRadiusZ.get() < 0 || Input.isPastWorldLimit(newRadiusZ.get())) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
final double radiusZ = signZ.map(sign -> current.radiusZ() + sign * newRadiusZ.get()).orElseGet(newRadiusZ::get);
|
||||
if (radiusZ < 0 || Input.isPastWorldLimit(radiusZ)) {
|
||||
sender.sendMessage("help_radius");
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().radiusX(radiusX).radiusZ(radiusZ);
|
||||
sender.sendMessagePrefixed("format_radii", Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
} else {
|
||||
chunky.getSelection().radius(radiusX);
|
||||
sender.sendMessagePrefixed("format_radius", Formatting.number(radiusX));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,43 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.event.command.ReloadCommandEvent;
|
||||
import org.popcraft.chunky.platform.Config;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
public class ReloadCommand extends ChunkyCommand {
|
||||
public ReloadCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
import java.util.List;
|
||||
|
||||
public class ReloadCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ReloadCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (!chunky.getGenerationTasks().isEmpty()) {
|
||||
sender.sendMessagePrefixed("format_reload_tasks_running");
|
||||
return;
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final String type = arguments.next().orElse(null);
|
||||
if ("tasks".equals(type)) {
|
||||
if (!chunky.getGenerationTasks().isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RELOAD_TASKS_RUNNING);
|
||||
return;
|
||||
}
|
||||
chunky.getTaskLoader().reload();
|
||||
} else {
|
||||
final Config config = chunky.getServer().getConfig();
|
||||
config.reload();
|
||||
chunky.setLanguage(config.getLanguage());
|
||||
chunky.getEventBus().call(new ReloadCommandEvent());
|
||||
}
|
||||
Config config = chunky.getServer().getConfig();
|
||||
config.reload();
|
||||
chunky.setLanguage(config.getLanguage());
|
||||
sender.sendMessagePrefixed("format_reload");
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RELOAD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
return List.of("config", "tasks");
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class SelectionCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SelectionCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Selection current = chunky.getSelection().build();
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SELECTION);
|
||||
sender.sendMessage(TranslationKey.FORMAT_SELECTION_WORLD, current.world().getName());
|
||||
sender.sendMessage(TranslationKey.FORMAT_SELECTION_SHAPE, translate("shape_" + current.shape()));
|
||||
sender.sendMessage(TranslationKey.FORMAT_SELECTION_CENTER, Formatting.number(current.centerX()), Formatting.number(current.centerZ()));
|
||||
final double radiusX = current.radiusX();
|
||||
final double radiusZ = current.radiusZ();
|
||||
if (radiusX == radiusZ) {
|
||||
sender.sendMessage(TranslationKey.FORMAT_SELECTION_RADIUS, Formatting.number(radiusX));
|
||||
} else {
|
||||
sender.sendMessage(TranslationKey.FORMAT_SELECTION_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,39 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class ShapeCommand extends ChunkyCommand {
|
||||
public ShapeCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
}
|
||||
public class ShapeCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("help_shape");
|
||||
return;
|
||||
}
|
||||
Optional<String> inputShape = Input.tryShape(args[1]);
|
||||
if (!inputShape.isPresent()) {
|
||||
sender.sendMessage("help_shape");
|
||||
return;
|
||||
}
|
||||
String shape = inputShape.get();
|
||||
chunky.getSelection().shape(shape);
|
||||
sender.sendMessagePrefixed("format_shape", translate("shape_" + shape));
|
||||
public ShapeCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
return Input.SHAPES;
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<String> inputShape = arguments.next().flatMap(Input::tryShape);
|
||||
if (inputShape.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_SHAPE);
|
||||
return;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
final String shape = inputShape.get();
|
||||
chunky.getSelection().shape(shape);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, translate("shape_" + shape));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
return ShapeType.all();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,28 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class SilentCommand extends ChunkyCommand {
|
||||
public SilentCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class SilentCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SilentCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
chunky.getOptions().setSilent(!chunky.getOptions().isSilent());
|
||||
String status = translate(chunky.getOptions().isSilent() ? "enabled" : "disabled");
|
||||
sender.sendMessagePrefixed("format_silent", status);
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
chunky.getConfig().setSilent(!chunky.getConfig().isSilent());
|
||||
final String status = translate(chunky.getConfig().isSilent() ? TranslationKey.ENABLED : TranslationKey.DISABLED);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SILENT, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,26 @@ import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
public class SpawnCommand extends ChunkyCommand {
|
||||
public SpawnCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SpawnCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
chunky.getSelection().spawn();
|
||||
Selection current = chunky.getSelection().build();
|
||||
sender.sendMessagePrefixed("format_center", Formatting.number(current.centerX()), Formatting.number(current.centerZ()));
|
||||
final Selection current = chunky.getSelection().build();
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(current.centerX()), Formatting.number(current.centerZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -5,100 +5,104 @@ import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class StartCommand extends ChunkyCommand {
|
||||
public StartCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class StartCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public StartCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length > 1) {
|
||||
Optional<World> world = Input.tryWorld(chunky, args[1]);
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = arguments.next().flatMap(arg -> Input.tryWorld(chunky, arg));
|
||||
if (world.isPresent()) {
|
||||
chunky.getSelection().world(world.get());
|
||||
} else {
|
||||
sender.sendMessage("help_start");
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 2) {
|
||||
Optional<String> shape = Input.tryShape(args[2]);
|
||||
if (arguments.size() > 1) {
|
||||
final Optional<String> shape = arguments.next().flatMap(Input::tryShape);
|
||||
if (shape.isPresent()) {
|
||||
chunky.getSelection().shape(shape.get());
|
||||
} else {
|
||||
sender.sendMessage("help_start");
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 3) {
|
||||
Optional<Double> centerX = Input.tryDoubleSuffixed(args[3]).filter(cx -> !Input.isPastWorldLimit(cx));
|
||||
Optional<Double> centerZ = Input.tryDoubleSuffixed(args.length > 4 ? args[4] : null).filter(cz -> !Input.isPastWorldLimit(cz));
|
||||
if (arguments.size() > 2) {
|
||||
final Optional<Double> centerX = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(c -> !Input.isPastWorldLimit(c));
|
||||
final Optional<Double> centerZ = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(c -> !Input.isPastWorldLimit(c));
|
||||
if (centerX.isPresent() && centerZ.isPresent()) {
|
||||
chunky.getSelection().center(centerX.get(), centerZ.get());
|
||||
} else {
|
||||
sender.sendMessage("help_start");
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 5) {
|
||||
Optional<Double> radiusX = Input.tryDoubleSuffixed(args[5]).filter(rx -> rx >= 0 && !Input.isPastWorldLimit(rx));
|
||||
if (arguments.size() > 4) {
|
||||
final Optional<Double> radiusX = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(r -> r >= 0 && !Input.isPastWorldLimit(r));
|
||||
if (radiusX.isPresent()) {
|
||||
chunky.getSelection().radius(radiusX.get());
|
||||
} else {
|
||||
sender.sendMessage("help_start");
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 6) {
|
||||
Optional<Double> radiusZ = Input.tryDoubleSuffixed(args[6]).filter(rz -> rz >= 0 && !Input.isPastWorldLimit(rz));
|
||||
if (arguments.size() > 5) {
|
||||
final Optional<Double> radiusZ = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(r -> r >= 0 && !Input.isPastWorldLimit(r));
|
||||
if (radiusZ.isPresent()) {
|
||||
chunky.getSelection().radiusZ(radiusZ.get());
|
||||
} else {
|
||||
sender.sendMessage("help_start");
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
final Selection current = chunky.getSelection().build();
|
||||
if (chunky.getGenerationTasks().containsKey(current.world())) {
|
||||
sender.sendMessagePrefixed("format_started_already", current.world().getName());
|
||||
if (chunky.getGenerationTasks().containsKey(current.world().getName())) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_STARTED_ALREADY, current.world().getName());
|
||||
return;
|
||||
}
|
||||
if (current.radiusX() > chunky.getLimit()) {
|
||||
sender.sendMessagePrefixed("format_start_limit", Formatting.number(chunky.getLimit()));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START_LIMIT, Formatting.number(chunky.getLimit()));
|
||||
return;
|
||||
}
|
||||
final Runnable startAction = () -> {
|
||||
GenerationTask generationTask = new GenerationTask(chunky, current);
|
||||
chunky.getGenerationTasks().put(current.world(), generationTask);
|
||||
final GenerationTask generationTask = new GenerationTask(chunky, current);
|
||||
chunky.getGenerationTasks().put(current.world().getName(), generationTask);
|
||||
chunky.getScheduler().runTask(generationTask);
|
||||
sender.sendMessagePrefixed("format_start", current.world().getName(), translate("shape_" + current.shape()), Formatting.number(current.centerX()), Formatting.number(current.centerZ()), Formatting.radius(current));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START, current.world().getName(), translate("shape_" + current.shape()), Formatting.number(current.centerX()), Formatting.number(current.centerZ()), Formatting.radius(current));
|
||||
};
|
||||
if (chunky.getConfig().loadTask(current.world()).isPresent()) {
|
||||
if (chunky.getTaskLoader().loadTask(current.world()).filter(task -> !task.isCancelled()).isPresent()) {
|
||||
chunky.setPendingAction(sender, startAction);
|
||||
sender.sendMessagePrefixed("format_start_confirm", "/chunky continue", "/chunky confirm");
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START_CONFIRM, "/chunky continue", "/chunky confirm");
|
||||
} else {
|
||||
startAction.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
} else if (args.length == 3) {
|
||||
return Input.SHAPES;
|
||||
} else if (arguments.size() == 2) {
|
||||
return ShapeType.all();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -2,147 +2,171 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.nbt.LongTag;
|
||||
import org.popcraft.chunky.nbt.Tag;
|
||||
import org.popcraft.chunky.nbt.TagType;
|
||||
import org.popcraft.chunky.nbt.util.ChunkFilter;
|
||||
import org.popcraft.chunky.nbt.util.RegionFile;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
import org.popcraft.chunky.shape.ShapeFactory;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class TrimCommand extends ChunkyCommand {
|
||||
public TrimCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class TrimCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public TrimCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length > 1) {
|
||||
Optional<World> world = Input.tryWorld(chunky, args[1]);
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = arguments.next().flatMap(arg -> Input.tryWorld(chunky, arg));
|
||||
if (world.isPresent()) {
|
||||
chunky.getSelection().world(world.get());
|
||||
} else {
|
||||
sender.sendMessage("help_trim");
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 2) {
|
||||
Optional<String> shape = Input.tryShape(args[2]);
|
||||
if (arguments.size() > 1) {
|
||||
final Optional<String> shape = arguments.next().flatMap(Input::tryShape);
|
||||
if (shape.isPresent()) {
|
||||
chunky.getSelection().shape(shape.get());
|
||||
} else {
|
||||
sender.sendMessage("help_trim");
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 3) {
|
||||
Optional<Double> centerX = Input.tryDoubleSuffixed(args[3]).filter(cx -> !Input.isPastWorldLimit(cx));
|
||||
Optional<Double> centerZ = Input.tryDoubleSuffixed(args.length > 4 ? args[4] : null).filter(cz -> !Input.isPastWorldLimit(cz));
|
||||
if (arguments.size() > 2) {
|
||||
final Optional<Double> centerX = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(c -> !Input.isPastWorldLimit(c));
|
||||
final Optional<Double> centerZ = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(c -> !Input.isPastWorldLimit(c));
|
||||
if (centerX.isPresent() && centerZ.isPresent()) {
|
||||
chunky.getSelection().center(centerX.get(), centerZ.get());
|
||||
} else {
|
||||
sender.sendMessage("help_trim");
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 5) {
|
||||
Optional<Double> radiusX = Input.tryDoubleSuffixed(args[5]).filter(rx -> rx >= 0 && !Input.isPastWorldLimit(rx));
|
||||
if (arguments.size() > 4) {
|
||||
final Optional<Double> radiusX = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(r -> r >= 0 && !Input.isPastWorldLimit(r));
|
||||
if (radiusX.isPresent()) {
|
||||
chunky.getSelection().radius(radiusX.get());
|
||||
} else {
|
||||
sender.sendMessage("help_trim");
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (args.length > 6) {
|
||||
Optional<Double> radiusZ = Input.tryDoubleSuffixed(args[6]).filter(rz -> rz >= 0 && !Input.isPastWorldLimit(rz));
|
||||
if (arguments.size() > 5) {
|
||||
final Optional<Double> radiusZ = arguments.next().flatMap(Input::tryDoubleSuffixed).filter(r -> r >= 0 && !Input.isPastWorldLimit(r));
|
||||
if (radiusZ.isPresent()) {
|
||||
chunky.getSelection().radiusZ(radiusZ.get());
|
||||
} else {
|
||||
sender.sendMessage("help_trim");
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean inside;
|
||||
if (arguments.size() > 6) {
|
||||
final Optional<String> side = arguments.next().map(String::toLowerCase).filter(s -> "outside".equals(s) || "inside".equals(s));
|
||||
if (side.isPresent()) {
|
||||
inside = side.map("inside"::equals).orElse(false);
|
||||
} else {
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
inside = false;
|
||||
}
|
||||
final int inhabitedTime = arguments.next().flatMap(Input::tryIntegerSuffixed).orElse(Integer.MAX_VALUE);
|
||||
final boolean inhabitedTimeCheck = inhabitedTime < Integer.MAX_VALUE;
|
||||
final Selection selection = chunky.getSelection().build();
|
||||
final Shape shape = ShapeFactory.getShape(selection);
|
||||
final Runnable deletionAction = () -> chunky.getScheduler().runTask(() -> {
|
||||
sender.sendMessagePrefixed("format_start", selection.world().getName(), translate("shape_" + selection.shape()), Formatting.number(selection.centerX()), Formatting.number(selection.centerZ()), Formatting.radius(selection));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START, selection.world().getName(), translate("shape_" + selection.shape()), Formatting.number(selection.centerX()), Formatting.number(selection.centerZ()), Formatting.radius(selection));
|
||||
TrimCommand.Task trimTask = new TrimCommand.Task();
|
||||
chunky.getTrimTasks().put(selection.world().getName(), trimTask);
|
||||
final Optional<Path> regionPath = selection.world().getRegionDirectory();
|
||||
final Optional<Path> poiPath = selection.world().getPOIDirectory();
|
||||
final Optional<Path> entitiesPath = selection.world().getEntitiesDirectory();
|
||||
final AtomicLong finishedRegions = new AtomicLong();
|
||||
final AtomicLong deleted = new AtomicLong();
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final AtomicLong updateTime = new AtomicLong(startTime);
|
||||
try {
|
||||
if (regionPath.isPresent()) {
|
||||
Files.walk(regionPath.get()).forEach(region -> deleted.getAndAdd(checkRegion(region, shape)));
|
||||
}
|
||||
if (poiPath.isPresent()) {
|
||||
Files.walk(poiPath.get()).forEach(region -> checkRegion(region, shape));
|
||||
}
|
||||
if (entitiesPath.isPresent()) {
|
||||
Files.walk(entitiesPath.get()).forEach(region -> checkRegion(region, shape));
|
||||
try (final Stream<Path> files = Files.list(regionPath.get())) {
|
||||
final List<Path> regions = files
|
||||
.filter(file -> ChunkCoordinate.fromRegionFile(file.getFileName().toString()).isPresent())
|
||||
.toList();
|
||||
final long totalRegions = regions.size();
|
||||
for (final Path region : regions) {
|
||||
if (trimTask.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
deleted.getAndAdd(checkRegion(selection.world(), region.getFileName().toString(), shape, inside, inhabitedTimeCheck, inhabitedTime));
|
||||
finishedRegions.getAndIncrement();
|
||||
if (!trimTask.isCancelled() && !chunky.getConfig().isSilent()) {
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final boolean updateIntervalElapsed = ((currentTime - updateTime.get()) / 1e3) > chunky.getConfig().getUpdateInterval();
|
||||
if (updateIntervalElapsed || finishedRegions.get() == totalRegions) {
|
||||
chunky.getServer().getConsole().sendMessagePrefixed(TranslationKey.TASK_TRIM_UPDATE, selection.world().getName(), finishedRegions.get(), String.format("%.2f", 100f * finishedRegions.get() / totalRegions));
|
||||
updateTime.set(currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
final long totalTime = System.currentTimeMillis() - startTime;
|
||||
sender.sendMessagePrefixed("task_trim", deleted.get(), selection.world().getName(), String.format("%.3f", totalTime / 1e3f));
|
||||
chunky.getTrimTasks().remove(selection.world().getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.TASK_TRIM, deleted.get(), selection.world().getName(), String.format("%.3f", totalTime / 1e3f));
|
||||
});
|
||||
chunky.setPendingAction(sender, deletionAction);
|
||||
sender.sendMessagePrefixed("format_trim_confirm", selection.world().getName(), translate("shape_" + selection.shape()), Formatting.number(selection.centerX()), Formatting.number(selection.centerZ()), Formatting.radius(selection), "/chunky confirm");
|
||||
sender.sendMessagePrefixed(inside ? TranslationKey.FORMAT_TRIM_CONFIRM_INSIDE : TranslationKey.FORMAT_TRIM_CONFIRM, selection.world().getName(), translate("shape_" + selection.shape()), Formatting.number(selection.centerX()), Formatting.number(selection.centerZ()), Formatting.radius(selection), "/chunky confirm");
|
||||
if (inhabitedTimeCheck) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_TRIM_CONFIRM_INHABITED, Formatting.number(inhabitedTime));
|
||||
}
|
||||
}
|
||||
|
||||
private int checkRegion(final Path region, final Shape shape) {
|
||||
Optional<ChunkCoordinate> regionCoordinate = tryRegionCoordinate(region);
|
||||
if (!regionCoordinate.isPresent()) {
|
||||
private int checkRegion(final World world, final String regionFileName, final Shape shape, final boolean inside, final boolean inhabitedTimeCheck, final int inhabitedTime) {
|
||||
final Optional<ChunkCoordinate> regionCoordinate = ChunkCoordinate.fromRegionFile(regionFileName);
|
||||
if (regionCoordinate.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
int chunkX = regionCoordinate.get().x << 5;
|
||||
int chunkZ = regionCoordinate.get().z << 5;
|
||||
if (shouldDeleteRegion(shape, chunkX, chunkZ)) {
|
||||
return deleteRegion(region);
|
||||
final int chunkX = regionCoordinate.get().x() << 5;
|
||||
final int chunkZ = regionCoordinate.get().z() << 5;
|
||||
if (!inhabitedTimeCheck && shouldDeleteRegion(shape, inside, chunkX, chunkZ)) {
|
||||
return deleteRegion(world, regionFileName);
|
||||
} else {
|
||||
return trimRegion(region, shape, chunkX, chunkZ);
|
||||
return trimRegion(world, regionFileName, shape, inside, chunkX, chunkZ, inhabitedTimeCheck, inhabitedTime);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<ChunkCoordinate> tryRegionCoordinate(final Path region) {
|
||||
final String fileName = region.getFileName().toString();
|
||||
if (!fileName.startsWith("r.")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final int extension = fileName.indexOf(".mca");
|
||||
if (extension < 2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final String regionCoordinates = fileName.substring(2, extension);
|
||||
final int separator = regionCoordinates.indexOf('.');
|
||||
Optional<Integer> regionX = Input.tryInteger(regionCoordinates.substring(0, separator));
|
||||
Optional<Integer> regionZ = Input.tryInteger(regionCoordinates.substring(separator + 1));
|
||||
if (regionX.isPresent() && regionZ.isPresent()) {
|
||||
return Optional.of(new ChunkCoordinate(regionX.get(), regionZ.get()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private boolean shouldDeleteRegion(final Shape shape, final int chunkX, final int chunkZ) {
|
||||
private boolean shouldDeleteRegion(final Shape shape, final boolean inside, final int chunkX, final int chunkZ) {
|
||||
for (int offsetX = 0; offsetX < 32; ++offsetX) {
|
||||
for (int offsetZ = 0; offsetZ < 32; ++offsetZ) {
|
||||
int chunkCenterX = ((chunkX + offsetX) << 4) + 8;
|
||||
int chunkCenterZ = ((chunkZ + offsetZ) << 4) + 8;
|
||||
if (shape.isBounding(chunkCenterX, chunkCenterZ)) {
|
||||
final int chunkCenterX = ((chunkX + offsetX) << 4) + 8;
|
||||
final int chunkCenterZ = ((chunkZ + offsetZ) << 4) + 8;
|
||||
if (inside != shape.isBounding(chunkCenterX, chunkCenterZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -150,9 +174,18 @@ public class TrimCommand extends ChunkyCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int deleteRegion(final Path region) {
|
||||
private int deleteRegion(final World world, final String regionFileName) {
|
||||
try {
|
||||
Files.deleteIfExists(region);
|
||||
final Path regionPath = world.getRegionDirectory().map(region -> region.resolve(regionFileName)).orElseThrow(IllegalStateException::new);
|
||||
Files.deleteIfExists(regionPath);
|
||||
final Path poiPath = world.getPOIDirectory().map(region -> region.resolve(regionFileName)).orElse(null);
|
||||
if (poiPath != null) {
|
||||
Files.deleteIfExists(poiPath);
|
||||
}
|
||||
final Path entitiesPath = world.getEntitiesDirectory().map(region -> region.resolve(regionFileName)).orElse(null);
|
||||
if (entitiesPath != null) {
|
||||
Files.deleteIfExists(entitiesPath);
|
||||
}
|
||||
return 1024;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -160,42 +193,98 @@ public class TrimCommand extends ChunkyCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private int trimRegion(final Path region, final Shape shape, final int chunkX, final int chunkZ) {
|
||||
private int trimRegion(final World world, final String regionFileName, final Shape shape, final boolean inside, final int chunkX, final int chunkZ, final boolean inhabitedTimeCheck, final int inhabitedTime) {
|
||||
final Path regionPath = world.getRegionDirectory().map(region -> region.resolve(regionFileName)).orElseThrow(IllegalStateException::new);
|
||||
final Path poiPath = world.getPOIDirectory().map(region -> region.resolve(regionFileName)).orElse(null);
|
||||
final Path entitiesPath = world.getEntitiesDirectory().map(region -> region.resolve(regionFileName)).orElse(null);
|
||||
int marked = 0;
|
||||
int deleted = 0;
|
||||
try (RandomAccessFile regionFile = new RandomAccessFile(region.toFile(), "rw")) {
|
||||
final RegionFile regionData = inhabitedTimeCheck ? new RegionFile(regionPath.toFile(), ChunkFilter.of(TagType.LONG, "InhabitedTime")) : null;
|
||||
try (final RandomAccessFile regionFile = new RandomAccessFile(regionPath.toFile(), "rw");
|
||||
final RandomAccessFile poiFile = poiPath == null || Files.notExists(poiPath) ? null : new RandomAccessFile(poiPath.toFile(), "rw");
|
||||
final RandomAccessFile entitiesFile = entitiesPath == null || Files.notExists(entitiesPath) ? null : new RandomAccessFile(entitiesPath.toFile(), "rw")) {
|
||||
if (regionFile.length() < 4096) {
|
||||
return 0;
|
||||
}
|
||||
final boolean poiValid = poiFile != null && poiFile.length() >= 4096;
|
||||
final boolean entitiesValid = entitiesFile != null && entitiesFile.length() >= 4096;
|
||||
for (int offsetX = 0; offsetX < 32; ++offsetX) {
|
||||
for (int offsetZ = 0; offsetZ < 32; ++offsetZ) {
|
||||
int chunkCenterX = ((chunkX + offsetX) << 4) + 8;
|
||||
int chunkCenterZ = ((chunkZ + offsetZ) << 4) + 8;
|
||||
if (!shape.isBounding(chunkCenterX, chunkCenterZ)) {
|
||||
int chunkLocation = ((offsetX % 32) + (offsetZ % 32) * 32) * 4;
|
||||
final int offsetChunkX = chunkX + offsetX;
|
||||
final int offsetChunkZ = chunkZ + offsetZ;
|
||||
final int chunkCenterX = (offsetChunkX << 4) + 8;
|
||||
final int chunkCenterZ = (offsetChunkZ << 4) + 8;
|
||||
final boolean trimChunk;
|
||||
if (inside) {
|
||||
trimChunk = shape.isBounding(chunkCenterX, chunkCenterZ);
|
||||
} else {
|
||||
trimChunk = !shape.isBounding(chunkCenterX, chunkCenterZ);
|
||||
}
|
||||
final boolean trimInhabited = regionData == null || regionData.getChunk(offsetChunkX, offsetChunkZ)
|
||||
.map(chunk -> {
|
||||
final Tag tag = chunk.getData();
|
||||
if (!(tag instanceof final LongTag inhabited)) {
|
||||
return true;
|
||||
}
|
||||
return inhabited.value() <= inhabitedTime;
|
||||
})
|
||||
.orElse(true);
|
||||
if (trimChunk && trimInhabited) {
|
||||
++marked;
|
||||
final int chunkLocation = ((offsetX % 32) + (offsetZ % 32) * 32) * 4;
|
||||
regionFile.seek(chunkLocation);
|
||||
if (regionFile.readInt() != 0) {
|
||||
regionFile.seek(chunkLocation);
|
||||
regionFile.writeInt(0);
|
||||
++deleted;
|
||||
}
|
||||
if (poiValid) {
|
||||
poiFile.seek(chunkLocation);
|
||||
if (poiFile.readInt() != 0) {
|
||||
poiFile.seek(chunkLocation);
|
||||
poiFile.writeInt(0);
|
||||
}
|
||||
}
|
||||
if (entitiesValid) {
|
||||
entitiesFile.seek(chunkLocation);
|
||||
if (entitiesFile.readInt() != 0) {
|
||||
entitiesFile.seek(chunkLocation);
|
||||
entitiesFile.writeInt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (inhabitedTimeCheck && marked == 1024) {
|
||||
deleteRegion(world, regionFileName);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
} else if (args.length == 3) {
|
||||
return Input.SHAPES;
|
||||
} else if (arguments.size() == 2) {
|
||||
return ShapeType.all();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public static final class Task {
|
||||
private boolean cancelled;
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
public void setCancelled(final boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -7,41 +7,71 @@ import org.popcraft.chunky.integration.Integration;
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WorldBorderCommand extends ChunkyCommand {
|
||||
public WorldBorderCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public class WorldBorderCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public WorldBorderCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Selection previous = chunky.getSelection().build();
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = arguments.next().flatMap(arg -> Input.tryWorld(chunky, arg));
|
||||
if (world.isPresent()) {
|
||||
chunky.getSelection().world(world.get());
|
||||
} else {
|
||||
sender.sendMessage(TranslationKey.HELP_WORLDBORDER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
final Selection previous = chunky.getSelection().build();
|
||||
if (!setBorderViaIntegration(previous.world())) {
|
||||
chunky.getSelection().worldborder();
|
||||
}
|
||||
Selection current = chunky.getSelection().build();
|
||||
sender.sendMessagePrefixed("format_center", Formatting.number(current.centerX()), Formatting.number(current.centerZ()));
|
||||
final Selection current = chunky.getSelection().build();
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CENTER, Formatting.number(current.centerX()), Formatting.number(current.centerZ()));
|
||||
if (current.radiusX() == current.radiusZ()) {
|
||||
sender.sendMessagePrefixed("format_radius", Formatting.number(current.radiusX()));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(current.radiusX()));
|
||||
} else {
|
||||
sender.sendMessagePrefixed("format_radii", Formatting.number(current.radiusX()), Formatting.number(current.radiusZ()));
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(current.radiusX()), Formatting.number(current.radiusZ()));
|
||||
}
|
||||
if (!previous.shape().equals(current.shape())) {
|
||||
sender.sendMessagePrefixed("format_shape", current.shape());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, current.shape());
|
||||
}
|
||||
if (current.radiusX() > chunky.getServer().getMaxWorldSize()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_WORLDBORDER_TOO_LARGE, chunky.getServer().getMaxWorldSize());
|
||||
}
|
||||
}
|
||||
|
||||
boolean setBorderViaIntegration(World world) {
|
||||
Map<String, Integration> integrations = chunky.getServer().getIntegrations();
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
boolean setBorderViaIntegration(final World world) {
|
||||
final Map<String, Integration> integrations = chunky.getServer().getIntegrations();
|
||||
if (integrations.containsKey("border")) {
|
||||
BorderIntegration worldborder = (BorderIntegration) integrations.get("border");
|
||||
String worldName = world.getName();
|
||||
final BorderIntegration worldborder = (BorderIntegration) integrations.get("border");
|
||||
final String worldName = world.getName();
|
||||
if (worldborder.hasBorder(worldName)) {
|
||||
Border border = worldborder.getBorder(worldName);
|
||||
Coordinate center = border.getCenter();
|
||||
final Border border = worldborder.getBorder(worldName);
|
||||
final Vector2 center = border.getCenter();
|
||||
chunky.getSelection().center(center.getX(), center.getZ())
|
||||
.radiusX(border.getRadiusX()).radiusZ(border.getRadiusZ())
|
||||
.shape(border.getShape());
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Player;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WorldCommand extends ChunkyCommand {
|
||||
public WorldCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
}
|
||||
public class WorldCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("help_world");
|
||||
return;
|
||||
}
|
||||
Optional<World> newWorld = Input.tryWorld(chunky, String.join(" ", Arrays.copyOfRange(args, 1, args.length)));
|
||||
if (!newWorld.isPresent()) {
|
||||
sender.sendMessage("help_world");
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().world(newWorld.get());
|
||||
sender.sendMessagePrefixed("format_world", newWorld.get().getName());
|
||||
public WorldCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
if (args.length == 2) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final World world;
|
||||
if (arguments.size() == 0 && sender instanceof final Player player) {
|
||||
world = player.getWorld();
|
||||
} else {
|
||||
world = Input.tryWorld(chunky, arguments.joined()).orElse(null);
|
||||
}
|
||||
if (world == null) {
|
||||
sender.sendMessage(TranslationKey.HELP_WORLD);
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().world(world);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_WORLD, world.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
if (arguments.size() == 1) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
chunky.getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.popcraft.chunky.event;
|
||||
|
||||
public abstract class Cancellable implements Event {
|
||||
private boolean cancelled;
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
public void setCancelled(final boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.popcraft.chunky.event;
|
||||
|
||||
public interface Event {
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.popcraft.chunky.event;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class EventBus {
|
||||
private static final MethodHandle accept;
|
||||
|
||||
static {
|
||||
MethodHandle acceptMethodHandle = null;
|
||||
try {
|
||||
acceptMethodHandle = MethodHandles.publicLookup().findVirtual(Consumer.class, "accept", MethodType.methodType(void.class, Object.class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
accept = acceptMethodHandle;
|
||||
}
|
||||
|
||||
private final Map<Class<?>, Set<Consumer<?>>> subscribers = new HashMap<>();
|
||||
|
||||
public <T> void subscribe(final Class<T> eventClass, final Consumer<T> subscriber) {
|
||||
subscribers.computeIfAbsent(eventClass, x -> new HashSet<>());
|
||||
subscribers.get(eventClass).add(subscriber);
|
||||
}
|
||||
|
||||
public <T> void unsubscribe(final Class<T> eventClass, final Consumer<T> subscriber) {
|
||||
subscribers.computeIfAbsent(eventClass, x -> new HashSet<>());
|
||||
subscribers.get(eventClass).remove(subscriber);
|
||||
}
|
||||
|
||||
public void unsubscribeAll() {
|
||||
subscribers.clear();
|
||||
}
|
||||
|
||||
public void unsubscribeAll(final Class<?> eventClass) {
|
||||
subscribers.remove(eventClass);
|
||||
}
|
||||
|
||||
public void call(final Object event) {
|
||||
final Class<?> eventClass = event.getClass();
|
||||
if (accept == null || !subscribers.containsKey(eventClass)) {
|
||||
return;
|
||||
}
|
||||
subscribers.get(eventClass).forEach(subscriber -> {
|
||||
try {
|
||||
accept.invoke(subscriber, event);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.popcraft.chunky.event.command;
|
||||
|
||||
import org.popcraft.chunky.event.Event;
|
||||
|
||||
public record ReloadCommandEvent() implements Event {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.popcraft.chunky.event.task;
|
||||
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.event.Event;
|
||||
|
||||
public record GenerationTaskFinishEvent(GenerationTask generationTask) implements Event {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.popcraft.chunky.event.task;
|
||||
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.event.Event;
|
||||
|
||||
public record GenerationTaskUpdateEvent(GenerationTask generationTask) implements Event {
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
public abstract class AbstractMapIntegration implements MapIntegration {
|
||||
protected String label = "World Border";
|
||||
protected int color = 0xFF0000;
|
||||
protected int weight = 3;
|
||||
|
||||
@Override
|
||||
public void setOptions(String label, String color, boolean hideByDefault, int priority, int weight) {
|
||||
if (label != null && !label.isEmpty()) {
|
||||
this.label = label;
|
||||
}
|
||||
this.weight = Math.max(1, weight);
|
||||
if (color.length() != 6) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.color = Integer.parseInt(color, 16);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerAPI;
|
||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||
import de.bluecolored.bluemap.api.marker.ShapeMarker;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.AbstractEllipse;
|
||||
import org.popcraft.chunky.shape.AbstractPolygon;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BlueMapIntegration extends AbstractMapIntegration {
|
||||
private static final String MARKERSET_ID = "chunky";
|
||||
private final List<Runnable> pendingMarkers = new ArrayList<>();
|
||||
private BlueMapAPI blueMapAPI;
|
||||
|
||||
public BlueMapIntegration() {
|
||||
BlueMapAPI.onEnable(blueMap -> {
|
||||
this.blueMapAPI = blueMap;
|
||||
pendingMarkers.forEach(Runnable::run);
|
||||
pendingMarkers.clear();
|
||||
});
|
||||
BlueMapAPI.onDisable(blueMap -> this.blueMapAPI = null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addShapeMarker(final World world, final Shape shape) {
|
||||
if (blueMapAPI == null) {
|
||||
this.pendingMarkers.add(() -> this.addShapeMarker(world, shape));
|
||||
return;
|
||||
}
|
||||
final MarkerAPI markerAPI;
|
||||
try {
|
||||
markerAPI = blueMapAPI.getMarkerAPI();
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
final MarkerSet markerSet = markerAPI.createMarkerSet(MARKERSET_ID);
|
||||
markerSet.setLabel(this.label);
|
||||
de.bluecolored.bluemap.api.marker.Shape blueShape;
|
||||
if (shape instanceof AbstractPolygon) {
|
||||
AbstractPolygon polygon = (AbstractPolygon) shape;
|
||||
double[] pointsX = polygon.pointsX();
|
||||
double[] pointsZ = polygon.pointsZ();
|
||||
if (pointsX.length != pointsZ.length) {
|
||||
return;
|
||||
}
|
||||
Vector2d[] points = new Vector2d[pointsX.length];
|
||||
for (int i = 0; i < pointsX.length; ++i) {
|
||||
points[i] = new Vector2d(pointsX[i], pointsZ[i]);
|
||||
}
|
||||
blueShape = new de.bluecolored.bluemap.api.marker.Shape(points);
|
||||
} else if (shape instanceof AbstractEllipse) {
|
||||
AbstractEllipse ellipse = (AbstractEllipse) shape;
|
||||
double[] center = ellipse.getCenter();
|
||||
double[] radii = ellipse.getRadii();
|
||||
Vector2d centerPos = new Vector2d(center[0], center[1]);
|
||||
blueShape = de.bluecolored.bluemap.api.marker.Shape.createEllipse(centerPos, radii[0], radii[1], 100);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
blueMapAPI.getWorld(world.getUUID()).ifPresent(blueWorld -> blueWorld.getMaps().forEach(map -> {
|
||||
ShapeMarker marker = markerSet.createShapeMarker(world.getName(), map, blueShape, world.getSeaLevel());
|
||||
marker.setColors(new Color(this.color), new Color(0, true));
|
||||
marker.setLabel(this.label);
|
||||
try {
|
||||
ShapeMarker.class.getMethod("setLineWidth", int.class).invoke(marker, this.weight);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}));
|
||||
try {
|
||||
markerAPI.save();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShapeMarker(final World world) {
|
||||
if (blueMapAPI == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final MarkerAPI markerAPI = blueMapAPI.getMarkerAPI();
|
||||
final MarkerSet markerSet = markerAPI.createMarkerSet(MARKERSET_ID);
|
||||
blueMapAPI.getWorld(world.getUUID()).ifPresent(blueWorld -> blueWorld.getMaps().forEach(map -> markerSet.removeMarker(world.getName())));
|
||||
markerAPI.save();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllShapeMarkers() {
|
||||
if (blueMapAPI == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final MarkerAPI markerAPI = blueMapAPI.getMarkerAPI();
|
||||
if (markerAPI.removeMarkerSet(MARKERSET_ID)) {
|
||||
markerAPI.save();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import org.dynmap.DynmapAPI;
|
||||
import org.dynmap.markers.AreaMarker;
|
||||
import org.dynmap.markers.CircleMarker;
|
||||
import org.dynmap.markers.MarkerDescription;
|
||||
import org.dynmap.markers.MarkerSet;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.AbstractEllipse;
|
||||
import org.popcraft.chunky.shape.AbstractPolygon;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DynmapIntegration extends AbstractMapIntegration {
|
||||
private final MarkerSet markerSet;
|
||||
private final Map<World, MarkerDescription> markers;
|
||||
|
||||
public DynmapIntegration(DynmapAPI dynmapAPI) {
|
||||
this.markerSet = dynmapAPI.getMarkerAPI().createMarkerSet("chunky.markerset", this.label, null, false);
|
||||
this.markers = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addShapeMarker(World world, Shape shape) {
|
||||
removeShapeMarker(world);
|
||||
if (shape instanceof AbstractPolygon) {
|
||||
AbstractPolygon polygon = (AbstractPolygon) shape;
|
||||
AreaMarker marker = markerSet.createAreaMarker(null, this.label, false, world.getName(), polygon.pointsX(), polygon.pointsZ(), false);
|
||||
marker.setLineStyle(this.weight, 1f, color);
|
||||
marker.setFillStyle(0f, 0x000000);
|
||||
markers.put(world, marker);
|
||||
} else if (shape instanceof AbstractEllipse) {
|
||||
AbstractEllipse ellipse = (AbstractEllipse) shape;
|
||||
double[] center = ellipse.getCenter();
|
||||
double[] radii = ellipse.getRadii();
|
||||
CircleMarker marker = markerSet.createCircleMarker(null, this.label, false, world.getName(), center[0], world.getSeaLevel(), center[1], radii[0], radii[1], false);
|
||||
marker.setLineStyle(this.weight, 1f, color);
|
||||
marker.setFillStyle(0f, 0x000000);
|
||||
markers.put(world, marker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShapeMarker(World world) {
|
||||
MarkerDescription marker = markers.remove(world);
|
||||
if (marker != null) {
|
||||
marker.deleteMarker();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllShapeMarkers() {
|
||||
if (markerSet != null) {
|
||||
markerSet.deleteMarkerSet();
|
||||
}
|
||||
markers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(String label, String color, boolean hideByDefault, int priority, int weight) {
|
||||
super.setOptions(label, color, hideByDefault, priority, weight);
|
||||
if (markerSet != null) {
|
||||
markerSet.setHideByDefault(hideByDefault);
|
||||
markerSet.setLayerPriority(priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
|
||||
public interface MapIntegration extends Integration {
|
||||
void addShapeMarker(World world, Shape shape);
|
||||
|
||||
void removeShapeMarker(World world);
|
||||
|
||||
void removeAllShapeMarkers();
|
||||
|
||||
void setOptions(String label, String color, boolean hideByDefault, int priority, int weight);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import net.pl3x.map.api.Key;
|
||||
import net.pl3x.map.api.LayerProvider;
|
||||
import net.pl3x.map.api.MapWorld;
|
||||
import net.pl3x.map.api.Pl3xMap;
|
||||
import net.pl3x.map.api.Point;
|
||||
import net.pl3x.map.api.Registry;
|
||||
import net.pl3x.map.api.SimpleLayerProvider;
|
||||
import net.pl3x.map.api.marker.Marker;
|
||||
import net.pl3x.map.api.marker.MarkerOptions;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.shape.AbstractEllipse;
|
||||
import org.popcraft.chunky.shape.AbstractPolygon;
|
||||
import org.popcraft.chunky.shape.Circle;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Pl3xMapIntegration extends AbstractMapIntegration {
|
||||
private static final Key WORLDBORDER_KEY = Key.of("pl3xmap-worldborder");
|
||||
private static final Key CHUNKY_KEY = Key.of("chunky");
|
||||
private final Pl3xMap pl3xMap;
|
||||
private final Map<UUID, LayerProvider> defaultProviders = new HashMap<>();
|
||||
private boolean hideByDefault;
|
||||
private int priority;
|
||||
|
||||
public Pl3xMapIntegration(Pl3xMap pl3xMap) {
|
||||
this.pl3xMap = pl3xMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addShapeMarker(final World world, final Shape shape) {
|
||||
pl3xMap.getWorldIfEnabled(world.getUUID()).ifPresent(pl3xWorld -> {
|
||||
Registry<LayerProvider> layerRegistry = pl3xWorld.layerRegistry();
|
||||
if (layerRegistry.hasEntry(WORLDBORDER_KEY)) {
|
||||
defaultProviders.put(pl3xWorld.uuid(), layerRegistry.get(WORLDBORDER_KEY));
|
||||
layerRegistry.unregister(WORLDBORDER_KEY);
|
||||
}
|
||||
if (!layerRegistry.hasEntry(CHUNKY_KEY)) {
|
||||
layerRegistry.register(CHUNKY_KEY, SimpleLayerProvider.builder(this.label)
|
||||
.defaultHidden(hideByDefault)
|
||||
.layerPriority(1)
|
||||
.zIndex(priority)
|
||||
.build());
|
||||
}
|
||||
SimpleLayerProvider chunkyLayerProvider = (SimpleLayerProvider) layerRegistry.get(CHUNKY_KEY);
|
||||
chunkyLayerProvider.clearMarkers();
|
||||
final Marker marker;
|
||||
if (shape instanceof AbstractPolygon) {
|
||||
AbstractPolygon polygon = (AbstractPolygon) shape;
|
||||
double[] pointsX = polygon.pointsX();
|
||||
double[] pointsZ = polygon.pointsZ();
|
||||
if (pointsX.length != pointsZ.length) {
|
||||
return;
|
||||
}
|
||||
Point[] points = new Point[pointsX.length + 1];
|
||||
for (int i = 0; i < pointsX.length; ++i) {
|
||||
points[i] = Point.of(pointsX[i], pointsZ[i]);
|
||||
}
|
||||
points[pointsX.length] = Point.of(pointsX[0], pointsZ[0]);
|
||||
marker = Marker.polyline(points);
|
||||
} else if (shape instanceof AbstractEllipse) {
|
||||
AbstractEllipse ellipse = (AbstractEllipse) shape;
|
||||
double[] center = ellipse.getCenter();
|
||||
double[] radii = ellipse.getRadii();
|
||||
Point centerPoint = Point.of(center[0], center[1]);
|
||||
if (ellipse instanceof Circle) {
|
||||
marker = Marker.circle(centerPoint, radii[0]);
|
||||
} else {
|
||||
marker = ellipse(centerPoint, radii[0], radii[1]);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
MarkerOptions markerOptions = MarkerOptions.builder()
|
||||
.stroke(true)
|
||||
.strokeColor(new Color(this.color))
|
||||
.strokeWeight(this.weight)
|
||||
.fill(false)
|
||||
.clickTooltip(this.label)
|
||||
.build();
|
||||
marker.markerOptions(markerOptions);
|
||||
chunkyLayerProvider.addMarker(CHUNKY_KEY, marker);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShapeMarker(final World world) {
|
||||
pl3xMap.getWorldIfEnabled(world.getUUID()).ifPresent(this::unregisterLayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllShapeMarkers() {
|
||||
pl3xMap.mapWorlds().forEach(this::unregisterLayer);
|
||||
}
|
||||
|
||||
private void unregisterLayer(MapWorld mapWorld) {
|
||||
Registry<LayerProvider> layerRegistry = mapWorld.layerRegistry();
|
||||
if (!layerRegistry.hasEntry(WORLDBORDER_KEY)) {
|
||||
LayerProvider defaultProvider = defaultProviders.get(mapWorld.uuid());
|
||||
if (defaultProvider != null) {
|
||||
layerRegistry.register(WORLDBORDER_KEY, defaultProvider);
|
||||
}
|
||||
}
|
||||
if (layerRegistry.hasEntry(CHUNKY_KEY)) {
|
||||
((SimpleLayerProvider) layerRegistry.get(CHUNKY_KEY)).clearMarkers();
|
||||
layerRegistry.unregister(CHUNKY_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(String label, String color, boolean hideByDefault, int priority, int weight) {
|
||||
super.setOptions(label, color, hideByDefault, priority, weight);
|
||||
this.hideByDefault = hideByDefault;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
private Marker ellipse(Point center, double radiusX, double radiusZ) {
|
||||
int numPoints = 360;
|
||||
Point[] points = new Point[numPoints + 1];
|
||||
double segmentAngle = 2 * Math.PI / numPoints;
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
double pointX = center.x() + Math.sin(segmentAngle * i) * radiusX;
|
||||
double pointZ = center.z() + Math.cos(segmentAngle * i) * radiusZ;
|
||||
points[i] = Point.of(pointX, pointZ);
|
||||
}
|
||||
points[numPoints] = Point.of(center.x(), center.z() + radiusZ);
|
||||
return Marker.polyline(points);
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ public interface ChunkIterator extends Iterator<ChunkCoordinate> {
|
||||
long total();
|
||||
|
||||
String name();
|
||||
|
||||
default boolean process() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,30 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public class ChunkIteratorFactory {
|
||||
public static ChunkIterator getChunkIterator(Selection selection, long count) {
|
||||
switch (selection.shape()) {
|
||||
case "rectangle":
|
||||
case "oval":
|
||||
case "ellipse":
|
||||
return new Loop2ChunkIterator(selection, count);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (selection.pattern()) {
|
||||
case "loop":
|
||||
return new Loop2ChunkIterator(selection, count);
|
||||
case "spiral":
|
||||
return new SpiralChunkIterator(selection, count);
|
||||
case "concentric":
|
||||
default:
|
||||
return new ConcentricChunkIterator(selection, count);
|
||||
}
|
||||
public final class ChunkIteratorFactory {
|
||||
private ChunkIteratorFactory() {
|
||||
}
|
||||
|
||||
public static ChunkIterator getChunkIterator(Selection selection) {
|
||||
public static ChunkIterator getChunkIterator(final Selection selection, final long count) {
|
||||
if (selection.pattern().getType().equals(PatternType.WORLD)) {
|
||||
return new WorldChunkIterator(selection);
|
||||
}
|
||||
final String shape = selection.shape();
|
||||
if (ShapeType.RECTANGLE.equals(shape) || ShapeType.ELLIPSE.equals(shape) || ShapeType.OVAL.equals(shape)) {
|
||||
return new Loop2ChunkIterator(selection, count);
|
||||
}
|
||||
return switch (selection.pattern().getType()) {
|
||||
case PatternType.LOOP -> new Loop2ChunkIterator(selection, count);
|
||||
case PatternType.SPIRAL -> new SpiralChunkIterator(selection, count);
|
||||
case PatternType.CSV -> new CsvChunkIterator(selection, count);
|
||||
case PatternType.CONCENTRIC -> new ConcentricChunkIterator(selection, count);
|
||||
default -> new RegionChunkIterator(selection, count);
|
||||
};
|
||||
}
|
||||
|
||||
public static ChunkIterator getChunkIterator(final Selection selection) {
|
||||
return getChunkIterator(selection, 0);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.popcraft.chunky.iterator;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class ConcentricChunkIterator implements ChunkIterator {
|
||||
private final int xCenter, zCenter;
|
||||
private final int radiusChunks;
|
||||
@@ -12,7 +14,7 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
private int down, left, up, right;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public ConcentricChunkIterator(Selection selection, long count) {
|
||||
public ConcentricChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
@@ -40,13 +42,13 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public ConcentricChunkIterator(Selection selection) {
|
||||
public ConcentricChunkIterator(final Selection selection) {
|
||||
this.radiusChunks = selection.radiusChunksX();
|
||||
this.x = selection.centerChunkX();
|
||||
this.z = selection.centerChunkZ();
|
||||
this.xCenter = x;
|
||||
this.zCenter = z;
|
||||
long diameterChunks = selection.diameterChunksX();
|
||||
final long diameterChunks = selection.diameterChunksX();
|
||||
this.total = diameterChunks * diameterChunks;
|
||||
}
|
||||
|
||||
@@ -57,6 +59,9 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (x == xCenter + annulus && z == zCenter + annulus) {
|
||||
++annulus;
|
||||
@@ -91,6 +96,6 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "concentric";
|
||||
return PatternType.CONCENTRIC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CsvChunkIterator implements ChunkIterator {
|
||||
private final Queue<ChunkCoordinate> chunks;
|
||||
private final long total;
|
||||
private final String name;
|
||||
|
||||
public CsvChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
for (int i = 0; i < count && hasNext(); ++i) {
|
||||
chunks.poll();
|
||||
}
|
||||
}
|
||||
|
||||
public CsvChunkIterator(final Selection selection) {
|
||||
final Path filePath = selection.pattern().getValue()
|
||||
.map(value -> selection.chunky().getConfig().getDirectory().resolve(String.format("%s.csv", value)))
|
||||
.orElse(null);
|
||||
this.chunks = new LinkedList<>();
|
||||
final AtomicLong valid = new AtomicLong();
|
||||
if (filePath != null) {
|
||||
try (final Stream<String> lines = Files.lines(filePath)) {
|
||||
lines.forEach(line -> {
|
||||
final String[] split = line.split(",");
|
||||
if (split.length > 1) {
|
||||
final Optional<Integer> x = Input.tryInteger(split[0]);
|
||||
final Optional<Integer> z = Input.tryInteger(split[1]);
|
||||
if (x.isPresent() && z.isPresent()) {
|
||||
chunks.add(new ChunkCoordinate(x.get(), z.get()));
|
||||
valid.incrementAndGet();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.total = valid.get();
|
||||
this.name = selection.pattern().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !chunks.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (chunks.isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return chunks.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.popcraft.chunky.iterator;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class Loop2ChunkIterator implements ChunkIterator {
|
||||
private final int x1, x2, z1, z2;
|
||||
private final long diameterChunksZ;
|
||||
@@ -10,7 +12,7 @@ public class Loop2ChunkIterator implements ChunkIterator {
|
||||
private int x, z;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public Loop2ChunkIterator(Selection selection, long count) {
|
||||
public Loop2ChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
@@ -22,18 +24,18 @@ public class Loop2ChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public Loop2ChunkIterator(Selection selection) {
|
||||
int radiusChunksX = selection.radiusChunksX();
|
||||
int radiusChunksZ = selection.radiusChunksZ();
|
||||
int centerChunkX = selection.centerChunkX();
|
||||
int centerChunkZ = selection.centerChunkZ();
|
||||
public Loop2ChunkIterator(final Selection selection) {
|
||||
final int radiusChunksX = selection.radiusChunksX();
|
||||
final int radiusChunksZ = selection.radiusChunksZ();
|
||||
final int centerChunkX = selection.centerChunkX();
|
||||
final int centerChunkZ = selection.centerChunkZ();
|
||||
this.x1 = centerChunkX - radiusChunksX;
|
||||
this.x2 = centerChunkX + radiusChunksX;
|
||||
this.z1 = centerChunkZ - radiusChunksZ;
|
||||
this.z2 = centerChunkZ + radiusChunksZ;
|
||||
this.x = x1;
|
||||
this.z = z1;
|
||||
int diameterChunksX = selection.diameterChunksX();
|
||||
final int diameterChunksX = selection.diameterChunksX();
|
||||
this.diameterChunksZ = selection.diameterChunksZ();
|
||||
this.total = diameterChunksX * diameterChunksZ;
|
||||
}
|
||||
@@ -45,6 +47,9 @@ public class Loop2ChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (++z > z2) {
|
||||
z = z1;
|
||||
@@ -62,6 +67,6 @@ public class Loop2ChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "loop";
|
||||
return PatternType.LOOP;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class PatternType {
|
||||
public static final String CONCENTRIC = "concentric";
|
||||
public static final String LOOP = "loop";
|
||||
public static final String SPIRAL = "spiral";
|
||||
public static final String CSV = "csv";
|
||||
public static final String REGION = "region";
|
||||
public static final String WORLD = "world";
|
||||
|
||||
public static final List<String> ALL = List.of(CONCENTRIC, LOOP, SPIRAL, CSV, REGION, WORLD);
|
||||
|
||||
private PatternType() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Hilbert;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class RegionChunkIterator implements ChunkIterator {
|
||||
private final int centerRegionX;
|
||||
private final int centerRegionZ;
|
||||
private final int radiusRegions;
|
||||
private final int minChunkX;
|
||||
private final int minChunkZ;
|
||||
private final int maxChunkX;
|
||||
private final int maxChunkZ;
|
||||
private final long totalChunks;
|
||||
private RegionChunkProgress currentRegionProgress;
|
||||
private int regionX;
|
||||
private int regionZ;
|
||||
private int annulusRegions;
|
||||
private int spanRegions;
|
||||
private int downRegions;
|
||||
private int leftRegions;
|
||||
private int upRegions;
|
||||
private int rightRegions;
|
||||
private boolean hasNextRegion = true;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public RegionChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
this.currentRegionProgress = null;
|
||||
this.regionX = centerRegionX;
|
||||
this.regionZ = centerRegionZ;
|
||||
this.annulusRegions = 0;
|
||||
this.spanRegions = 0;
|
||||
this.downRegions = this.leftRegions = this.upRegions = this.rightRegions = 0;
|
||||
this.hasNextRegion = true;
|
||||
this.hasNext = true;
|
||||
long countRemainingChunks = count;
|
||||
final long estimatedRegionCount = count / 1024;
|
||||
int estimatedDiameterRegions = (int) Math.floor(Math.sqrt(estimatedRegionCount));
|
||||
if (estimatedDiameterRegions % 2 == 0) {
|
||||
--estimatedDiameterRegions;
|
||||
}
|
||||
if (estimatedDiameterRegions > 2) {
|
||||
final int skipDiameterRegions = estimatedDiameterRegions - 2;
|
||||
this.annulusRegions = skipDiameterRegions / 2 + 1;
|
||||
this.regionX += annulusRegions;
|
||||
this.regionZ += annulusRegions - 1;
|
||||
this.spanRegions = 2 * annulusRegions;
|
||||
++this.downRegions;
|
||||
final long skipChunks = ((long) skipDiameterRegions * skipDiameterRegions) * 1024;
|
||||
countRemainingChunks -= skipChunks;
|
||||
}
|
||||
currentRegionProgress = nextRegionChunkProgress(countRemainingChunks);
|
||||
countRemainingChunks -= Math.min(countRemainingChunks, currentRegionProgress == null ? 0 : currentRegionProgress.total());
|
||||
while (currentRegionProgress != null && !currentRegionProgress.hasNext()) {
|
||||
currentRegionProgress = nextRegionChunkProgress(countRemainingChunks);
|
||||
countRemainingChunks -= Math.min(countRemainingChunks, currentRegionProgress == null ? 0 : currentRegionProgress.total());
|
||||
}
|
||||
if (currentRegionProgress == null) {
|
||||
hasNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
public RegionChunkIterator(final Selection selection) {
|
||||
this.centerRegionX = selection.centerRegionX();
|
||||
this.centerRegionZ = selection.centerRegionZ();
|
||||
this.radiusRegions = selection.radiusRegionsX();
|
||||
final int centerChunkX = selection.centerChunkX();
|
||||
final int centerChunkZ = selection.centerChunkZ();
|
||||
final int radiusChunks = selection.radiusChunksX();
|
||||
this.minChunkX = centerChunkX - radiusChunks;
|
||||
this.minChunkZ = centerChunkZ - radiusChunks;
|
||||
this.maxChunkX = centerChunkX + radiusChunks;
|
||||
this.maxChunkZ = centerChunkZ + radiusChunks;
|
||||
this.regionX = centerRegionX;
|
||||
this.regionZ = centerRegionZ;
|
||||
final long diameterChunks = selection.diameterChunksX();
|
||||
this.totalChunks = diameterChunks * diameterChunks;
|
||||
this.currentRegionProgress = nextRegionChunkProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = currentRegionProgress.next();
|
||||
while (currentRegionProgress != null && !currentRegionProgress.hasNext()) {
|
||||
currentRegionProgress = nextRegionChunkProgress();
|
||||
}
|
||||
if (currentRegionProgress == null) {
|
||||
hasNext = false;
|
||||
}
|
||||
return chunkCoord;
|
||||
}
|
||||
|
||||
private RegionChunkProgress nextRegionChunkProgress() {
|
||||
return nextRegionChunkProgress(0);
|
||||
}
|
||||
|
||||
private RegionChunkProgress nextRegionChunkProgress(final long count) {
|
||||
if (!hasNextRegion) {
|
||||
return null;
|
||||
}
|
||||
final RegionChunkProgress regionChunkProgress = new RegionChunkProgress(regionX, regionZ, count);
|
||||
if (regionX == centerRegionX + annulusRegions && regionZ == centerRegionZ + annulusRegions) {
|
||||
++annulusRegions;
|
||||
++regionX;
|
||||
++regionZ;
|
||||
if (annulusRegions > radiusRegions) {
|
||||
hasNextRegion = false;
|
||||
}
|
||||
spanRegions = 2 * annulusRegions;
|
||||
downRegions = leftRegions = upRegions = rightRegions = 0;
|
||||
}
|
||||
if (downRegions < spanRegions) {
|
||||
--regionZ;
|
||||
++downRegions;
|
||||
} else if (leftRegions < spanRegions) {
|
||||
--regionX;
|
||||
++leftRegions;
|
||||
} else if (upRegions < spanRegions) {
|
||||
++regionZ;
|
||||
++upRegions;
|
||||
} else if (rightRegions < spanRegions) {
|
||||
++regionX;
|
||||
++rightRegions;
|
||||
}
|
||||
return regionChunkProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return totalChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return PatternType.REGION;
|
||||
}
|
||||
|
||||
public final class RegionChunkProgress implements ChunkIterator {
|
||||
private final int minX;
|
||||
private final int minZ;
|
||||
private final int sizeZ;
|
||||
private final int total;
|
||||
private final boolean full;
|
||||
private final String name;
|
||||
private int current;
|
||||
private int offsetX;
|
||||
private int offsetZ;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public RegionChunkProgress(final int x, final int z, final long count) {
|
||||
this(x, z);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
this.current = (int) Math.min(count, 1024);
|
||||
if (!full && sizeZ > 0) {
|
||||
offsetX = current / sizeZ;
|
||||
offsetZ = current % sizeZ;
|
||||
}
|
||||
if (current >= total) {
|
||||
this.hasNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
public RegionChunkProgress(final int x, final int z) {
|
||||
final int lowEdgeX = x << 5;
|
||||
final int lowEdgeZ = z << 5;
|
||||
final int highEdgeX = lowEdgeX + 31;
|
||||
final int highEdgeZ = lowEdgeZ + 31;
|
||||
this.minX = Math.max(lowEdgeX, minChunkX);
|
||||
this.minZ = Math.max(lowEdgeZ, minChunkZ);
|
||||
final int maxX = Math.min(highEdgeX, maxChunkX);
|
||||
final int maxZ = Math.min(highEdgeZ, maxChunkZ);
|
||||
final int sizeX = maxX - minX + 1;
|
||||
if (minX > highEdgeX || minZ > highEdgeZ || maxX < lowEdgeX || maxZ < lowEdgeZ) {
|
||||
this.sizeZ = 0;
|
||||
this.total = 0;
|
||||
this.full = false;
|
||||
this.hasNext = false;
|
||||
} else {
|
||||
this.sizeZ = maxZ - minZ + 1;
|
||||
this.total = sizeX * sizeZ;
|
||||
this.full = total == 1024;
|
||||
}
|
||||
this.name = "region_chunk_progress_%d_%d".formatted(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord;
|
||||
if (full) {
|
||||
final ChunkCoordinate offset = Hilbert.regionDistanceToChunkCoordinateOffset(current);
|
||||
chunkCoord = new ChunkCoordinate(minX + offset.x(), minZ + offset.z());
|
||||
} else {
|
||||
chunkCoord = new ChunkCoordinate(minX + offsetX, minZ + offsetZ);
|
||||
if (++offsetZ >= sizeZ) {
|
||||
offsetZ = 0;
|
||||
++offsetX;
|
||||
}
|
||||
}
|
||||
++current;
|
||||
if (current >= total) {
|
||||
hasNext = false;
|
||||
}
|
||||
return chunkCoord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.popcraft.chunky.iterator;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class SpiralChunkIterator implements ChunkIterator {
|
||||
private static final int RIGHT = 0, DOWN = 1, LEFT = 2, UP = 3;
|
||||
private final int stopX, stopZ;
|
||||
@@ -12,7 +14,7 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
private int direction;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public SpiralChunkIterator(Selection selection, long count) {
|
||||
public SpiralChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
@@ -24,7 +26,7 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
this.span = diameterFinished;
|
||||
this.spanCount = 1;
|
||||
this.direction = DOWN;
|
||||
int radiusFinished = diameterFinished / 2;
|
||||
final int radiusFinished = diameterFinished / 2;
|
||||
this.x += radiusFinished + 1;
|
||||
this.z += radiusFinished;
|
||||
long perimeterCount = count - (long) diameterFinished * diameterFinished;
|
||||
@@ -71,13 +73,13 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public SpiralChunkIterator(Selection selection) {
|
||||
int radiusChunks = selection.radiusChunksX();
|
||||
public SpiralChunkIterator(final Selection selection) {
|
||||
final int radiusChunks = selection.radiusChunksX();
|
||||
this.x = selection.centerChunkX();
|
||||
this.z = selection.centerChunkZ();
|
||||
this.stopX = x + radiusChunks;
|
||||
this.stopZ = z + radiusChunks;
|
||||
long diameter = selection.diameterChunksX();
|
||||
final long diameter = selection.diameterChunksX();
|
||||
this.total = diameter * diameter;
|
||||
}
|
||||
|
||||
@@ -88,6 +90,9 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (x == stopX && z == stopZ) {
|
||||
hasNext = false;
|
||||
@@ -96,21 +101,16 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
spanCount = 0;
|
||||
++span;
|
||||
}
|
||||
switch (direction) {
|
||||
case RIGHT:
|
||||
x += 1;
|
||||
break;
|
||||
case DOWN:
|
||||
z -= 1;
|
||||
break;
|
||||
case LEFT:
|
||||
x -= 1;
|
||||
break;
|
||||
case UP:
|
||||
z += 1;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid direction");
|
||||
if (direction == RIGHT) {
|
||||
x += 1;
|
||||
} else if (direction == DOWN) {
|
||||
z -= 1;
|
||||
} else if (direction == LEFT) {
|
||||
x -= 1;
|
||||
} else if (direction == UP) {
|
||||
z += 1;
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid direction");
|
||||
}
|
||||
++spanProgress;
|
||||
if (spanProgress == span) {
|
||||
@@ -128,6 +128,6 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "spiral";
|
||||
return PatternType.SPIRAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.nbt.StringTag;
|
||||
import org.popcraft.chunky.nbt.TagType;
|
||||
import org.popcraft.chunky.nbt.util.ChunkFilter;
|
||||
import org.popcraft.chunky.nbt.util.RegionFile;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Hilbert;
|
||||
import org.popcraft.chunky.util.Parameter;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WorldChunkIterator implements ChunkIterator {
|
||||
private final Chunky chunky;
|
||||
private final int minRegionX;
|
||||
private final int minRegionZ;
|
||||
private final int maxRegionX;
|
||||
private final int maxRegionZ;
|
||||
private final int minChunkX;
|
||||
private final int minChunkZ;
|
||||
private final int maxChunkX;
|
||||
private final int maxChunkZ;
|
||||
private final String worldName;
|
||||
private final Queue<ChunkCoordinate> chunks;
|
||||
private final AtomicLong total = new AtomicLong();
|
||||
private final Path savePath;
|
||||
private final Path regionPath;
|
||||
private final String name;
|
||||
|
||||
public WorldChunkIterator(final Selection selection) {
|
||||
this.chunky = selection.chunky();
|
||||
final int centerRegionX = selection.centerRegionX();
|
||||
final int centerRegionZ = selection.centerRegionZ();
|
||||
final int radiusRegionsX = selection.radiusRegionsX();
|
||||
final int radiusRegionsZ = selection.radiusRegionsZ();
|
||||
this.minRegionX = centerRegionX - radiusRegionsX;
|
||||
this.minRegionZ = centerRegionZ - radiusRegionsZ;
|
||||
this.maxRegionX = centerRegionX + radiusRegionsX;
|
||||
this.maxRegionZ = centerRegionZ + radiusRegionsZ;
|
||||
final int centerChunkX = selection.centerChunkX();
|
||||
final int centerChunkZ = selection.centerChunkZ();
|
||||
final int radiusChunksX = selection.radiusChunksX();
|
||||
final int radiusChunksZ = selection.radiusChunksZ();
|
||||
this.minChunkX = centerChunkX - radiusChunksX;
|
||||
this.minChunkZ = centerChunkZ - radiusChunksZ;
|
||||
this.maxChunkX = centerChunkX + radiusChunksX;
|
||||
this.maxChunkZ = centerChunkZ + radiusChunksZ;
|
||||
this.chunks = new LinkedList<>();
|
||||
this.worldName = selection.world().getName();
|
||||
final String saveFile = worldName.substring(worldName.indexOf(':') + 1);
|
||||
this.savePath = selection.chunky().getConfig().getDirectory().resolve(String.format("%s.csv", saveFile));
|
||||
this.regionPath = selection.world().getRegionDirectory().orElse(null);
|
||||
this.name = Parameter.of(PatternType.CSV, saveFile).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !chunks.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (chunks.isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return chunks.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process() {
|
||||
if (regionPath == null) {
|
||||
return false;
|
||||
}
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final AtomicLong updateTime = new AtomicLong(startTime);
|
||||
final StringBuilder saveData = new StringBuilder();
|
||||
try (final Stream<Path> files = Files.list(regionPath)) {
|
||||
final List<Path> regions = files
|
||||
.filter(file -> {
|
||||
final ChunkCoordinate regionCoordinate = ChunkCoordinate.fromRegionFile(file.getFileName().toString())
|
||||
.orElse(null);
|
||||
return regionCoordinate != null && regionCoordinate.x() >= minRegionX && regionCoordinate.x() <= maxRegionX && regionCoordinate.z() >= minRegionZ && regionCoordinate.z() <= maxRegionZ;
|
||||
})
|
||||
.toList();
|
||||
final long totalRegions = regions.size();
|
||||
final AtomicLong finishedRegions = new AtomicLong();
|
||||
for (final Path region : regions) {
|
||||
final ChunkCoordinate regionCoordinate = ChunkCoordinate.fromRegionFile(region.getFileName().toString())
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
final int regionX = regionCoordinate.x();
|
||||
final int regionZ = regionCoordinate.z();
|
||||
final RegionFile regionFile = new RegionFile(region.toFile(), ChunkFilter.of(TagType.STRING, "Status"));
|
||||
for (final ChunkCoordinate offset : Hilbert.chunkCoordinateOffsets()) {
|
||||
final ChunkCoordinate chunkCoordinate = new ChunkCoordinate((regionX << 5) + offset.x(), (regionZ << 5) + offset.z());
|
||||
if (chunkCoordinate.x() < minChunkX || chunkCoordinate.x() > maxChunkX || chunkCoordinate.z() < minChunkZ || chunkCoordinate.z() > maxChunkZ) {
|
||||
continue;
|
||||
}
|
||||
regionFile.getChunk(chunkCoordinate.x(), chunkCoordinate.z()).ifPresent(chunk -> {
|
||||
final boolean generated = Optional.ofNullable(chunk.getData())
|
||||
.filter(StringTag.class::isInstance)
|
||||
.map(StringTag.class::cast)
|
||||
.map(StringTag::value)
|
||||
.map(status -> "minecraft:full".equals(status) || "full".equals(status))
|
||||
.orElse(false);
|
||||
if (generated) {
|
||||
chunks.add(chunkCoordinate);
|
||||
saveData.append(chunkCoordinate.x()).append(',').append(chunkCoordinate.z()).append('\n');
|
||||
total.incrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
finishedRegions.getAndIncrement();
|
||||
if (!chunky.getConfig().isSilent()) {
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final boolean updateIntervalElapsed = ((currentTime - updateTime.get()) / 1e3) > chunky.getConfig().getUpdateInterval();
|
||||
if (updateIntervalElapsed || finishedRegions.get() == totalRegions) {
|
||||
chunky.getServer().getConsole().sendMessagePrefixed(TranslationKey.TASK_TRIM_UPDATE, worldName, finishedRegions.get(), String.format("%.2f", 100f * finishedRegions.get() / totalRegions));
|
||||
updateTime.set(currentTime);
|
||||
}
|
||||
}
|
||||
if (Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Files.writeString(savePath, saveData, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public interface Border {
|
||||
Coordinate getCenter();
|
||||
Vector2 getCenter();
|
||||
|
||||
double getRadiusX();
|
||||
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface Config {
|
||||
Path getDirectory();
|
||||
|
||||
Optional<GenerationTask> loadTask(World world);
|
||||
|
||||
List<GenerationTask> loadTasks();
|
||||
|
||||
void saveTask(GenerationTask generationTask);
|
||||
|
||||
void saveTasks();
|
||||
|
||||
void cancelTask(World world);
|
||||
|
||||
void cancelTasks();
|
||||
|
||||
int getVersion();
|
||||
|
||||
String getLanguage();
|
||||
|
||||
boolean getContinueOnRestart();
|
||||
|
||||
boolean isForceLoadExistingChunks();
|
||||
|
||||
boolean isSilent();
|
||||
|
||||
void setSilent(boolean silent);
|
||||
|
||||
int getUpdateInterval();
|
||||
|
||||
void setUpdateInterval(int updateInterval);
|
||||
|
||||
void reload();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Player extends Sender {
|
||||
UUID getUUID();
|
||||
|
||||
void teleport(Location location);
|
||||
|
||||
void sendActionBar(String key);
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
public interface Sender {
|
||||
boolean isPlayer();
|
||||
|
||||
String getName();
|
||||
|
||||
Coordinate getCoordinate();
|
||||
World getWorld();
|
||||
|
||||
Location getLocation();
|
||||
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
void sendMessage(String key, boolean prefixed, Object... args);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.integration.Integration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -13,7 +14,13 @@ public interface Server {
|
||||
|
||||
List<World> getWorlds();
|
||||
|
||||
Sender getConsoleSender();
|
||||
int getMaxWorldSize();
|
||||
|
||||
Sender getConsole();
|
||||
|
||||
Collection<Player> getPlayers();
|
||||
|
||||
Optional<Player> getPlayer(String name);
|
||||
|
||||
Config getConfig();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
@@ -10,7 +10,9 @@ import java.util.concurrent.CompletableFuture;
|
||||
public interface World {
|
||||
String getName();
|
||||
|
||||
boolean isChunkGenerated(int x, int z);
|
||||
String getKey();
|
||||
|
||||
CompletableFuture<Boolean> isChunkGenerated(int x, int z);
|
||||
|
||||
CompletableFuture<Void> getChunkAtAsync(int x, int z);
|
||||
|
||||
@@ -18,13 +20,29 @@ public interface World {
|
||||
|
||||
int getSeaLevel();
|
||||
|
||||
Coordinate getSpawnCoordinate();
|
||||
Location getSpawn();
|
||||
|
||||
Border getWorldBorder();
|
||||
|
||||
Optional<Path> getEntitiesDirectory();
|
||||
int getElevation(int x, int z);
|
||||
|
||||
Optional<Path> getPOIDirectory();
|
||||
int getMaxElevation();
|
||||
|
||||
Optional<Path> getRegionDirectory();
|
||||
void playEffect(Player player, String effect);
|
||||
|
||||
void playSound(Player player, String sound);
|
||||
|
||||
Optional<Path> getDirectory(String name);
|
||||
|
||||
default Optional<Path> getEntitiesDirectory() {
|
||||
return getDirectory("entities");
|
||||
}
|
||||
|
||||
default Optional<Path> getPOIDirectory() {
|
||||
return getDirectory("poi");
|
||||
}
|
||||
|
||||
default Optional<Path> getRegionDirectory() {
|
||||
return getDirectory("region");
|
||||
}
|
||||
}
|
||||
@@ -2,199 +2,249 @@ package org.popcraft.chunky.platform.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.Config;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GsonConfig implements Config {
|
||||
private final Supplier<Chunky> chunky;
|
||||
private final Gson gson;
|
||||
private final Path configPath;
|
||||
private ConfigModel configModel;
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private final Path savePath;
|
||||
private ConfigModel configModel = new ConfigModel();
|
||||
|
||||
public GsonConfig(Supplier<Chunky> chunky, File configFile) {
|
||||
this.chunky = chunky;
|
||||
this.gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
this.configPath = configFile.toPath();
|
||||
if (!configFile.exists()) {
|
||||
try {
|
||||
if (configFile.createNewFile()) {
|
||||
this.configModel = new ConfigModel();
|
||||
configModel.version = 1;
|
||||
configModel.language = "en";
|
||||
configModel.continueOnRestart = false;
|
||||
saveConfig();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
public GsonConfig(final Path savePath) {
|
||||
this.savePath = savePath;
|
||||
if (Files.exists(this.savePath)) {
|
||||
reload();
|
||||
} else {
|
||||
saveConfig();
|
||||
}
|
||||
Translator.setLanguage(getLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDirectory() {
|
||||
return configPath.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<GenerationTask> loadTask(World world) {
|
||||
if (this.configModel == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Map<String, TaskModel> tasks = this.configModel.tasks;
|
||||
if (tasks == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TaskModel taskModel = tasks.get(world.getName());
|
||||
if (taskModel == null || taskModel.cancelled) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Selection.Builder selection = Selection.builder(world)
|
||||
.centerX(taskModel.centerX)
|
||||
.centerZ(taskModel.centerZ)
|
||||
.radiusX(taskModel.radius)
|
||||
.radiusZ(taskModel.radiusZ == null ? taskModel.radius : taskModel.radiusZ)
|
||||
.pattern(taskModel.iterator)
|
||||
.shape(taskModel.shape);
|
||||
long count = taskModel.count;
|
||||
long time = taskModel.time;
|
||||
return Optional.of(new GenerationTask(chunky.get(), selection.build(), count, time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GenerationTask> loadTasks() {
|
||||
List<GenerationTask> generationTasks = new ArrayList<>();
|
||||
chunky.get().getServer().getWorlds().forEach(world -> loadTask(world).ifPresent(generationTasks::add));
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTask(GenerationTask generationTask) {
|
||||
if (this.configModel == null) {
|
||||
this.configModel = new ConfigModel();
|
||||
}
|
||||
if (this.configModel.tasks == null) {
|
||||
this.configModel.tasks = new HashMap<>();
|
||||
}
|
||||
Map<String, TaskModel> tasks = this.configModel.tasks;
|
||||
Selection selection = generationTask.getSelection();
|
||||
TaskModel taskModel = tasks.getOrDefault(selection.world().getName(), new TaskModel());
|
||||
String shape = generationTask.getShape().name();
|
||||
taskModel.cancelled = generationTask.isCancelled();
|
||||
taskModel.radius = selection.radiusX();
|
||||
if ("rectangle".equals(shape) || "ellipse".equals(shape)) {
|
||||
taskModel.radiusZ = selection.radiusZ();
|
||||
}
|
||||
taskModel.centerX = selection.centerX();
|
||||
taskModel.centerZ = selection.centerZ();
|
||||
taskModel.iterator = generationTask.getChunkIterator().name();
|
||||
taskModel.shape = shape;
|
||||
taskModel.count = generationTask.getCount();
|
||||
taskModel.time = generationTask.getTotalTime();
|
||||
tasks.put(selection.world().getName(), taskModel);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTasks() {
|
||||
chunky.get().getGenerationTasks().values().forEach(this::saveTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTask(World world) {
|
||||
loadTask(world).ifPresent(generationTask -> {
|
||||
generationTask.stop(true);
|
||||
saveTask(generationTask);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks() {
|
||||
loadTasks().forEach(generationTask -> {
|
||||
generationTask.stop(true);
|
||||
saveTask(generationTask);
|
||||
});
|
||||
return savePath.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return getConfigModel().map(model -> model.version).orElse(0);
|
||||
return Optional.ofNullable(configModel.version).orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return getConfigModel().map(model -> Input.checkLanguage(model.language)).orElse("en");
|
||||
return Optional.ofNullable(configModel.language).map(Input::checkLanguage).orElse("en");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getContinueOnRestart() {
|
||||
return getConfigModel().map(model -> model.continueOnRestart).orElse(false);
|
||||
return Optional.ofNullable(configModel.continueOnRestart).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForceLoadExistingChunks() {
|
||||
return Optional.ofNullable(configModel.forceLoadExistingChunks).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSilent() {
|
||||
return Optional.ofNullable(configModel.silent).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSilent(final boolean silent) {
|
||||
configModel.silent = silent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval() {
|
||||
return Optional.ofNullable(configModel.updateInterval).orElse(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateInterval(final int updateInterval) {
|
||||
configModel.updateInterval = updateInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
StringBuilder configBuilder = new StringBuilder();
|
||||
try (Stream<String> input = Files.lines(configPath)) {
|
||||
input.forEach(configBuilder::append);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
configModel = gson.fromJson(configBuilder.toString(), new TypeToken<ConfigModel>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
public void saveConfig() {
|
||||
try (Writer writer = new BufferedWriter(new FileWriter(configPath.toFile()))) {
|
||||
gson.toJson(configModel, new TypeToken<ConfigModel>() {
|
||||
}.getType(), writer);
|
||||
try (final Reader reader = Files.newBufferedReader(savePath)) {
|
||||
configModel = GSON.fromJson(reader, ConfigModel.class);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ConfigModel> getConfigModel() {
|
||||
return Optional.ofNullable(configModel);
|
||||
private void saveConfig() {
|
||||
try {
|
||||
Files.createDirectories(savePath.getParent());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try (final Writer writer = Files.newBufferedWriter(savePath)) {
|
||||
GSON.toJson(configModel, writer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfigModel {
|
||||
public Integer version;
|
||||
public String language;
|
||||
public Boolean continueOnRestart;
|
||||
public Map<String, TaskModel> tasks;
|
||||
@SuppressWarnings("unused")
|
||||
private static class ConfigModel {
|
||||
private Integer version = 2;
|
||||
private String language = "en";
|
||||
private Boolean continueOnRestart = false;
|
||||
private Boolean forceLoadExistingChunks = false;
|
||||
private Boolean silent = false;
|
||||
private Integer updateInterval = 1;
|
||||
private Map<String, TaskModel> tasks;
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(final Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(final String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public Boolean getContinueOnRestart() {
|
||||
return continueOnRestart;
|
||||
}
|
||||
|
||||
public void setContinueOnRestart(final Boolean continueOnRestart) {
|
||||
this.continueOnRestart = continueOnRestart;
|
||||
}
|
||||
|
||||
public Boolean getForceLoadExistingChunks() {
|
||||
return forceLoadExistingChunks;
|
||||
}
|
||||
|
||||
public void setForceLoadExistingChunks(final Boolean forceLoadExistingChunks) {
|
||||
this.forceLoadExistingChunks = forceLoadExistingChunks;
|
||||
}
|
||||
|
||||
public Map<String, TaskModel> getTasks() {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public void setTasks(final Map<String, TaskModel> tasks) {
|
||||
this.tasks = tasks;
|
||||
}
|
||||
|
||||
public boolean isSilent() {
|
||||
return silent;
|
||||
}
|
||||
|
||||
public void setSilent(final boolean silent) {
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
public int getUpdateInterval() {
|
||||
return updateInterval;
|
||||
}
|
||||
|
||||
public void setUpdateInterval(final int updateInterval) {
|
||||
this.updateInterval = updateInterval;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TaskModel {
|
||||
public Boolean cancelled;
|
||||
public Double radius;
|
||||
public Double radiusZ;
|
||||
public Double centerX;
|
||||
public Double centerZ;
|
||||
public String iterator;
|
||||
public String shape;
|
||||
public Long count;
|
||||
public Long time;
|
||||
@SuppressWarnings("unused")
|
||||
private static class TaskModel {
|
||||
private Boolean cancelled;
|
||||
private Double radius;
|
||||
private Double radiusZ;
|
||||
private Double centerX;
|
||||
private Double centerZ;
|
||||
private String iterator;
|
||||
private String shape;
|
||||
private Long count;
|
||||
private Long time;
|
||||
|
||||
public Boolean getCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
public void setCancelled(final Boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public Double getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public void setRadius(final Double radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
public Double getRadiusZ() {
|
||||
return radiusZ;
|
||||
}
|
||||
|
||||
public void setRadiusZ(final Double radiusZ) {
|
||||
this.radiusZ = radiusZ;
|
||||
}
|
||||
|
||||
public Double getCenterX() {
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public void setCenterX(final Double centerX) {
|
||||
this.centerX = centerX;
|
||||
}
|
||||
|
||||
public Double getCenterZ() {
|
||||
return centerZ;
|
||||
}
|
||||
|
||||
public void setCenterZ(final Double centerZ) {
|
||||
this.centerZ = centerZ;
|
||||
}
|
||||
|
||||
public String getIterator() {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
public void setIterator(final String iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public String getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
public void setShape(final String shape) {
|
||||
this.shape = shape;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(final Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(final Long time) {
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.popcraft.chunky.platform.util;
|
||||
|
||||
import org.popcraft.chunky.platform.World;
|
||||
|
||||
public class Location {
|
||||
private World world;
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
private float yaw;
|
||||
private float pitch;
|
||||
|
||||
public Location(final World world, final double x, final double y, final double z, final float yaw, final float pitch) {
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public Location(final World world, final double x, final double y, final double z) {
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector3 toVector() {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
public Location add(final Vector3 vector) {
|
||||
x += vector.getX();
|
||||
y += vector.getY();
|
||||
z += vector.getZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Location setDirection(final Vector3 direction) {
|
||||
final double dirX = direction.getX();
|
||||
final double dirY = direction.getY();
|
||||
final double dirZ = direction.getZ();
|
||||
if (dirX == 0 && dirZ == 0) {
|
||||
if (dirY == 0) {
|
||||
pitch = 0;
|
||||
} else {
|
||||
pitch = dirY < 0 ? 90 : -90;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
yaw = (float) Math.toDegrees(Math.atan2(-dirX, dirZ));
|
||||
pitch = (float) Math.toDegrees(-Math.atan(dirY / Math.sqrt(dirX * dirX + dirZ * dirZ)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public void setWorld(final World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(final double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(final double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(final double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public void setYaw(final float yaw) {
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public void setPitch(final float pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.popcraft.chunky.platform.util;
|
||||
|
||||
public class Vector2 {
|
||||
private double x;
|
||||
private double z;
|
||||
|
||||
public Vector2(final double x, final double z) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public static Vector2 of(final double x, final double z) {
|
||||
return new Vector2(x, z);
|
||||
}
|
||||
|
||||
public Vector2 add(final Vector2 other) {
|
||||
x += other.x;
|
||||
z += other.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector2 multiply(final double value) {
|
||||
x *= value;
|
||||
z *= value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector2 normalize() {
|
||||
final double length = length();
|
||||
x /= length;
|
||||
z /= length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double distance(final Vector2 other) {
|
||||
return Math.sqrt(distanceSquared(other));
|
||||
}
|
||||
|
||||
public double distanceSquared(final Vector2 other) {
|
||||
final double dx = this.x - other.x;
|
||||
final double dz = this.z - other.z;
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return Math.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
public double lengthSquared() {
|
||||
return x * x + z * z;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(final double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(final double z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.popcraft.chunky.platform.util;
|
||||
|
||||
public class Vector3 {
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
|
||||
public Vector3(final double x, final double y, final double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public static Vector3 of(final double x, final double y, final double z) {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
public Vector3 add(final Vector3 other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 multiply(final double value) {
|
||||
x *= value;
|
||||
y *= value;
|
||||
z *= value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vector3 normalize() {
|
||||
final double length = length();
|
||||
x /= length;
|
||||
y /= length;
|
||||
z /= length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double distance(final Vector3 other) {
|
||||
return Math.sqrt(distanceSquared(other));
|
||||
}
|
||||
|
||||
public double distanceSquared(final Vector3 other) {
|
||||
final double dx = this.x - other.x;
|
||||
final double dy = this.y - other.y;
|
||||
final double dz = this.z - other.z;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
public double length() {
|
||||
return Math.sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
public double lengthSquared() {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(final double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(final double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(final double z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public abstract class AbstractEllipse extends AbstractShape {
|
||||
protected AbstractEllipse(Selection selection, boolean chunkAligned) {
|
||||
protected AbstractEllipse(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
public double[] getRadii() {
|
||||
return new double[]{radiusX, radiusZ};
|
||||
}
|
||||
public abstract Vector2 radii();
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractPolygon extends AbstractShape {
|
||||
protected AbstractPolygon(Selection selection, boolean chunkAligned) {
|
||||
protected AbstractPolygon(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
public abstract double[] pointsX();
|
||||
|
||||
public abstract double[] pointsZ();
|
||||
public abstract List<Vector2> points();
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public abstract class AbstractShape implements Shape {
|
||||
protected final double centerX, centerZ;
|
||||
protected final double diameterX, diameterZ;
|
||||
protected final double radiusX, radiusZ;
|
||||
|
||||
protected AbstractShape(Selection selection, boolean chunkAligned) {
|
||||
protected AbstractShape(final Selection selection, final boolean chunkAligned) {
|
||||
if (chunkAligned) {
|
||||
this.centerX = (double) (selection.centerChunkX() << 4) + 8;
|
||||
this.centerZ = (double) (selection.centerChunkZ() << 4) + 8;
|
||||
@@ -25,7 +26,7 @@ public abstract class AbstractShape implements Shape {
|
||||
}
|
||||
}
|
||||
|
||||
public double[] getCenter() {
|
||||
return new double[]{centerX, centerZ};
|
||||
public Vector2 center() {
|
||||
return Vector2.of(centerX, centerZ);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public class Circle extends AbstractEllipse {
|
||||
public Circle(Selection selection, boolean chunkAligned) {
|
||||
public Circle(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
return Math.hypot(centerX - x, centerZ - z) <= radiusX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "circle";
|
||||
return ShapeType.CIRCLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 radii() {
|
||||
return Vector2.of(radiusX, radiusX);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Diamond extends AbstractPolygon {
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
public Diamond(Selection selection, boolean chunkAligned) {
|
||||
public Diamond(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX;
|
||||
this.p1z = centerZ + radiusX;
|
||||
@@ -20,17 +24,17 @@ public class Diamond extends AbstractPolygon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, p2x, p3x, p4x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(p4x, p4z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, p2z, p3z, p4z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
if (!insideLine(p1x, p1z, p2x, p2z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
@@ -45,6 +49,6 @@ public class Diamond extends AbstractPolygon {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "diamond";
|
||||
return ShapeType.DIAMOND;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public class Ellipse extends AbstractEllipse {
|
||||
public Ellipse(Selection selection, boolean chunkAligned) {
|
||||
public Ellipse(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
return (Math.pow(x - centerX, 2) / Math.pow(radiusX, 2)) + (Math.pow(z - centerZ, 2) / Math.pow(radiusZ, 2)) <= 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "ellipse";
|
||||
return ShapeType.ELLIPSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 radii() {
|
||||
return Vector2.of(radiusX, radiusZ);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Hexagon extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z, p6x, p6z;
|
||||
|
||||
public Hexagon(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX + radiusX * Math.cos(Math.toRadians(60));
|
||||
this.p1z = centerZ + radiusX * Math.sin(Math.toRadians(60));
|
||||
this.p2x = centerX + radiusX * Math.cos(Math.toRadians(120));
|
||||
this.p2z = centerZ + radiusX * Math.sin(Math.toRadians(120));
|
||||
this.p3x = centerX + radiusX * Math.cos(Math.toRadians(180));
|
||||
this.p3z = centerZ + radiusX * Math.sin(Math.toRadians(180));
|
||||
this.p4x = centerX + radiusX * Math.cos(Math.toRadians(240));
|
||||
this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(240));
|
||||
this.p5x = centerX + radiusX * Math.cos(Math.toRadians(300));
|
||||
this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(300));
|
||||
this.p6x = centerX + radiusX * Math.cos(Math.toRadians(360));
|
||||
this.p6z = centerZ + radiusX * Math.sin(Math.toRadians(360));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(p4x, p4z),
|
||||
Vector2.of(p5x, p5z),
|
||||
Vector2.of(p6x, p6z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
final boolean inside12 = insideLine(p1x, p1z, p2x, p2z, x, z);
|
||||
final boolean inside23 = insideLine(p2x, p2z, p3x, p3z, x, z);
|
||||
final boolean inside34 = insideLine(p3x, p3z, p4x, p4z, x, z);
|
||||
final boolean inside45 = insideLine(p4x, p4z, p5x, p5z, x, z);
|
||||
final boolean inside56 = insideLine(p5x, p5z, p6x, p6z, x, z);
|
||||
final boolean inside61 = insideLine(p6x, p6z, p1x, p1z, x, z);
|
||||
return inside12 && inside23 && inside34 && inside45 && inside56 && inside61;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.HEXAGON;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Pentagon extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z;
|
||||
|
||||
public Pentagon(Selection selection, boolean chunkAligned) {
|
||||
public Pentagon(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX + radiusX * Math.cos(Math.toRadians(54));
|
||||
this.p1z = centerZ + radiusX * Math.sin(Math.toRadians(54));
|
||||
@@ -22,27 +26,28 @@ public class Pentagon extends AbstractPolygon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, p2x, p3x, p4x, p5x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(p4x, p4z),
|
||||
Vector2.of(p5x, p5z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, p2z, p3z, p4z, p5z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
boolean inside12 = insideLine(p1x, p1z, p2x, p2z, x, z);
|
||||
boolean inside23 = insideLine(p2x, p2z, p3x, p3z, x, z);
|
||||
boolean inside34 = insideLine(p3x, p3z, p4x, p4z, x, z);
|
||||
boolean inside45 = insideLine(p4x, p4z, p5x, p5z, x, z);
|
||||
boolean inside51 = insideLine(p5x, p5z, p1x, p1z, x, z);
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
final boolean inside12 = insideLine(p1x, p1z, p2x, p2z, x, z);
|
||||
final boolean inside23 = insideLine(p2x, p2z, p3x, p3z, x, z);
|
||||
final boolean inside34 = insideLine(p3x, p3z, p4x, p4z, x, z);
|
||||
final boolean inside45 = insideLine(p4x, p4z, p5x, p5z, x, z);
|
||||
final boolean inside51 = insideLine(p5x, p5z, p1x, p1z, x, z);
|
||||
return inside12 && inside23 && inside34 && inside45 && inside51;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "pentagon";
|
||||
return ShapeType.PENTAGON;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Rectangle extends AbstractPolygon {
|
||||
final double b1x, b1z, b2x, b2z;
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
protected Rectangle(Selection selection, boolean chunkAligned) {
|
||||
protected Rectangle(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.b1x = centerX - radiusX;
|
||||
this.b1z = centerZ - radiusZ;
|
||||
@@ -23,22 +27,22 @@ public class Rectangle extends AbstractPolygon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, p2x, p3x, p4x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(p4x, p4z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, p2z, p3z, p4z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
return x >= b1x && x <= b2x && z >= b1z && z <= b2z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "rectangle";
|
||||
return ShapeType.RECTANGLE;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,43 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
public class ShapeFactory {
|
||||
public static Shape getShape(Selection selection) {
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public final class ShapeFactory {
|
||||
private static final Map<String, BiFunction<Selection, Boolean, Shape>> custom = new HashMap<>();
|
||||
|
||||
private ShapeFactory() {
|
||||
}
|
||||
|
||||
public static Shape getShape(final Selection selection) {
|
||||
return getShape(selection, true);
|
||||
}
|
||||
|
||||
public static Shape getShape(Selection selection, boolean chunkAligned) {
|
||||
switch (selection.shape()) {
|
||||
case "circle":
|
||||
return new Circle(selection, chunkAligned);
|
||||
case "diamond":
|
||||
return new Diamond(selection, chunkAligned);
|
||||
case "oval":
|
||||
case "ellipse":
|
||||
return new Ellipse(selection, chunkAligned);
|
||||
case "pentagon":
|
||||
return new Pentagon(selection, chunkAligned);
|
||||
case "rectangle":
|
||||
return new Rectangle(selection, chunkAligned);
|
||||
case "star":
|
||||
return new Star(selection, chunkAligned);
|
||||
case "triangle":
|
||||
return new Triangle(selection, chunkAligned);
|
||||
case "square":
|
||||
default:
|
||||
return new Square(selection, chunkAligned);
|
||||
}
|
||||
public static Shape getShape(final Selection selection, final boolean chunkAligned) {
|
||||
return switch (selection.shape()) {
|
||||
case ShapeType.CIRCLE -> new Circle(selection, chunkAligned);
|
||||
case ShapeType.DIAMOND -> new Diamond(selection, chunkAligned);
|
||||
case ShapeType.ELLIPSE, ShapeType.OVAL -> new Ellipse(selection, chunkAligned);
|
||||
case ShapeType.HEXAGON -> new Hexagon(selection, chunkAligned);
|
||||
case ShapeType.PENTAGON -> new Pentagon(selection, chunkAligned);
|
||||
case ShapeType.RECTANGLE -> new Rectangle(selection, chunkAligned);
|
||||
case ShapeType.STAR -> new Star(selection, chunkAligned);
|
||||
case ShapeType.TRIANGLE -> new Triangle(selection, chunkAligned);
|
||||
default -> custom.getOrDefault(selection.shape(), Square::new).apply(selection, chunkAligned);
|
||||
};
|
||||
}
|
||||
|
||||
public static void registerCustom(final String name, final BiFunction<Selection, Boolean, Shape> shapeFunction) {
|
||||
custom.put(name, shapeFunction);
|
||||
Translator.addCustomTranslation("shape_%s".formatted(name), name);
|
||||
}
|
||||
|
||||
public static Set<String> getCustomTypes() {
|
||||
return custom.keySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ShapeType {
|
||||
public static final String CIRCLE = "circle";
|
||||
public static final String DIAMOND = "diamond";
|
||||
public static final String ELLIPSE = "ellipse";
|
||||
public static final String HEXAGON = "hexagon";
|
||||
public static final String OVAL = "oval";
|
||||
public static final String PENTAGON = "pentagon";
|
||||
public static final String RECTANGLE = "rectangle";
|
||||
public static final String SQUARE = "square";
|
||||
public static final String STAR = "star";
|
||||
public static final String TRIANGLE = "triangle";
|
||||
|
||||
private static final List<String> DEFAULTS = List.of(CIRCLE, DIAMOND, ELLIPSE, HEXAGON, PENTAGON, RECTANGLE, SQUARE, STAR, TRIANGLE);
|
||||
|
||||
private ShapeType() {
|
||||
}
|
||||
|
||||
public static List<String> all() {
|
||||
final Set<String> customTypes = ShapeFactory.getCustomTypes();
|
||||
if (customTypes.isEmpty()) {
|
||||
return DEFAULTS;
|
||||
}
|
||||
final List<String> allTypes = new ArrayList<>(DEFAULTS);
|
||||
allTypes.addAll(customTypes);
|
||||
return allTypes;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ShapeUtil {
|
||||
public final class ShapeUtil {
|
||||
private ShapeUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a point C is inside (to the left of) the line defined by point A and B.
|
||||
*
|
||||
@@ -14,7 +19,7 @@ public class ShapeUtil {
|
||||
* @param cz Point C z
|
||||
* @return Whether point C can be considered inside of line AB.
|
||||
*/
|
||||
public static boolean insideLine(double ax, double az, double bx, double bz, double cx, double cz) {
|
||||
public static boolean insideLine(final double ax, final double az, final double bx, final double bz, final double cx, final double cz) {
|
||||
// Compute whether the point is inside the line using a cross product
|
||||
return (bx - ax) * (cz - az) > (bz - az) * (cx - ax);
|
||||
}
|
||||
@@ -22,49 +27,31 @@ public class ShapeUtil {
|
||||
/**
|
||||
* Given two lines defined by two points each respectively, return the intersection point, if any.
|
||||
*
|
||||
* @param l1p1x Line 1 point 1 x
|
||||
* @param l1p1z Line 1 point 1 z
|
||||
* @param l1p2x Line 1 point 2 x
|
||||
* @param l1p2z Line 1 point 2 z
|
||||
* @param l2p1x Line 2 point 1 x
|
||||
* @param l2p1z Line 2 point 1 z
|
||||
* @param l2p2x Line 2 point 2 x
|
||||
* @param l2p2z Line 2 point 2 z
|
||||
* @param l1x1 Line 1 point 1 x
|
||||
* @param l1z1 Line 1 point 1 z
|
||||
* @param l1x2 Line 1 point 2 x
|
||||
* @param l1z2 Line 1 point 2 z
|
||||
* @param l2x1 Line 2 point 1 x
|
||||
* @param l2z1 Line 2 point 1 z
|
||||
* @param l2x2 Line 2 point 2 x
|
||||
* @param l2z2 Line 2 point 2 z
|
||||
* @return An optional containing the intersection point, or empty if no intersection.
|
||||
*/
|
||||
public static Optional<double[]> intersection(double l1p1x, double l1p1z, double l1p2x, double l1p2z, double l2p1x, double l2p1z, double l2p2x, double l2p2z) {
|
||||
// Compute delta x for each line
|
||||
double l1dx = l1p2x - l1p1x, l2dx = l2p2x - l2p1x;
|
||||
// If both are equal to zero, the lines do not intersect at a point
|
||||
if (l1dx == 0 && l2dx == 0) {
|
||||
public static Optional<Vector2> intersection(final double l1x1, final double l1z1, final double l1x2, final double l1z2, final double l2x1, final double l2z1, final double l2x2, final double l2z2) {
|
||||
final double a1 = l1z2 - l1z1;
|
||||
final double a2 = l2z2 - l2z1;
|
||||
final double b1 = l1x1 - l1x2;
|
||||
final double b2 = l2x1 - l2x2;
|
||||
final double determinant = a1 * b2 - a2 * b1;
|
||||
if (determinant == 0) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
final double c1 = a1 * l1x1 + b1 * l1z1;
|
||||
final double c2 = a2 * l2x1 + b2 * l2z1;
|
||||
final double x = (b2 * c1 - b1 * c2) / determinant;
|
||||
final double z = (a1 * c2 - a2 * c1) / determinant;
|
||||
return Optional.of(Vector2.of(x, z));
|
||||
}
|
||||
// Compute delta z for each line
|
||||
double l1dz = l1p2z - l1p1z, l2dz = l2p2z - l2p1z;
|
||||
// Compute the slope and intercept for line 2
|
||||
double l2m = l2dz / l2dx;
|
||||
double l2b = l2p1z - (l2m * l2p1x);
|
||||
// If the slope of line 1 is zero, then the intersection can be computed directly from its x value
|
||||
if (l1dx == 0) {
|
||||
return Optional.of(new double[]{l1p1x, l2m * l1p1x + l2b});
|
||||
}
|
||||
// Compute the slope and intercept for line 1
|
||||
double l1m = l1dz / l1dx;
|
||||
double l1b = l1p1z - (l1m * l1p1x);
|
||||
// If the slope of line 2 is zero, then the intersection can be computed directly from its x value
|
||||
if (l2dx == 0) {
|
||||
return Optional.of(new double[]{l2p1x, l1m * l2p1x + l1b});
|
||||
}
|
||||
// If the slope of both lines are equal, then they do not intersect, or are the same line
|
||||
if (l1m == l2m) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// Solve the system of equations to get the intersection point
|
||||
double a1 = -l1m, c1 = -l1b;
|
||||
double a2 = -l2m, c2 = -l2b;
|
||||
double ix = (c2 - c1) / (a1 - a2);
|
||||
double iz = (a2 * c1 - a1 * c2) / (a1 - a2);
|
||||
return Optional.of(new double[]{ix, iz});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,9 +65,48 @@ public class ShapeUtil {
|
||||
* @param angle Angle in radians
|
||||
* @return The point on the ellipse.
|
||||
*/
|
||||
public static double[] pointOnEllipse(double centerX, double centerZ, double radiusX, double radiusZ, double angle) {
|
||||
double pointX = centerX + radiusX * Math.cos(angle);
|
||||
double pointZ = centerZ + radiusZ * Math.sin(angle);
|
||||
return new double[]{pointX, pointZ};
|
||||
public static Vector2 pointOnEllipse(final double centerX, final double centerZ, final double radiusX, final double radiusZ, final double angle) {
|
||||
final double x = centerX + radiusX * Math.cos(angle);
|
||||
final double z = centerZ + radiusZ * Math.sin(angle);
|
||||
return Vector2.of(x, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the closest point on a line from a given position.
|
||||
*
|
||||
* @param posX Position x
|
||||
* @param posZ Position z
|
||||
* @param p1x Line point 1 x
|
||||
* @param p1z Line point 1 z
|
||||
* @param p2x Line point 2 x
|
||||
* @param p2z Line point 2 z
|
||||
* @return The closest point to the position on the line.
|
||||
*/
|
||||
public static Vector2 closestPointOnLine(final double posX, final double posZ, final double p1x, final double p1z, final double p2x, final double p2z) {
|
||||
final double dx = p2x - p1x;
|
||||
final double dz = p2z - p1z;
|
||||
final double perpendicularSlope = -dx / dz;
|
||||
final double p3x, p3z;
|
||||
if (Double.isInfinite(perpendicularSlope)) {
|
||||
p3x = posX;
|
||||
p3z = posZ + 1;
|
||||
} else {
|
||||
p3x = posX + 1;
|
||||
p3z = posZ + perpendicularSlope;
|
||||
}
|
||||
return ShapeUtil.intersection(p1x, p1z, p2x, p2z, posX, posZ, p3x, p3z).orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the distance between 2 points.
|
||||
*
|
||||
* @param p1x Point 1 x
|
||||
* @param p1z Point 1 z
|
||||
* @param p2x Point 2 x
|
||||
* @param p2z Point 2 z
|
||||
* @return The distance between the 2 points.
|
||||
*/
|
||||
public static double distanceBetweenPoints(final double p1x, final double p1z, final double p2x, final double p2z) {
|
||||
return Math.sqrt(Math.pow(p1x - p2x, 2) + Math.pow(p1z - p2z, 2));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Square extends AbstractPolygon {
|
||||
final double b1x, b1z, b2x, b2z;
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
protected Square(Selection selection, boolean chunkAligned) {
|
||||
protected Square(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.b1x = centerX - radiusX;
|
||||
this.b1z = centerZ - radiusX;
|
||||
@@ -23,22 +27,22 @@ public class Square extends AbstractPolygon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, p2x, p3x, p4x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(p4x, p4z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, p2z, p3z, p4z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
return x >= b1x && x <= b2x && z >= b1z && z <= b2z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "square";
|
||||
return ShapeType.SQUARE;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.intersection;
|
||||
@@ -9,7 +13,7 @@ public class Star extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z;
|
||||
private final double i1x, i1z, i2x, i2z, i3x, i3z, i4x, i4z, i5x, i5z;
|
||||
|
||||
public Star(Selection selection, boolean chunkAligned) {
|
||||
public Star(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX + radiusX * Math.cos(Math.toRadians(54));
|
||||
this.p1z = centerZ + radiusX * Math.sin(Math.toRadians(54));
|
||||
@@ -21,43 +25,49 @@ public class Star extends AbstractPolygon {
|
||||
this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(270));
|
||||
this.p5x = centerX + radiusX * Math.cos(Math.toRadians(342));
|
||||
this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(342));
|
||||
double[] i1 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p5x, p5z).orElse(new double[]{p1x, p1z});
|
||||
double[] i2 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p4x, p4z).orElse(new double[]{p2x, p2z});
|
||||
double[] i3 = intersection(p2x, p2z, p4x, p4z, p3x, p3z, p5x, p5z).orElse(new double[]{p3x, p3z});
|
||||
double[] i4 = intersection(p3x, p3z, p5x, p5z, p1x, p1z, p4x, p4z).orElse(new double[]{p4x, p4z});
|
||||
double[] i5 = intersection(p1x, p1z, p4x, p4z, p2x, p2z, p5x, p5z).orElse(new double[]{p5x, p5z});
|
||||
this.i1x = i1[0];
|
||||
this.i1z = i1[1];
|
||||
this.i2x = i2[0];
|
||||
this.i2z = i2[1];
|
||||
this.i3x = i3[0];
|
||||
this.i3z = i3[1];
|
||||
this.i4x = i4[0];
|
||||
this.i4z = i4[1];
|
||||
this.i5x = i5[0];
|
||||
this.i5z = i5[1];
|
||||
final Vector2 i1 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p5x, p5z).orElse(Vector2.of(p1x, p1z));
|
||||
final Vector2 i2 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p4x, p4z).orElse(Vector2.of(p2x, p2z));
|
||||
final Vector2 i3 = intersection(p2x, p2z, p4x, p4z, p3x, p3z, p5x, p5z).orElse(Vector2.of(p3x, p3z));
|
||||
final Vector2 i4 = intersection(p3x, p3z, p5x, p5z, p1x, p1z, p4x, p4z).orElse(Vector2.of(p4x, p4z));
|
||||
final Vector2 i5 = intersection(p1x, p1z, p4x, p4z, p2x, p2z, p5x, p5z).orElse(Vector2.of(p5x, p5z));
|
||||
this.i1x = i1.getX();
|
||||
this.i1z = i1.getZ();
|
||||
this.i2x = i2.getX();
|
||||
this.i2z = i2.getZ();
|
||||
this.i3x = i3.getX();
|
||||
this.i3z = i3.getZ();
|
||||
this.i4x = i4.getX();
|
||||
this.i4z = i4.getZ();
|
||||
this.i5x = i5.getX();
|
||||
this.i5z = i5.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, i1x, p2x, i2x, p3x, i3x, p4x, i4x, p5x, i5x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(i1x, i1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(i2x, i2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(i3x, i3z),
|
||||
Vector2.of(p4x, p4z),
|
||||
Vector2.of(i4x, i4z),
|
||||
Vector2.of(p5x, p5z),
|
||||
Vector2.of(i5x, i5z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, i1z, p2z, i2z, p3z, i3z, p4z, i4z, p5z, i5z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
boolean inside13 = insideLine(p1x, p1z, p3x, p3z, x, z);
|
||||
boolean inside24 = insideLine(p2x, p2z, p4x, p4z, x, z);
|
||||
boolean inside35 = insideLine(p3x, p3z, p5x, p5z, x, z);
|
||||
boolean inside41 = insideLine(p4x, p4z, p1x, p1z, x, z);
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
final boolean inside13 = insideLine(p1x, p1z, p3x, p3z, x, z);
|
||||
final boolean inside24 = insideLine(p2x, p2z, p4x, p4z, x, z);
|
||||
final boolean inside35 = insideLine(p3x, p3z, p5x, p5z, x, z);
|
||||
final boolean inside41 = insideLine(p4x, p4z, p1x, p1z, x, z);
|
||||
if (inside13 && inside24 && inside35 && inside41) {
|
||||
return true;
|
||||
}
|
||||
boolean inside52 = insideLine(p5x, p5z, p2x, p2z, x, z);
|
||||
final boolean inside52 = insideLine(p5x, p5z, p2x, p2z, x, z);
|
||||
if (inside24 && inside35 && inside41 && inside52) {
|
||||
return true;
|
||||
}
|
||||
@@ -72,6 +82,6 @@ public class Star extends AbstractPolygon {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "star";
|
||||
return ShapeType.STAR;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Triangle extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z;
|
||||
|
||||
public Triangle(Selection selection, boolean chunkAligned) {
|
||||
public Triangle(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX + radiusX;
|
||||
this.p1z = centerZ + radiusX;
|
||||
@@ -18,17 +22,16 @@ public class Triangle extends AbstractPolygon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsX() {
|
||||
return new double[]{p1x, p2x, p3x};
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] pointsZ() {
|
||||
return new double[]{p1z, p2z, p3z};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, double z) {
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
if (!insideLine(p1x, p1z, p2x, p2z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
@@ -40,6 +43,6 @@ public class Triangle extends AbstractPolygon {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "triangle";
|
||||
return ShapeType.TRIANGLE;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,49 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
public class ChunkCoordinate implements Comparable<ChunkCoordinate> {
|
||||
public final int x, z;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public ChunkCoordinate(int x, int z) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
public record ChunkCoordinate(int x, int z) implements Comparable<ChunkCoordinate> {
|
||||
public static Optional<ChunkCoordinate> fromRegionFile(final String regionFileName) {
|
||||
if (!regionFileName.startsWith("r.")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final int extension = regionFileName.indexOf(".mca");
|
||||
if (extension < 2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final String regionCoordinates = regionFileName.substring(2, extension);
|
||||
final int separator = regionCoordinates.indexOf('.');
|
||||
final Optional<Integer> regionX = Input.tryInteger(regionCoordinates.substring(0, separator));
|
||||
final Optional<Integer> regionZ = Input.tryInteger(regionCoordinates.substring(separator + 1));
|
||||
if (regionX.isPresent() && regionZ.isPresent()) {
|
||||
return Optional.of(new ChunkCoordinate(regionX.get(), regionZ.get()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ChunkCoordinate o) {
|
||||
public int compareTo(final ChunkCoordinate o) {
|
||||
return this.x == o.x ? Integer.compare(this.z, o.z) : Integer.compare(this.x, o.x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final ChunkCoordinate that = (ChunkCoordinate) o;
|
||||
return x == that.x && z == that.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d, %d", x, z);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
public class ChunkMath {
|
||||
public static long pack(int x, int z) {
|
||||
long lx = x & 0xFFFFFFFFL;
|
||||
long lz = z & 0xFFFFFFFFL;
|
||||
public final class ChunkMath {
|
||||
private ChunkMath() {
|
||||
}
|
||||
|
||||
public static long pack(final int x, final int z) {
|
||||
final long lx = x & 0xFFFFFFFFL;
|
||||
final long lz = z & 0xFFFFFFFFL;
|
||||
return lx << 32 | lz;
|
||||
}
|
||||
|
||||
public static int regionIndex(int x, int z) {
|
||||
public static int regionIndex(final int x, final int z) {
|
||||
return (x & 0x1F) * 32 + (z & 0x1F);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
public class Coordinate {
|
||||
private double x, z;
|
||||
|
||||
public Coordinate(double x, double z) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 219 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user