Compare commits
| 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 | ||
|
|
704b4f778a | ||
|
|
595b2a7ef3 | ||
|
|
1953302756 | ||
|
|
3a27f1675e | ||
|
|
3059d23e82 | ||
|
|
34b653c4b4 | ||
|
|
6f357ff623 | ||
|
|
52de6a734a | ||
|
|
a6df5afee6 | ||
|
|
7c82c8787a | ||
|
|
8fdd26d576 | ||
|
|
5a85972030 | ||
|
|
55330fd243 | ||
|
|
a04a548b67 | ||
|
|
b6418a7cd0 | ||
|
|
c24fb2cdc0 | ||
|
|
6cca8531b3 | ||
|
|
10a866a77f | ||
|
|
fe149c73bb | ||
|
|
710789eaef | ||
|
|
002ebe3d84 | ||
|
|
0dd8e13bf0 | ||
|
|
e2a29198a7 | ||
|
|
132023f7e0 | ||
|
|
e80df2186a | ||
|
|
28f0fd7395 | ||
|
|
536375cdd9 | ||
|
|
b8bba0daa0 | ||
|
|
b0a116a480 | ||
|
|
06e400741d | ||
|
|
b6113a648a | ||
|
|
250dfe6894 | ||
|
|
becb26e2a8 | ||
|
|
7bd8124e95 | ||
|
|
90a87428df | ||
|
|
4853b93991 | ||
|
|
83ba481df0 | ||
|
|
c8fe6a8d24 | ||
|
|
4e1ad4f341 | ||
|
|
61186d0b89 | ||
|
|
95e82dbcd0 | ||
|
|
5e4cfbd942 | ||
|
|
fd6eab114d | ||
|
|
21872e1ae1 | ||
|
|
f9010efe0f | ||
|
|
167e9c47c6 | ||
|
|
92c75e5ef4 | ||
|
|
e2cc0fcccf | ||
|
|
a37ac45396 | ||
|
|
5fcdfdfb61 | ||
|
|
8508c6da82 | ||
|
|
779c8e2675 | ||
|
|
73caab174d | ||
|
|
0e594b0efe | ||
|
|
751f98d852 | ||
|
|
95eb42f55a | ||
|
|
267167f145 | ||
|
|
43ac6473c0 | ||
|
|
4ec9b22599 | ||
|
|
a244ac1ee8 | ||
|
|
6dd318add6 | ||
|
|
42b914a9db | ||
|
|
4abb47cdd5 | ||
|
|
7a86586b42 | ||
|
|
5daf5db2ef | ||
|
|
04facd1044 | ||
|
|
d5fc3795ed | ||
|
|
9b8fcfb502 | ||
|
|
bf3c024625 | ||
|
|
bc9137ee1a | ||
|
|
001de17224 | ||
|
|
168bf37911 | ||
|
|
34fda3ff7b | ||
|
|
4881a178d4 | ||
|
|
e5c8a97b95 | ||
|
|
d793193052 | ||
|
|
8435db73af | ||
|
|
95502cbc69 | ||
|
|
e7e8adb28e | ||
|
|
f18b49fb46 | ||
|
|
34ca13ac9e | ||
|
|
ce6c11e6cb | ||
|
|
2eef87ace1 | ||
|
|
7ba6178803 | ||
|
|
4ba23043ba | ||
|
|
9cdea6b108 | ||
|
|
0caba5e6f9 | ||
|
|
1608119c12 | ||
|
|
e31d1923aa | ||
|
|
2e1c833d2f | ||
|
|
160bf3f6fc | ||
|
|
d6b36ced5f | ||
|
|
f1e0fbb423 | ||
|
|
2ce1c49f57 | ||
|
|
99672423c5 | ||
|
|
14cdfd3def | ||
|
|
4db17075b8 | ||
|
|
88dfe88093 | ||
|
|
d645eeb119 | ||
|
|
50e3cf0b0a | ||
|
|
23b6c4a6f8 | ||
|
|
17a43ecfaa | ||
|
|
dce0283f66 | ||
|
|
00e8102b25 | ||
|
|
02ae8cabb0 | ||
|
|
2c25fa97c0 | ||
|
|
eaaa8a4956 | ||
|
|
dd6e4b4298 | ||
|
|
fb9f8eb1a2 | ||
|
|
6d991dac64 | ||
|
|
f707e3b258 | ||
|
|
e7128d08a3 | ||
|
|
ab7dca34dc | ||
|
|
5e7e0607e3 | ||
|
|
e75c99bd58 | ||
|
|
80f380b085 | ||
|
|
16f9aa6b32 | ||
|
|
1a42f99226 | ||
|
|
cf098ceb42 | ||
|
|
cf893e5169 | ||
|
|
289c484806 | ||
|
|
072fd2744a | ||
|
|
5e34edc21b |
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
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Build Chunky
|
||||
on: [ push, pull_request ]
|
||||
jobs:
|
||||
build:
|
||||
# Only run on PRs if the source branch is on someone else's repo
|
||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Gradle wrapper validation
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
- name: Build
|
||||
run: ./gradlew build
|
||||
- name: Upload Bukkit
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Bukkit
|
||||
path: "bukkit/build/libs/Chunky-*.jar"
|
||||
- name: Upload Fabric
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Fabric
|
||||
path: "fabric/build/libs/Chunky-*.jar"
|
||||
- name: Upload Forge
|
||||
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@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
|
||||
+193
-28
@@ -1,17 +1,166 @@
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
|
||||
*.iml
|
||||
*.ipr
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,java,gradle,intellij+all,eclipse,visualstudiocode,netbeans
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,java,gradle,intellij+all,eclipse,visualstudiocode,netbeans
|
||||
|
||||
### Eclipse ###
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
### Eclipse Patch ###
|
||||
# Spring Boot Tooling
|
||||
.sts4-cache/
|
||||
|
||||
### Intellij+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Java ###
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
@@ -21,6 +170,9 @@ atlassian-ide-plugin.xml
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
@@ -33,6 +185,7 @@ atlassian-ide-plugin.xml
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
@@ -47,6 +200,7 @@ hs_err_pid*
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
@@ -55,6 +209,7 @@ hs_err_pid*
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
@@ -74,6 +229,29 @@ Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### NetBeans ###
|
||||
**/nbproject/private/
|
||||
**/nbproject/Makefile-*.mk
|
||||
**/nbproject/Package-*.bash
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
@@ -99,35 +277,22 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Maven
|
||||
target/
|
||||
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
.flattened-pom.xml
|
||||
|
||||
# Gradle
|
||||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
**/build/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Common working directory
|
||||
run/
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### Gradle Patch ###
|
||||
**/build/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,java,gradle,intellij+all,eclipse,visualstudiocode,netbeans
|
||||
@@ -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,6 +1,26 @@
|
||||
## Chunky
|
||||
Pre-generates chunks, quickly, efficiently, and safely
|
||||
# Chunky
|
||||
|
||||
[Spigot Resource Page](https://www.spigotmc.org/resources/chunky.81534/)
|
||||

|
||||
|
||||
[Wiki](https://github.com/pop4959/Chunky/wiki/Commands)
|
||||
Minecraft chunk pre-generator, supporting a wide variety of server implementations including Spigot, Paper, Fabric, Forge, NeoForge, and Sponge.
|
||||
|
||||
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.
|
||||
@@ -1,28 +0,0 @@
|
||||
defaultTasks 'build'
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
group = 'org.popcraft'
|
||||
|
||||
project.ext.majorVersion = '1'
|
||||
project.ext.minorVersion = '2'
|
||||
project.ext.patchVersion = '0'
|
||||
project.ext.fullVersion = project.ext.majorVersion + '.' + project.ext.minorVersion + '.' + project.ext.patchVersion
|
||||
version = project.ext.fullVersion
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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.gradleup.shadow") version "8.3.9" apply false
|
||||
}
|
||||
|
||||
subprojects {
|
||||
plugins.apply("java-library")
|
||||
plugins.apply("maven-publish")
|
||||
plugins.apply("com.gradleup.shadow")
|
||||
|
||||
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(17))
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.release = 17
|
||||
options.compilerArgs.add("-Xlint:none")
|
||||
}
|
||||
|
||||
withType<AbstractArchiveTask> {
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
}
|
||||
|
||||
named<Jar>("jar") {
|
||||
archiveClassifier.set("noshade")
|
||||
}
|
||||
|
||||
named<ShadowJar>("shadowJar") {
|
||||
archiveClassifier.set("")
|
||||
archiveFileName.set("${project.property("artifactName")}-${project.version}.jar")
|
||||
isEnableRelocation = true
|
||||
relocationPrefix = "${project.group}.${rootProject.name.lowercase()}.shadow"
|
||||
}
|
||||
|
||||
named("build") {
|
||||
dependsOn("shadowJar")
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
if (hasProperty("mavenUsername") && hasProperty("mavenPassword")) {
|
||||
maven {
|
||||
credentials {
|
||||
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.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
|
||||
}
|
||||
val desc = tagDescription.toString().trim()
|
||||
return if ('-' !in desc) "0" else desc.split('-')[1]
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots'
|
||||
}
|
||||
maven {
|
||||
url 'https://papermc.io/repo/repository/maven-public'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':common')
|
||||
|
||||
compileOnly 'org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT'
|
||||
implementation 'io.papermc:paperlib:1.0.6'
|
||||
}
|
||||
|
||||
processResources {
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
expand 'projectVersion': project.ext.fullVersion
|
||||
include 'plugin.yml'
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = "Chunky-${project.ext.fullVersion}.jar"
|
||||
|
||||
relocate 'io.papermc.lib', 'org.popcraft.chunky.lib.paperlib'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
repositories {
|
||||
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.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 project.property("artifactName"),
|
||||
"version" to project.version,
|
||||
"group" to project.group,
|
||||
"author" to project.property("author"),
|
||||
"description" to project.property("description"),
|
||||
)
|
||||
}
|
||||
}
|
||||
shadowJar {
|
||||
archiveClassifier.set("")
|
||||
isEnableRelocation = false
|
||||
minimize {
|
||||
exclude(project(":Chunky-common"))
|
||||
exclude(project(":Chunky-paper"))
|
||||
exclude(project(":Chunky-folia"))
|
||||
}
|
||||
relocate("org.bstats", "${project.group}.${rootProject.name.lowercase()}.lib.bstats")
|
||||
}
|
||||
}
|
||||
@@ -1,109 +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.bukkit.scheduler.BukkitWorker;
|
||||
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.BukkitPlatform;
|
||||
import org.popcraft.chunky.platform.BukkitPlayer;
|
||||
import org.popcraft.chunky.platform.BukkitSender;
|
||||
import org.popcraft.chunky.platform.Platform;
|
||||
import org.popcraft.chunky.platform.BukkitServer;
|
||||
import org.popcraft.chunky.platform.Folia;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Metrics;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
import org.popcraft.chunky.util.Version;
|
||||
|
||||
import java.io.InputStream;
|
||||
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.Chunky.translate;
|
||||
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 BukkitPlatform(this));
|
||||
this.getConfig().options().copyDefaults(true);
|
||||
this.getConfig().options().copyHeader(true);
|
||||
this.saveConfig();
|
||||
chunky.setConfig(new BukkitConfig(chunky, this));
|
||||
InputStream configLanguage = getResource("lang/" + getConfig().getString("language", "en") + ".json");
|
||||
InputStream defaultLanguage = getResource("lang/en.json");
|
||||
if (configLanguage == null) {
|
||||
configLanguage = defaultLanguage;
|
||||
this.chunky = new Chunky(new BukkitServer(this), new BukkitConfig(this));
|
||||
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);
|
||||
}
|
||||
chunky.setTranslations(chunky.loadTranslation(configLanguage));
|
||||
chunky.setFallbackTranslations(chunky.loadTranslation(defaultLanguage));
|
||||
chunky.loadCommands();
|
||||
Metrics metrics = new Metrics(this, 8211);
|
||||
if (metrics.isEnabled()) {
|
||||
metrics.addCustomChart(new Metrics.SimplePie("language", () -> getConfig().getString("language", "en")));
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
Version currentVersion = Version.getCurrentMinecraftVersion();
|
||||
if (Version.v1_13_2.isEqualTo(currentVersion) && !PaperLib.isPaper()) {
|
||||
this.getLogger().severe(translate("error_version_spigot"));
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
} else if (Version.v1_13_2.isHigherThan(currentVersion)) {
|
||||
this.getLogger().severe(translate("error_version"));
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
Platform platform = chunky.getPlatform();
|
||||
if (this.getConfig().getBoolean("continue-on-restart", false)) {
|
||||
chunky.getCommands().get("continue").execute(platform.getServer().getConsoleSender(), new String[]{});
|
||||
getServer().getServicesManager().register(Chunky.class, chunky, this, ServicePriority.Normal);
|
||||
getServer().getServicesManager().register(ChunkyAPI.class, chunky.getApi(), this, ServicePriority.Normal);
|
||||
if (chunky.getConfig().getContinueOnRestart()) {
|
||||
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) {
|
||||
platform.getServer().getIntegrations().put("border", new WorldBorderIntegration());
|
||||
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.getConfig().saveTasks();
|
||||
chunky.getGenerationTasks().values().forEach(generationTask -> generationTask.stop(false));
|
||||
this.getServer().getScheduler().getActiveWorkers().stream()
|
||||
.filter(w -> w.getOwner() == this)
|
||||
.map(BukkitWorker::getThread)
|
||||
.forEach(Thread::interrupt);
|
||||
this.getServer().getScheduler().cancelTasks(this);
|
||||
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())) {
|
||||
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(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) {
|
||||
suggestions.addAll(commands.keySet());
|
||||
} else if (commands.containsKey(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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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.platform.util.Vector2;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public class WorldBorderIntegration implements BorderIntegration {
|
||||
@Override
|
||||
public boolean hasBorder(final String world) {
|
||||
return Config.Border(world) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Border getBorder(final String world) {
|
||||
return new Border() {
|
||||
@Override
|
||||
public Vector2 getCenter() {
|
||||
final BorderData borderData = Config.Border(world);
|
||||
return Vector2.of(borderData.getX(), borderData.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadiusX() {
|
||||
return Config.Border(world).getRadiusX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadiusZ() {
|
||||
return Config.Border(world).getRadiusZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String 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 ? ShapeType.CIRCLE : ShapeType.SQUARE;
|
||||
} else {
|
||||
shape = round ? ShapeType.ELLIPSE : ShapeType.RECTANGLE;
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,33 @@
|
||||
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 {
|
||||
WorldBorder worldBorder;
|
||||
final WorldBorder worldBorder;
|
||||
|
||||
public BukkitBorder(WorldBorder worldBorder) {
|
||||
public BukkitBorder(final WorldBorder worldBorder) {
|
||||
this.worldBorder = worldBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getCenter() {
|
||||
return new Coordinate(worldBorder.getCenter().getBlockX(), worldBorder.getCenter().getBlockZ());
|
||||
public Vector2 getCenter() {
|
||||
return Vector2.of(worldBorder.getCenter().getX(), worldBorder.getCenter().getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRadiusX() {
|
||||
return (int) worldBorder.getSize() / 2;
|
||||
public double getRadiusX() {
|
||||
return worldBorder.getSize() / 2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRadiusZ() {
|
||||
return (int) worldBorder.getSize() / 2;
|
||||
public double getRadiusZ() {
|
||||
return worldBorder.getSize() / 2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
return "square";
|
||||
return ShapeType.SQUARE;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +1,75 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.bukkit.configuration.file.FileConfigurationOptions;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BukkitConfig implements Config {
|
||||
private final Chunky chunky;
|
||||
private final JavaPlugin plugin;
|
||||
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(Chunky chunky, JavaPlugin plugin) {
|
||||
this.chunky = chunky;
|
||||
public BukkitConfig(final ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Optional<GenerationTask> loadTask(World world) {
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
if (config.getConfigurationSection(TASKS_KEY + world.getName()) == null) {
|
||||
return Optional.empty();
|
||||
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);
|
||||
}
|
||||
String world_key = TASKS_KEY + world.getName() + ".";
|
||||
if (config.getBoolean(world_key + "cancelled", false)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Selection selection = new Selection(chunky);
|
||||
selection.world = world;
|
||||
selection.radiusX = config.getInt(world_key + "radius", 500);
|
||||
selection.radiusZ = config.getInt(world_key + "z-radius", selection.radiusX);
|
||||
selection.centerX = config.getInt(world_key + "x-center", 0);
|
||||
selection.centerZ = config.getInt(world_key + "z-center", 0);
|
||||
selection.pattern = config.getString(world_key + "iterator", "loop");
|
||||
selection.shape = config.getString(world_key + "shape", "square");
|
||||
long count = config.getLong(world_key + "count", 0);
|
||||
long time = config.getLong(world_key + "time", 0);
|
||||
return Optional.of(new GenerationTask(chunky, selection, count, time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<GenerationTask> loadTasks() {
|
||||
List<GenerationTask> generationTasks = new ArrayList<>();
|
||||
chunky.getPlatform().getServer().getWorlds().forEach(world -> loadTask(world).ifPresent(generationTasks::add));
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveTask(GenerationTask generationTask) {
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
String world_key = TASKS_KEY + generationTask.getWorld().getName() + ".";
|
||||
String shape = generationTask.getShape().name();
|
||||
config.set(world_key + "cancelled", generationTask.isCancelled());
|
||||
config.set(world_key + "radius", generationTask.getRadiusX());
|
||||
if ("rectangle".equals(shape) || "oval".equals(shape)) {
|
||||
config.set(world_key + "z-radius", generationTask.getRadiusZ());
|
||||
}
|
||||
config.set(world_key + "x-center", generationTask.getCenterX());
|
||||
config.set(world_key + "z-center", generationTask.getCenterZ());
|
||||
config.set(world_key + "iterator", generationTask.getChunkIterator().name());
|
||||
config.set(world_key + "shape", shape);
|
||||
config.set(world_key + "count", generationTask.getCount());
|
||||
config.set(world_key + "time", generationTask.getTotalTime());
|
||||
plugin.saveConfig();
|
||||
Translator.setLanguage(getLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void saveTasks() {
|
||||
chunky.getGenerationTasks().values().forEach(this::saveTask);
|
||||
public Path getDirectory() {
|
||||
return plugin.getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void cancelTasks() {
|
||||
loadTasks().forEach(generationTask -> {
|
||||
generationTask.stop(true);
|
||||
saveTask(generationTask);
|
||||
});
|
||||
public int getVersion() {
|
||||
return plugin.getConfig().getInt("version", 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return Input.checkLanguage(plugin.getConfig().getString("language", "en"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getContinueOnRestart() {
|
||||
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
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
|
||||
public class BukkitPlatform implements Platform {
|
||||
private Server server;
|
||||
|
||||
public BukkitPlatform(ChunkyBukkit plugin) {
|
||||
this.server = new BukkitServer(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
@@ -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,22 +0,0 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
|
||||
public class BukkitScheduler implements Scheduler {
|
||||
private ChunkyBukkit plugin;
|
||||
|
||||
public BukkitScheduler(ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runTaskAsync(Runnable runnable) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks() {
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,35 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
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.platform.util.Location;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translateKey;
|
||||
|
||||
public class BukkitSender implements Sender {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -19,7 +39,42 @@ public class BukkitSender implements Sender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String key, Object... args) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', translate(key, args)));
|
||||
public String getName() {
|
||||
return sender.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return new BukkitWorld(Bukkit.getWorlds().get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
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,24 +1,22 @@
|
||||
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;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BukkitServer implements Server {
|
||||
private ChunkyBukkit plugin;
|
||||
private Map<String, Integration> integrations;
|
||||
private Scheduler scheduler;
|
||||
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<>();
|
||||
this.scheduler = new BukkitScheduler(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -27,26 +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 Scheduler getScheduler() {
|
||||
return scheduler;
|
||||
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,17 +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.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BukkitWorld implements World {
|
||||
private org.bukkit.World world;
|
||||
private Border worldBorder;
|
||||
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());
|
||||
}
|
||||
@@ -22,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
|
||||
@@ -38,19 +109,98 @@ public class BukkitWorld implements World {
|
||||
return world.getUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return world.getSeaLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public Border getWorldBorder() {
|
||||
return worldBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getSpawnCoordinate() {
|
||||
Location spawnLocation = world.getSpawnLocation();
|
||||
return new Coordinate(spawnLocation.getBlockX(), spawnLocation.getBlockZ());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return world.getSeaLevel();
|
||||
public int getMaxElevation() {
|
||||
return world.getLogicalHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
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,724 +0,0 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class Metrics {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// The plugin id
|
||||
private final int pluginId;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
* @param pluginId The id of the plugin.
|
||||
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(Plugin plugin, int pluginId) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
this.pluginId = pluginId;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JsonObject getPluginData() {
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.addProperty("pluginName", pluginName); // Append the name of the plugin
|
||||
data.addProperty("id", pluginId); // Append the id of the plugin
|
||||
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
JsonArray customCharts = new JsonArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JsonObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.add("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JsonObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
String bukkitName = Bukkit.getName();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bukkitVersion", bukkitVersion);
|
||||
data.addProperty("bukkitName", bukkitName);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JsonObject data = getServerData();
|
||||
|
||||
JsonArray pluginData = new JsonArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
} else { // old bstats version compatibility
|
||||
try {
|
||||
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
|
||||
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
|
||||
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
|
||||
jsonStringGetter.setAccessible(true);
|
||||
String jsonString = (String) jsonStringGetter.invoke(plugin);
|
||||
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
pluginData.add(object);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// minecraft version 1.14+
|
||||
if (logFailedRequests) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
data.add("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data);
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JsonObject getRequestJsonObject() {
|
||||
JsonObject chart = new JsonObject();
|
||||
chart.addProperty("chartId", chartId);
|
||||
try {
|
||||
JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.add("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JsonObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.addProperty("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
JsonObject values = new JsonObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.addProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
JsonObject values = new JsonObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObject value = new JsonObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.addProperty(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.add(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.addProperty("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
JsonObject values = new JsonObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.addProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
JsonObject values = new JsonObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JsonArray categoryValues = new JsonArray();
|
||||
categoryValues.add(new JsonPrimitive(entry.getValue()));
|
||||
values.add(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
JsonObject data = new JsonObject();
|
||||
JsonObject values = new JsonObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JsonArray categoryValues = new JsonArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(new JsonPrimitive(categoryValue));
|
||||
}
|
||||
values.add(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class Version implements Comparable<Version> {
|
||||
|
||||
public static final Version v1_13_2 = new Version(1, 13, 2);
|
||||
public static final Version v1_15_0 = new Version(1, 15, 0);
|
||||
|
||||
private static Version currentMinecraftVersion;
|
||||
private int major = 0, minor = 0, patch = 0;
|
||||
|
||||
public Version(int major, int minor, int patch) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
}
|
||||
|
||||
public Version(String version) {
|
||||
int dash = version.indexOf('-');
|
||||
if (dash > -1) {
|
||||
version = version.substring(0, dash);
|
||||
}
|
||||
String[] semVer = version.split("\\.");
|
||||
try {
|
||||
if (semVer.length > 0) {
|
||||
this.major = Integer.parseInt(semVer[0]);
|
||||
}
|
||||
if (semVer.length > 1) {
|
||||
this.minor = Integer.parseInt(semVer[1]);
|
||||
}
|
||||
if (semVer.length > 2) {
|
||||
this.patch = Integer.parseInt(semVer[2]);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static Version getCurrentMinecraftVersion() {
|
||||
if (currentMinecraftVersion == null) {
|
||||
currentMinecraftVersion = new Version(Bukkit.getBukkitVersion());
|
||||
}
|
||||
return currentMinecraftVersion;
|
||||
}
|
||||
|
||||
public boolean isEqualTo(Version o) {
|
||||
return compareTo(o) == 0;
|
||||
}
|
||||
|
||||
public boolean isHigherThan(Version o) {
|
||||
return compareTo(o) > 0;
|
||||
}
|
||||
|
||||
public boolean isHigherThanOrEqualTo(Version o) {
|
||||
return compareTo(o) >= 0;
|
||||
}
|
||||
|
||||
public boolean isLowerThan(Version o) {
|
||||
return compareTo(o) < 0;
|
||||
}
|
||||
|
||||
public boolean isLowerThanOrEqualTo(Version o) {
|
||||
return compareTo(o) <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Version o) {
|
||||
if (this.major != o.major) {
|
||||
return this.major - o.major;
|
||||
}
|
||||
if (this.minor != o.minor) {
|
||||
return this.minor - o.minor;
|
||||
}
|
||||
return this.patch - o.patch;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,19 +1,105 @@
|
||||
name: Chunky
|
||||
version: ${projectVersion}
|
||||
main: org.popcraft.chunky.ChunkyBukkit
|
||||
api-version: "1.13"
|
||||
softdepend: [dynmap, BlueMap, WorldBorder]
|
||||
name: ${name}
|
||||
version: ${version}
|
||||
main: ${group}.chunky.ChunkyBukkit
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
softdepend: [dynmap, BlueMap, squaremap, WorldBorder]
|
||||
load: POSTWORLD
|
||||
authors: [pop4959]
|
||||
description: Pre-generates chunks, quickly, efficiently, and safely
|
||||
authors: [${author}]
|
||||
description: ${description}
|
||||
website: https://www.spigotmc.org/resources/chunky.81534/
|
||||
commands:
|
||||
chunky:
|
||||
description: Generates chunks
|
||||
aliases: [cy]
|
||||
permission: chunky.chunky
|
||||
permission: chunky.command
|
||||
usage: /chunky
|
||||
permissions:
|
||||
chunky.chunky:
|
||||
description: Generates chunks
|
||||
chunky.command:
|
||||
default: op
|
||||
chunky.command.border:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.cancel:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.center:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.confirm:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.continue:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.corners:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.help:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.pattern:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.pause:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.progress:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.quiet:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.radius:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.reload:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.selection:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.shape:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.silent:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.spawn:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.start:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.trim:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.worldborder:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.command.world:
|
||||
default: op
|
||||
children: [chunky.command]
|
||||
chunky.chunky:
|
||||
default: false
|
||||
children:
|
||||
- chunky.command
|
||||
- chunky.command.cancel
|
||||
- chunky.command.center
|
||||
- chunky.command.confirm
|
||||
- chunky.command.continue
|
||||
- chunky.command.corners
|
||||
- chunky.command.help
|
||||
- chunky.command.pattern
|
||||
- chunky.command.pause
|
||||
- chunky.command.progress
|
||||
- chunky.command.quiet
|
||||
- chunky.command.radius
|
||||
- chunky.command.reload
|
||||
- chunky.command.selection
|
||||
- chunky.command.shape
|
||||
- chunky.command.silent
|
||||
- chunky.command.spawn
|
||||
- chunky.command.start
|
||||
- chunky.command.worldborder
|
||||
- chunky.command.world
|
||||
@@ -1,23 +0,0 @@
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.mikeprimm.com'
|
||||
}
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
compileOnly 'us.dynmap:dynmap-api:3.0'
|
||||
compileOnly 'com.github.BlueMap-Minecraft:BlueMapAPI:v1.1.0'
|
||||
compileOnly 'com.github.Brettflan:WorldBorder:c0d1772418'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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"))
|
||||
}
|
||||
|
||||
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,116 +1,196 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
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.Platform;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.Server;
|
||||
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.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Chunky {
|
||||
private static Map<String, String> translations = Collections.emptyMap();
|
||||
private static Map<String, String> fallbackTranslations = Collections.emptyMap();
|
||||
private Platform platform;
|
||||
private Config config;
|
||||
private Map<World, GenerationTask> generationTasks;
|
||||
private Map<String, ChunkyCommand> commands;
|
||||
private Selection selection;
|
||||
private Runnable pendingAction;
|
||||
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<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(Platform platform) {
|
||||
this.platform = platform;
|
||||
this.generationTasks = new ConcurrentHashMap<>();
|
||||
this.selection = new Selection(this);
|
||||
public Chunky(final Server server, final Config config) {
|
||||
this.server = server;
|
||||
this.config = config;
|
||||
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);
|
||||
}
|
||||
|
||||
public void loadCommands() {
|
||||
final Map<String, ChunkyCommand> commands = new HashMap<>();
|
||||
commands.put("cancel", new CancelCommand(this));
|
||||
commands.put("center", new CenterCommand(this));
|
||||
commands.put("confirm", new ConfirmCommand(this));
|
||||
commands.put("continue", new ContinueCommand(this));
|
||||
commands.put("help", new HelpCommand(this));
|
||||
commands.put("pattern", new PatternCommand(this));
|
||||
commands.put("pause", new PauseCommand(this));
|
||||
commands.put("quiet", new QuietCommand(this));
|
||||
commands.put("radius", new RadiusCommand(this));
|
||||
commands.put("reload", new ReloadCommand(this));
|
||||
commands.put("shape", new ShapeCommand(this));
|
||||
commands.put("silent", new SilentCommand(this));
|
||||
commands.put("spawn", new SpawnCommand(this));
|
||||
commands.put("start", new StartCommand(this));
|
||||
commands.put("worldborder", new WorldBorderCommand(this));
|
||||
commands.put("world", new WorldCommand(this));
|
||||
this.commands = commands;
|
||||
public void disable() {
|
||||
taskLoader.saveTasks();
|
||||
getGenerationTasks().values().forEach(generationTask -> generationTask.stop(false));
|
||||
getScheduler().cancelTasks();
|
||||
ChunkyProvider.unregister();
|
||||
}
|
||||
|
||||
public Map<String, String> loadTranslation(InputStream input) {
|
||||
if (input != null) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
|
||||
StringBuilder lang = new StringBuilder();
|
||||
String s;
|
||||
while ((s = reader.readLine()) != null) {
|
||||
lang.append(s);
|
||||
}
|
||||
return new Gson().fromJson(lang.toString(), new TypeToken<HashMap<String, String>>() {
|
||||
}.getType());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
private Optional<Double> loadLimit() {
|
||||
final Path limitFile = config.getDirectory().resolve(".chunky.properties");
|
||||
try (final InputStream input = Files.newInputStream(limitFile)) {
|
||||
final Properties properties = new Properties();
|
||||
properties.load(input);
|
||||
return Input.tryDouble(properties.getProperty("radius-limit"));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
public static String translate(String key, Object... args) {
|
||||
String message = translations.getOrDefault(key, fallbackTranslations.getOrDefault(key, "Missing translation"));
|
||||
return String.format(message, args);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public Platform getPlatform() {
|
||||
return platform;
|
||||
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() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Config config) {
|
||||
this.config = config;
|
||||
public TaskLoader getTaskLoader() {
|
||||
return taskLoader;
|
||||
}
|
||||
|
||||
public Map<World, GenerationTask> getGenerationTasks() {
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
public Map<String, GenerationTask> getGenerationTasks() {
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
public void setTranslations(Map<String, String> translations) {
|
||||
Chunky.translations = translations;
|
||||
}
|
||||
|
||||
public void setFallbackTranslations(Map<String, String> fallbackTranslations) {
|
||||
Chunky.fallbackTranslations = fallbackTranslations;
|
||||
public Map<String, TrimCommand.Task> getTrimTasks() {
|
||||
return trimTasks;
|
||||
}
|
||||
|
||||
public Map<String, ChunkyCommand> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
public Selection getSelection() {
|
||||
public Selection.Builder getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public Runnable getPendingAction() {
|
||||
return pendingAction;
|
||||
public Optional<Runnable> getPendingAction(final Sender sender) {
|
||||
pendingActions.values().removeIf(PendingAction::hasExpired);
|
||||
final PendingAction pendingAction = pendingActions.remove(sender.getName());
|
||||
return Optional.ofNullable(pendingAction).map(PendingAction::getAction);
|
||||
}
|
||||
|
||||
public void setPendingAction(Runnable pendingAction) {
|
||||
this.pendingAction = pendingAction;
|
||||
public void setPendingAction(final Sender sender, final Runnable action) {
|
||||
pendingActions.put(sender.getName(), new PendingAction(action));
|
||||
}
|
||||
|
||||
public void setLanguage(final String language) {
|
||||
Translator.setLanguage(language);
|
||||
}
|
||||
|
||||
public RegionCache getRegionCache() {
|
||||
return regionCache;
|
||||
}
|
||||
|
||||
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,150 +1,186 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
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.concurrent.ConcurrentLinkedQueue;
|
||||
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;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
|
||||
public class GenerationTask implements Runnable {
|
||||
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 World world;
|
||||
private final int radiusX, radiusZ, centerX, centerZ;
|
||||
private final Selection selection;
|
||||
private final Shape shape;
|
||||
private final AtomicLong startTime = new AtomicLong();
|
||||
private final AtomicLong updateTime = new AtomicLong();
|
||||
private final AtomicLong finishedChunks = new AtomicLong();
|
||||
private final Deque<Pair<Long, AtomicLong>> updateSamples = new ConcurrentLinkedDeque<>();
|
||||
private final Progress progress;
|
||||
private final RegionCache.WorldState worldState;
|
||||
private ChunkIterator chunkIterator;
|
||||
private Shape shape;
|
||||
private boolean stopped, cancelled;
|
||||
private long prevTime;
|
||||
private final AtomicLong startTime = new AtomicLong();
|
||||
private final AtomicLong printTime = new AtomicLong();
|
||||
private final AtomicLong finishedChunks = new AtomicLong();
|
||||
private final AtomicLong totalChunks = new AtomicLong();
|
||||
private final ConcurrentLinkedQueue<Long> chunkUpdateTimes10Sec = new ConcurrentLinkedQueue<>();
|
||||
private static final int MAX_WORKING = 50;
|
||||
|
||||
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.shape = ShapeFactory.getShape(selection);
|
||||
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.world = selection.world;
|
||||
this.radiusX = selection.radiusX;
|
||||
this.radiusZ = selection.radiusZ;
|
||||
this.centerX = selection.centerX;
|
||||
this.centerZ = selection.centerZ;
|
||||
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());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void printUpdate(World chunkWorld, int chunkX, int chunkZ) {
|
||||
private synchronized void update(final int chunkX, final int chunkZ, final boolean loaded) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
String world = chunkWorld.getName();
|
||||
long chunkNum = finishedChunks.addAndGet(1);
|
||||
double percentDone = 100f * chunkNum / totalChunks.get();
|
||||
long currentTime = System.currentTimeMillis();
|
||||
chunkUpdateTimes10Sec.add(currentTime);
|
||||
while (currentTime - chunkUpdateTimes10Sec.peek() > 1e4) chunkUpdateTimes10Sec.poll();
|
||||
long chunksLeft = totalChunks.get() - finishedChunks.get();
|
||||
if (chunksLeft > 0 && (chunky.getSelection().silent || ((currentTime - printTime.get()) / 1e3) < chunky.getSelection().quiet)) {
|
||||
return;
|
||||
progress.chunkCount = finishedChunks.addAndGet(1);
|
||||
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() < SAMPLE_SUB_INTERVAL) {
|
||||
bin.right().addAndGet(1);
|
||||
} else if (updateSamples.add(Pair.of(currentTime, new AtomicLong(1)))) {
|
||||
while (!updateSamples.isEmpty() && currentTime - updateSamples.peek().left() > SAMPLE_INTERVAL) {
|
||||
updateSamples.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
printTime.set(currentTime);
|
||||
long oldestTime = chunkUpdateTimes10Sec.peek();
|
||||
double timeDiff = (currentTime - oldestTime) / 1e3;
|
||||
final Pair<Long, AtomicLong> oldest = updateSamples.peek();
|
||||
final long oldestTime = oldest == null ? currentTime : oldest.left();
|
||||
final long chunksLeft = chunkIterator.total() - finishedChunks.get();
|
||||
final double timeDiff = (currentTime - oldestTime) / 1e3;
|
||||
if (chunksLeft > 0 && timeDiff < 1e-1) {
|
||||
return;
|
||||
}
|
||||
double speed = chunkUpdateTimes10Sec.size() / timeDiff;
|
||||
Sender console = chunky.getPlatform().getServer().getConsoleSender();
|
||||
long sampleCount = 0;
|
||||
for (Pair<Long, AtomicLong> b : updateSamples) {
|
||||
sampleCount += b.right().get();
|
||||
}
|
||||
progress.rate = timeDiff > 0 ? sampleCount / timeDiff : 0;
|
||||
final long time;
|
||||
if (chunksLeft == 0) {
|
||||
long total = (prevTime + (currentTime - startTime.get())) / 1000;
|
||||
long totalHours = total / 3600;
|
||||
long totalMinutes = (total - totalHours * 3600) / 60;
|
||||
long totalSeconds = total - totalHours * 3600 - totalMinutes * 60;
|
||||
console.sendMessage("task_done", translate("prefix"), world, chunkNum, percentDone, totalHours, totalMinutes, totalSeconds);
|
||||
time = (prevTime + (currentTime - startTime.get())) / 1000;
|
||||
progress.complete = true;
|
||||
} else {
|
||||
long eta = (long) (chunksLeft / speed);
|
||||
long etaHours = eta / 3600;
|
||||
long etaMinutes = (eta - etaHours * 3600) / 60;
|
||||
long etaSeconds = eta - etaHours * 3600 - etaMinutes * 60;
|
||||
console.sendMessage("task_update", translate("prefix"), world, chunkNum, percentDone, etaHours, etaMinutes, etaSeconds, speed, chunkX, chunkZ);
|
||||
time = (long) (chunksLeft / progress.rate);
|
||||
}
|
||||
progress.hours = time / 3600;
|
||||
progress.minutes = (time - progress.hours * 3600) / 60;
|
||||
progress.seconds = time - progress.hours * 3600 - progress.minutes * 60;
|
||||
progress.chunkX = chunkX;
|
||||
progress.chunkZ = chunkZ;
|
||||
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;
|
||||
}
|
||||
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", world.getName()));
|
||||
final Semaphore working = new Semaphore(MAX_WORKING);
|
||||
Thread.currentThread().setName(String.format("Chunky-%s Thread", selection.world().getName()));
|
||||
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 chunkCoord = chunkIterator.next();
|
||||
int xChunkCenter = (chunkCoord.x << 4) + 8;
|
||||
int zChunkCenter = (chunkCoord.z << 4) + 8;
|
||||
if (!shape.isBounding(xChunkCenter, zChunkCenter) || world.isChunkGenerated(chunkCoord.x, chunkCoord.z)) {
|
||||
printUpdate(world, chunkCoord.x, chunkCoord.z);
|
||||
final ChunkCoordinate chunk = chunkIterator.next();
|
||||
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 (!forceLoadExistingChunks && worldState.isGenerated(chunk.x(), chunk.z())) {
|
||||
update(chunk.x(), chunk.z(), false);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
working.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
stop(cancelled);
|
||||
break;
|
||||
}
|
||||
world.getChunkAtAsync(chunkCoord.x, chunkCoord.z).thenRun(() -> {
|
||||
working.release();
|
||||
printUpdate(world, chunkCoord.x, chunkCoord.z);
|
||||
});
|
||||
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.getPlatform().getServer().getConsoleSender().sendMessage("task_stopped", translate("prefix"), world.getName());
|
||||
chunky.getServer().getConsole().sendMessagePrefixed(TranslationKey.TASK_STOPPED, selection.world().getName());
|
||||
} else {
|
||||
this.cancelled = true;
|
||||
cancelled = true;
|
||||
}
|
||||
chunky.getConfig().saveTask(this);
|
||||
chunky.getGenerationTasks().remove(this.getWorld());
|
||||
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(boolean cancelled) {
|
||||
public void stop(final boolean cancelled) {
|
||||
this.stopped = true;
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
public Chunky getChunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public int getRadiusX() {
|
||||
return radiusX;
|
||||
}
|
||||
|
||||
public int getRadiusZ() {
|
||||
return radiusZ;
|
||||
}
|
||||
|
||||
public int getCenterX() {
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public int getCenterZ() {
|
||||
return centerZ;
|
||||
public Selection getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
@@ -166,4 +202,71 @@ public class GenerationTask implements Runnable {
|
||||
public long getTotalTime() {
|
||||
return prevTime + (startTime.get() > 0 ? System.currentTimeMillis() - startTime.get() : 0);
|
||||
}
|
||||
|
||||
public Progress getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static final class Progress {
|
||||
private final String world;
|
||||
private long chunkCount;
|
||||
private boolean complete;
|
||||
private float percentComplete;
|
||||
private long hours, minutes, seconds;
|
||||
private double rate;
|
||||
private int chunkX, chunkZ;
|
||||
|
||||
private Progress(final String world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public String getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public long getChunkCount() {
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
public float getPercentComplete() {
|
||||
return percentComplete;
|
||||
}
|
||||
|
||||
public long getHours() {
|
||||
return hours;
|
||||
}
|
||||
|
||||
public long getMinutes() {
|
||||
return minutes;
|
||||
}
|
||||
|
||||
public long getSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
public double getRate() {
|
||||
return rate;
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public void sendUpdate(final Sender sender) {
|
||||
if (complete) {
|
||||
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(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,55 +1,227 @@
|
||||
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.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 {
|
||||
public World world;
|
||||
public int centerX;
|
||||
public int centerZ;
|
||||
public int radiusX;
|
||||
public int radiusZ;
|
||||
public String pattern;
|
||||
public String shape;
|
||||
public boolean silent;
|
||||
public int quiet;
|
||||
@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 Parameter pattern;
|
||||
private final String shape;
|
||||
private final int centerChunkX;
|
||||
private final int centerChunkZ;
|
||||
private final int radiusChunksX;
|
||||
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;
|
||||
|
||||
public Selection(Chunky chunky) {
|
||||
this.world = chunky.getPlatform().getServer().getWorlds().get(0);
|
||||
this.radiusX = 500;
|
||||
this.radiusZ = radiusX;
|
||||
this.pattern = "concentric";
|
||||
this.shape = "square";
|
||||
this.quiet = 1;
|
||||
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;
|
||||
this.radiusX = radiusX;
|
||||
this.radiusZ = radiusZ;
|
||||
this.pattern = pattern;
|
||||
this.shape = shape;
|
||||
this.centerChunkX = (int) centerX >> 4;
|
||||
this.centerChunkZ = (int) centerZ >> 4;
|
||||
this.radiusChunksX = (int) Math.ceil(radiusX / 16f);
|
||||
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 Selection(int x, int z, int radius) {
|
||||
this.centerX = x;
|
||||
this.centerZ = z;
|
||||
this.radiusX = radius;
|
||||
this.radiusZ = radius;
|
||||
public static Builder builder(final Chunky chunky, final World world) {
|
||||
return new Builder(chunky, world);
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return centerX >> 4;
|
||||
public Chunky chunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return centerZ >> 4;
|
||||
public World world() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
public int getRadiusChunksX() {
|
||||
return (int) Math.ceil(radiusX / 16f);
|
||||
public double centerX() {
|
||||
return this.centerX;
|
||||
}
|
||||
|
||||
public int getRadiusChunksZ() {
|
||||
return (int) Math.ceil(radiusZ / 16f);
|
||||
public double centerZ() {
|
||||
return this.centerZ;
|
||||
}
|
||||
|
||||
public int getDiameterChunksX() {
|
||||
return 2 * getRadiusChunksX() + 1;
|
||||
public double radiusX() {
|
||||
return this.radiusX;
|
||||
}
|
||||
|
||||
public int getDiameterChunksZ() {
|
||||
return 2 * getRadiusChunksZ() + 1;
|
||||
public double radiusZ() {
|
||||
return this.radiusZ;
|
||||
}
|
||||
|
||||
public Parameter pattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public String shape() {
|
||||
return this.shape;
|
||||
}
|
||||
|
||||
public int centerChunkX() {
|
||||
return this.centerChunkX;
|
||||
}
|
||||
|
||||
public int centerChunkZ() {
|
||||
return this.centerChunkZ;
|
||||
}
|
||||
|
||||
public int radiusChunksX() {
|
||||
return this.radiusChunksX;
|
||||
}
|
||||
|
||||
public int radiusChunksZ() {
|
||||
return this.radiusChunksZ;
|
||||
}
|
||||
|
||||
public int diameterChunksX() {
|
||||
return this.diameterChunksX;
|
||||
}
|
||||
|
||||
public int diameterChunksZ() {
|
||||
return this.diameterChunksZ;
|
||||
}
|
||||
|
||||
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 = 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(final Chunky chunky, final World world) {
|
||||
this.chunky = chunky;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public Builder world(final World world) {
|
||||
this.world = world;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder center(final double centerX, final double centerZ) {
|
||||
this.centerX = centerX;
|
||||
this.centerZ = centerZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder centerX(final double centerX) {
|
||||
this.centerX = centerX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder centerZ(final double centerZ) {
|
||||
this.centerZ = centerZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radius(final double radius) {
|
||||
this.radiusX = radius;
|
||||
this.radiusZ = radius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radiusX(final double radiusX) {
|
||||
this.radiusX = radiusX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder radiusZ(final double radiusZ) {
|
||||
this.radiusZ = radiusZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pattern(final Parameter pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder shape(final String shape) {
|
||||
this.shape = shape;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder spawn() {
|
||||
final Location spawn = world.getSpawn();
|
||||
this.centerX = spawn.getX();
|
||||
this.centerZ = spawn.getZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder worldborder() {
|
||||
final Border border = world.getWorldBorder();
|
||||
final Vector2 center = border.getCenter();
|
||||
this.centerX = center.getX();
|
||||
this.centerZ = center.getZ();
|
||||
this.radiusX = border.getRadiusX();
|
||||
this.radiusZ = border.getRadiusZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Selection build() {
|
||||
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 {
|
||||
}
|
||||
@@ -1,28 +1,73 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
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.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.ArrayList;
|
||||
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) {
|
||||
if (chunky.getGenerationTasks().size() == 0 && chunky.getConfig().loadTasks().size() == 0) {
|
||||
sender.sendMessage("format_cancel_no_tasks", translate("prefix"));
|
||||
@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 = () -> {
|
||||
sender.sendMessage("format_cancel", translate("prefix"));
|
||||
chunky.getConfig().cancelTasks();
|
||||
chunky.getGenerationTasks().values().forEach(generationTask -> generationTask.stop(true));
|
||||
chunky.getGenerationTasks().clear();
|
||||
chunky.getPlatform().getServer().getScheduler().cancelTasks();
|
||||
};
|
||||
chunky.setPendingAction(cancelAction);
|
||||
sender.sendMessage("format_cancel_confirm", translate("prefix"));
|
||||
final Runnable cancelAction;
|
||||
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(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(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(TranslationKey.FORMAT_CANCEL_CONFIRM, "/chunky confirm");
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -2,38 +2,48 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.Selection;
|
||||
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;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class CenterCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class CenterCommand extends ChunkyCommand {
|
||||
public CenterCommand(Chunky chunky) {
|
||||
super(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.tryDouble(args[1]);
|
||||
}
|
||||
Optional<Double> newZ = Optional.empty();
|
||||
if (args.length > 2) {
|
||||
newZ = Input.tryDouble(args[2]);
|
||||
}
|
||||
if (!newX.isPresent() || !newZ.isPresent()) {
|
||||
sender.sendMessage("help_center");
|
||||
@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.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(TranslationKey.HELP_CENTER);
|
||||
return;
|
||||
}
|
||||
if (Math.abs(newX.get().intValue()) > 3e7 || Math.abs(newZ.get().intValue()) > 3e7) {
|
||||
sender.sendMessage("help_center");
|
||||
if (Input.isPastWorldLimit(centerX) || Input.isPastWorldLimit(centerZ)) {
|
||||
sender.sendMessage(TranslationKey.HELP_CENTER);
|
||||
return;
|
||||
}
|
||||
Selection selection = chunky.getSelection();
|
||||
selection.centerX = newX.get().intValue();
|
||||
selection.centerZ = newZ.get().intValue();
|
||||
sender.sendMessage("format_center", translate("prefix"), selection.centerX, selection.centerZ);
|
||||
chunky.getSelection().center(centerX, 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);
|
||||
|
||||
public 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,22 +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 static org.popcraft.chunky.Chunky.translate;
|
||||
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) {
|
||||
Runnable pendingAction = chunky.getPendingAction();
|
||||
if (pendingAction == null) {
|
||||
sender.sendMessage("format_confirm", translate("prefix"));
|
||||
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;
|
||||
}
|
||||
chunky.setPendingAction(null);
|
||||
pendingAction.run();
|
||||
pendingAction.get().run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -4,32 +4,58 @@ 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.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class ContinueCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class ContinueCommand extends ChunkyCommand {
|
||||
public ContinueCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public ContinueCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final List<GenerationTask> loadTasks = chunky.getConfig().loadTasks();
|
||||
if (loadTasks.size() == 0) {
|
||||
sender.sendMessage("format_continue_no_tasks", translate("prefix"));
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final List<GenerationTask> loadTasks;
|
||||
if (arguments.size() > 0) {
|
||||
final Optional<World> world = Input.tryWorld(chunky, arguments.joined());
|
||||
if (world.isEmpty()) {
|
||||
sender.sendMessage(TranslationKey.HELP_CONTINUE);
|
||||
return;
|
||||
}
|
||||
loadTasks = chunky.getTaskLoader().loadTask(world.get()).map(List::of).orElse(List.of());
|
||||
} else {
|
||||
loadTasks = chunky.getTaskLoader().loadTasks();
|
||||
}
|
||||
if (loadTasks.stream().allMatch(GenerationTask::isCancelled)) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CONTINUE_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
final Map<World, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
loadTasks.forEach(generationTask -> {
|
||||
if (!generationTasks.containsKey(generationTask.getWorld())) {
|
||||
generationTasks.put(generationTask.getWorld(), generationTask);
|
||||
chunky.getPlatform().getServer().getScheduler().runTaskAsync(generationTask);
|
||||
sender.sendMessage("format_continue", translate("prefix"), generationTask.getWorld().getName());
|
||||
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(TranslationKey.FORMAT_CONTINUE, world.getName());
|
||||
} else {
|
||||
sender.sendMessage("format_started_already", translate("prefix"), generationTask.getWorld().getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_STARTED_ALREADY, 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 List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
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 implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CornersCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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(TranslationKey.HELP_CORNERS);
|
||||
return;
|
||||
}
|
||||
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(TranslationKey.FORMAT_CENTER, Formatting.number(centerX), Formatting.number(centerZ));
|
||||
final String shape;
|
||||
if (radiusX == radiusZ) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX));
|
||||
shape = ShapeType.SQUARE;
|
||||
} else {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
shape = ShapeType.RECTANGLE;
|
||||
}
|
||||
chunky.getSelection().shape(shape);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_SHAPE, shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -3,51 +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.Chunky.translate;
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class HelpCommand extends ChunkyCommand {
|
||||
private 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(translate("help_start"));
|
||||
helpMessages.add(translate("help_pause"));
|
||||
helpMessages.add(translate("help_continue"));
|
||||
helpMessages.add(translate("help_cancel"));
|
||||
helpMessages.add(translate("help_world"));
|
||||
helpMessages.add(translate("help_worldborder"));
|
||||
helpMessages.add(translate("help_center"));
|
||||
helpMessages.add(translate("help_spawn"));
|
||||
helpMessages.add(translate("help_radius"));
|
||||
helpMessages.add(translate("help_shape"));
|
||||
helpMessages.add(translate("help_pattern"));
|
||||
helpMessages.add(translate("help_silent"));
|
||||
helpMessages.add(translate("help_quiet"));
|
||||
helpMessages.add(translate("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(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(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,36 +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.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class PatternCommand extends ChunkyCommand {
|
||||
private static final List<String> PATTERNS = Arrays.asList("concentric", "loop", "spiral");
|
||||
public class PatternCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public PatternCommand(Chunky chunky) {
|
||||
super(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.sendMessage("format_pattern", translate("prefix"), pattern);
|
||||
public PatternCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,52 @@ package org.popcraft.chunky.command;
|
||||
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.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.ArrayList;
|
||||
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) {
|
||||
if (chunky.getGenerationTasks().size() == 0) {
|
||||
sender.sendMessage("format_pause_no_tasks", translate("prefix"));
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
if (generationTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
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().getName()).stop(false);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, world.get().getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (GenerationTask generationTask : chunky.getGenerationTasks().values()) {
|
||||
generationTask.stop(false);
|
||||
sender.sendMessage("format_pause", translate("prefix"), generationTask.getWorld().getName());
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PAUSE, generationTask.getSelection().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 List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
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 implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ProgressCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
if (generationTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_PROGRESS_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
for (World world : chunky.getServer().getWorlds()) {
|
||||
if (generationTasks.containsKey(world.getName())) {
|
||||
generationTasks.get(world.getName()).getProgress().sendUpdate(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,34 @@
|
||||
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.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class QuietCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class QuietCommand extends ChunkyCommand {
|
||||
public QuietCommand(Chunky chunky) {
|
||||
super(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;
|
||||
}
|
||||
Selection selection = chunky.getSelection();
|
||||
selection.quiet = Math.max(0, newQuiet.get());
|
||||
sender.sendMessage("format_quiet", translate("prefix"), selection.quiet);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -3,42 +3,58 @@ 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.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class RadiusCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class RadiusCommand extends ChunkyCommand {
|
||||
public RadiusCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public RadiusCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
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;
|
||||
}
|
||||
Optional<Integer> newRadiusX = Input.tryInteger(args[1]);
|
||||
if (!newRadiusX.isPresent() || newRadiusX.get() < 0 || newRadiusX.get() > 3e7) {
|
||||
sender.sendMessage("help_radius");
|
||||
final Selection current = chunky.getSelection().build();
|
||||
final double radiusX = signX.map(sign -> current.radiusX() + sign * newRadiusX.get()).orElseGet(newRadiusX::get);
|
||||
if (radiusX < 0 || Input.isPastWorldLimit(radiusX)) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
Selection selection = chunky.getSelection();
|
||||
if (args.length > 2) {
|
||||
Optional<Integer> newRadiusZ = Input.tryInteger(args[2]);
|
||||
if (!newRadiusZ.isPresent() || newRadiusZ.get() < 0 || newRadiusZ.get() > 3e7) {
|
||||
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;
|
||||
}
|
||||
selection.radiusZ = newRadiusZ.get();
|
||||
}
|
||||
selection.radiusX = newRadiusX.get();
|
||||
if (args.length == 2) {
|
||||
selection.radiusZ = selection.radiusX;
|
||||
sender.sendMessage("format_radius", translate("prefix"), selection.radiusX);
|
||||
final double radiusZ = signZ.map(sign -> current.radiusZ() + sign * newRadiusZ.get()).orElseGet(newRadiusZ::get);
|
||||
if (radiusZ < 0 || Input.isPastWorldLimit(radiusZ)) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().radiusX(radiusX).radiusZ(radiusZ);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
} else {
|
||||
sender.sendMessage("format_radii", translate("prefix"), selection.radiusX, selection.radiusZ);
|
||||
chunky.getSelection().radius(radiusX);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +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;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.List;
|
||||
|
||||
public class ReloadCommand extends ChunkyCommand {
|
||||
public ReloadCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
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().size() > 0) {
|
||||
sender.sendMessage("format_reload_tasks_running", translate("prefix"));
|
||||
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());
|
||||
}
|
||||
chunky.getPlatform().getServer().getConfig().reload();
|
||||
sender.sendMessage("format_reload", translate("prefix"));
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class ShapeCommand extends ChunkyCommand {
|
||||
private static final List<String> SHAPES = Arrays.asList("circle", "diamond", "oval", "pentagon", "rectangle", "square", "star", "triangle");
|
||||
public class ShapeCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ShapeCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("help_shape");
|
||||
return;
|
||||
}
|
||||
String shape = args[1].toLowerCase();
|
||||
if (!SHAPES.contains(shape)) {
|
||||
sender.sendMessage("help_shape");
|
||||
return;
|
||||
}
|
||||
Selection selection = chunky.getSelection();
|
||||
selection.shape = shape;
|
||||
sender.sendMessage("format_shape", translate("prefix"), shape);
|
||||
public ShapeCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
return 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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,29 @@
|
||||
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.TranslationKey;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.List;
|
||||
|
||||
public class SilentCommand extends ChunkyCommand {
|
||||
public SilentCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class SilentCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SilentCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Selection selection = chunky.getSelection();
|
||||
selection.silent = !selection.silent;
|
||||
String status = translate(selection.silent ? "enabled" : "disabled");
|
||||
sender.sendMessage("format_silent", translate("prefix"), 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();
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,27 @@ 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.Coordinate;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnCommand extends ChunkyCommand {
|
||||
public SpawnCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
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) {
|
||||
Selection selection = chunky.getSelection();
|
||||
Coordinate spawn = selection.world.getSpawnCoordinate();
|
||||
selection.centerX = spawn.getX();
|
||||
selection.centerZ = spawn.getZ();
|
||||
sender.sendMessage("format_center", translate("prefix"), selection.centerX, selection.centerZ);
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
chunky.getSelection().spawn();
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -4,32 +4,105 @@ import org.popcraft.chunky.Chunky;
|
||||
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 static org.popcraft.chunky.Chunky.translate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class StartCommand extends ChunkyCommand {
|
||||
public StartCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class StartCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public StartCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
final Selection selection = chunky.getSelection();
|
||||
if (chunky.getGenerationTasks().containsKey(selection.world)) {
|
||||
sender.sendMessage("format_started_already", translate("prefix"), selection.world.getName());
|
||||
@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_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (arguments.size() > 1) {
|
||||
final Optional<String> shape = arguments.next().flatMap(Input::tryShape);
|
||||
if (shape.isPresent()) {
|
||||
chunky.getSelection().shape(shape.get());
|
||||
} else {
|
||||
sender.sendMessage(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(TranslationKey.HELP_START);
|
||||
return;
|
||||
}
|
||||
}
|
||||
final Selection current = chunky.getSelection().build();
|
||||
if (chunky.getGenerationTasks().containsKey(current.world().getName())) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_STARTED_ALREADY, current.world().getName());
|
||||
return;
|
||||
}
|
||||
if (current.radiusX() > chunky.getLimit()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START_LIMIT, Formatting.number(chunky.getLimit()));
|
||||
return;
|
||||
}
|
||||
final Runnable startAction = () -> {
|
||||
GenerationTask generationTask = new GenerationTask(chunky, selection);
|
||||
chunky.getGenerationTasks().put(selection.world, generationTask);
|
||||
chunky.getPlatform().getServer().getScheduler().runTaskAsync(generationTask);
|
||||
String radius = selection.radiusX == selection.radiusZ ? String.valueOf(selection.radiusX) : String.format("%d, %d", selection.radiusX, selection.radiusZ);
|
||||
sender.sendMessage("format_start", translate("prefix"), selection.world.getName(), selection.centerX, selection.centerZ, radius);
|
||||
final GenerationTask generationTask = new GenerationTask(chunky, current);
|
||||
chunky.getGenerationTasks().put(current.world().getName(), generationTask);
|
||||
chunky.getScheduler().runTask(generationTask);
|
||||
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(selection.world).isPresent()) {
|
||||
chunky.setPendingAction(startAction);
|
||||
sender.sendMessage("format_start_confirm", translate("prefix"));
|
||||
if (chunky.getTaskLoader().loadTask(current.world()).filter(task -> !task.isCancelled()).isPresent()) {
|
||||
chunky.setPendingAction(sender, startAction);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_START_CONFIRM, "/chunky continue", "/chunky confirm");
|
||||
} else {
|
||||
startAction.run();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
} else if (arguments.size() == 2) {
|
||||
return ShapeType.all();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
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.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 implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public TrimCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@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_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (arguments.size() > 1) {
|
||||
final Optional<String> shape = arguments.next().flatMap(Input::tryShape);
|
||||
if (shape.isPresent()) {
|
||||
chunky.getSelection().shape(shape.get());
|
||||
} else {
|
||||
sender.sendMessage(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(TranslationKey.HELP_TRIM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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(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(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 AtomicLong finishedRegions = new AtomicLong();
|
||||
final AtomicLong deleted = new AtomicLong();
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final AtomicLong updateTime = new AtomicLong(startTime);
|
||||
try {
|
||||
if (regionPath.isPresent()) {
|
||||
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;
|
||||
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(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 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;
|
||||
}
|
||||
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(world, regionFileName, shape, inside, chunkX, chunkZ, inhabitedTimeCheck, inhabitedTime);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
final int chunkCenterX = ((chunkX + offsetX) << 4) + 8;
|
||||
final int chunkCenterZ = ((chunkZ + offsetZ) << 4) + 8;
|
||||
if (inside != shape.isBounding(chunkCenterX, chunkCenterZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int deleteRegion(final World world, final String regionFileName) {
|
||||
try {
|
||||
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();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
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> 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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,53 +2,82 @@ package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.integration.BorderIntegration;
|
||||
import org.popcraft.chunky.integration.Integration;
|
||||
import org.popcraft.chunky.integration.WorldBorderIntegration;
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
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;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class WorldBorderCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class WorldBorderCommand extends ChunkyCommand {
|
||||
public WorldBorderCommand(Chunky chunky) {
|
||||
super(chunky);
|
||||
public WorldBorderCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
public void execute(Sender sender, String[] args) {
|
||||
Selection selection = chunky.getSelection();
|
||||
Map<String, Integration> integrations = chunky.getPlatform().getServer().getIntegrations();
|
||||
if (integrations.containsKey("border")) {
|
||||
WorldBorderIntegration worldborder = (WorldBorderIntegration) integrations.get("border");
|
||||
String worldName = selection.world.getName();
|
||||
if (worldborder.hasBorder(worldName)) {
|
||||
Border border = worldborder.getBorder(worldName);
|
||||
Coordinate center = border.getCenter();
|
||||
selection.centerX = center.getX();
|
||||
selection.centerZ = center.getZ();
|
||||
selection.radiusX = border.getRadiusX();
|
||||
selection.radiusZ = border.getRadiusZ();
|
||||
selection.shape = border.getShape();
|
||||
sender.sendMessage("format_center", translate("prefix"), selection.centerX, selection.centerZ);
|
||||
if (selection.radiusX == selection.radiusZ) {
|
||||
sender.sendMessage("format_radius", translate("prefix"), selection.radiusX);
|
||||
} else {
|
||||
sender.sendMessage("format_radii", translate("prefix"), selection.radiusX, selection.radiusZ);
|
||||
}
|
||||
sender.sendMessage("format_shape", translate("prefix"), selection.shape);
|
||||
@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;
|
||||
}
|
||||
}
|
||||
// Default to the vanilla world border
|
||||
Border border = selection.world.getWorldBorder();
|
||||
Coordinate center = border.getCenter();
|
||||
selection.centerX = center.getX();
|
||||
selection.centerZ = center.getZ();
|
||||
selection.radiusX = border.getRadiusX();
|
||||
selection.radiusZ = border.getRadiusZ();
|
||||
sender.sendMessage("format_center", translate("prefix"), selection.centerX, selection.centerZ);
|
||||
sender.sendMessage("format_radius", translate("prefix"), selection.radiusX);
|
||||
final Selection previous = chunky.getSelection().build();
|
||||
if (!setBorderViaIntegration(previous.world())) {
|
||||
chunky.getSelection().worldborder();
|
||||
}
|
||||
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(TranslationKey.FORMAT_RADIUS, Formatting.number(current.radiusX()));
|
||||
} else {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(current.radiusX()), Formatting.number(current.radiusZ()));
|
||||
}
|
||||
if (!previous.shape().equals(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());
|
||||
}
|
||||
}
|
||||
|
||||
@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")) {
|
||||
final BorderIntegration worldborder = (BorderIntegration) integrations.get("border");
|
||||
final String worldName = world.getName();
|
||||
if (worldborder.hasBorder(worldName)) {
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,45 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.Selection;
|
||||
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.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.Chunky.translate;
|
||||
public class WorldCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public class WorldCommand extends ChunkyCommand {
|
||||
public WorldCommand(Chunky chunky) {
|
||||
super(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;
|
||||
}
|
||||
Selection selection = chunky.getSelection();
|
||||
selection.world = newWorld.get();
|
||||
sender.sendMessage("format_world", translate("prefix"), selection.world.getName());
|
||||
public WorldCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabSuggestions(Sender sender, String[] args) {
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
chunky.getPlatform().getServer().getWorlds().forEach(world -> suggestions.add(world.getName()));
|
||||
return suggestions;
|
||||
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 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,25 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public abstract class AbstractMapIntegration implements MapIntegration {
|
||||
protected String label = "World Border";
|
||||
protected Color color = Color.RED;
|
||||
|
||||
@Override
|
||||
public void setOptions(String label, String color, boolean hideByDefault, int priority, int weight) {
|
||||
if (label != null && !label.isEmpty()) {
|
||||
this.label = label;
|
||||
}
|
||||
if (color.length() != 6) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int r = Integer.parseInt(color.substring(0, 2), 16);
|
||||
int g = Integer.parseInt(color.substring(2, 4), 16);
|
||||
int b = Integer.parseInt(color.substring(4, 6), 16);
|
||||
this.color = new Color(r, g, b);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||
import de.bluecolored.bluemap.api.BlueMapAPIListener;
|
||||
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.Chunky;
|
||||
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;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BlueMapIntegration extends AbstractMapIntegration implements BlueMapAPIListener {
|
||||
private Chunky chunky;
|
||||
private BlueMapAPI blueMapAPI;
|
||||
private List<Consumer<Void>> toAdd = new ArrayList<>();
|
||||
|
||||
public BlueMapIntegration(Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(BlueMapAPI blueMapApi) {
|
||||
this.blueMapAPI = blueMapApi;
|
||||
toAdd.forEach(add -> add.accept(null));
|
||||
toAdd.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(BlueMapAPI blueMapApi) {
|
||||
this.blueMapAPI = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addShapeMarker(final World world, final Shape shape) {
|
||||
if (blueMapAPI == null) {
|
||||
this.toAdd.add(v -> this.addShapeMarker(world, shape));
|
||||
return;
|
||||
}
|
||||
final MarkerAPI markerAPI;
|
||||
try {
|
||||
markerAPI = blueMapAPI.getMarkerAPI();
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
final MarkerSet markerSet = markerAPI.createMarkerSet(this.label.toLowerCase());
|
||||
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 = createEllipse(centerPos, radii[0], radii[1], 100);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
blueMapAPI.getWorld(world.getUUID()).ifPresent(blueWorld -> blueWorld.getMaps().forEach(map -> {
|
||||
ShapeMarker marker = markerSet.createShapeMarker(shapeLabel(world), map, blueShape, world.getSeaLevel());
|
||||
marker.setColors(this.color, new Color(0, true));
|
||||
marker.setLabel(this.label.toLowerCase());
|
||||
}));
|
||||
try {
|
||||
markerAPI.save();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShapeMarker(World world) {
|
||||
if (blueMapAPI == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final MarkerAPI markerAPI = blueMapAPI.getMarkerAPI();
|
||||
final MarkerSet markerSet = markerAPI.createMarkerSet(this.label.toLowerCase());
|
||||
blueMapAPI.getWorld(world.getUUID()).ifPresent(blueWorld -> blueWorld.getMaps().forEach(map -> {
|
||||
markerSet.removeMarker(shapeLabel(world));
|
||||
}));
|
||||
markerAPI.save();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllShapeMarkers() {
|
||||
if (blueMapAPI == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final MarkerAPI markerAPI = blueMapAPI.getMarkerAPI();
|
||||
final MarkerSet markerSet = markerAPI.createMarkerSet(this.label.toLowerCase());
|
||||
chunky.getPlatform().getServer().getWorlds().forEach(world -> markerSet.removeMarker(shapeLabel(world)));
|
||||
markerAPI.save();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private String shapeLabel(World world) {
|
||||
return String.format("%s.%s", "chunky.markerset", world.getName());
|
||||
}
|
||||
|
||||
private de.bluecolored.bluemap.api.marker.Shape createEllipse(Vector2d centerPos, double radiusX, double radiusZ, @SuppressWarnings("SameParameterValue") int points) {
|
||||
if (points < 3) throw new IllegalArgumentException("A shape has to have at least 3 points!");
|
||||
Vector2d[] pointArray = new Vector2d[points];
|
||||
double segmentAngle = 2 * Math.PI / points;
|
||||
double angle = 0d;
|
||||
for (int i = 0; i < points; i++) {
|
||||
pointArray[i] = centerPos.add(Math.sin(angle) * radiusX, Math.cos(angle) * radiusZ);
|
||||
angle += segmentAngle;
|
||||
}
|
||||
return new de.bluecolored.bluemap.api.marker.Shape(pointArray);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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 MarkerSet markerSet;
|
||||
private Map<World, MarkerDescription> markers;
|
||||
private int weight = 3;
|
||||
|
||||
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);
|
||||
int color = (this.color.getRed() << 16) + (this.color.getGreen() << 8) + this.color.getBlue();
|
||||
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);
|
||||
this.weight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,50 +0,0 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import com.wimbli.WorldBorder.BorderData;
|
||||
import com.wimbli.WorldBorder.Config;
|
||||
import org.bukkit.Location;
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
import org.popcraft.chunky.util.Coordinate;
|
||||
|
||||
public class WorldBorderIntegration implements BorderIntegration {
|
||||
@Override
|
||||
public boolean hasBorder(String world) {
|
||||
return Config.Border(world) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Border getBorder(String world) {
|
||||
return new Border() {
|
||||
@Override
|
||||
public Coordinate getCenter() {
|
||||
BorderData borderData = Config.Border(world);
|
||||
return new Coordinate(Location.locToBlock(borderData.getX()), Location.locToBlock(borderData.getZ()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRadiusX() {
|
||||
return Config.Border(world).getRadiusX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRadiusZ() {
|
||||
return Config.Border(world).getRadiusZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
BorderData borderData = Config.Border(world);
|
||||
int radiusX = getRadiusX();
|
||||
int radiusZ = getRadiusZ();
|
||||
boolean round = borderData.getShape() != null && borderData.getShape();
|
||||
final String shape;
|
||||
if (radiusX == radiusZ) {
|
||||
shape = round ? "circle" : "square";
|
||||
} else {
|
||||
shape = round ? "oval" : "rectangle";
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,8 @@ public interface ChunkIterator extends Iterator<ChunkCoordinate> {
|
||||
long total();
|
||||
|
||||
String name();
|
||||
|
||||
default boolean process() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +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":
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
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;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private int xCenter, zCenter;
|
||||
private int annulus, span;
|
||||
private int down, left, up, right;
|
||||
private int radiusChunks;
|
||||
private long total;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public ConcentricChunkIterator(Selection selection, long count) {
|
||||
public ConcentricChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
@@ -25,7 +27,7 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
this.x += annulus;
|
||||
this.z += annulus;
|
||||
this.span = 2 * annulus;
|
||||
long perimeterCount = count - (diameterFinished * diameterFinished) + 1;
|
||||
long perimeterCount = count - ((long) diameterFinished * diameterFinished) + 1;
|
||||
this.down += Math.min(span, perimeterCount);
|
||||
perimeterCount -= Math.min(span, perimeterCount);
|
||||
this.left += Math.min(span, perimeterCount);
|
||||
@@ -40,13 +42,13 @@ public class ConcentricChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public ConcentricChunkIterator(Selection selection) {
|
||||
this.radiusChunks = selection.getRadiusChunksX();
|
||||
this.x = selection.getChunkX();
|
||||
this.z = selection.getChunkZ();
|
||||
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.getDiameterChunksX();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
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;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private int x1, x2, z1, z2;
|
||||
private long diameterChunksZ;
|
||||
private long total;
|
||||
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,19 +24,19 @@ public class Loop2ChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public Loop2ChunkIterator(Selection selection) {
|
||||
int radiusChunksX = selection.getRadiusChunksX();
|
||||
int radiusChunksZ = selection.getRadiusChunksZ();
|
||||
int centerChunkX = selection.getChunkX();
|
||||
int centerChunkZ = selection.getChunkZ();
|
||||
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.getDiameterChunksX();
|
||||
this.diameterChunksZ = selection.getDiameterChunksZ();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
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;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private int stopX, stopZ;
|
||||
private int span = 1, spanCount, spanProgress;
|
||||
private int direction;
|
||||
private long total;
|
||||
private final static int RIGHT = 0, DOWN = 1, LEFT = 2, UP = 3;
|
||||
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,10 +26,10 @@ 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 - diameterFinished * diameterFinished;
|
||||
long perimeterCount = count - (long) diameterFinished * diameterFinished;
|
||||
long spanned;
|
||||
spanned = Math.min(span, perimeterCount);
|
||||
this.z -= spanned;
|
||||
@@ -71,13 +73,13 @@ public class SpiralChunkIterator implements ChunkIterator {
|
||||
}
|
||||
}
|
||||
|
||||
public SpiralChunkIterator(Selection selection) {
|
||||
int radiusChunks = selection.getRadiusChunksX();
|
||||
this.x = selection.getChunkX();
|
||||
this.z = selection.getChunkZ();
|
||||
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.getDiameterChunksX();
|
||||
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,19 +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;
|
||||
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) {
|
||||
@@ -126,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,13 +1,13 @@
|
||||
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();
|
||||
|
||||
int getRadiusX();
|
||||
double getRadiusX();
|
||||
|
||||
int getRadiusZ();
|
||||
double getRadiusZ();
|
||||
|
||||
String getShape();
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.GenerationTask;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface Config {
|
||||
Optional<GenerationTask> loadTask(World world);
|
||||
Path getDirectory();
|
||||
|
||||
List<GenerationTask> loadTasks();
|
||||
int getVersion();
|
||||
|
||||
void saveTask(GenerationTask generationTask);
|
||||
String getLanguage();
|
||||
|
||||
void saveTasks();
|
||||
boolean getContinueOnRestart();
|
||||
|
||||
void cancelTasks();
|
||||
boolean isForceLoadExistingChunks();
|
||||
|
||||
boolean isSilent();
|
||||
|
||||
void setSilent(boolean silent);
|
||||
|
||||
int getUpdateInterval();
|
||||
|
||||
void setUpdateInterval(int updateInterval);
|
||||
|
||||
void reload();
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
public interface Platform {
|
||||
Server getServer();
|
||||
}
|
||||
@@ -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,7 +0,0 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
public interface Scheduler {
|
||||
void runTaskAsync(Runnable runnable);
|
||||
|
||||
void cancelTasks();
|
||||
}
|
||||
@@ -1,7 +1,25 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
public interface Sender {
|
||||
boolean isPlayer();
|
||||
|
||||
void sendMessage(String key, Object... args);
|
||||
String getName();
|
||||
|
||||
World getWorld();
|
||||
|
||||
Location getLocation();
|
||||
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
void sendMessage(String key, boolean prefixed, Object... args);
|
||||
|
||||
default void sendMessage(String key, Object... args) {
|
||||
sendMessage(key, false, args);
|
||||
}
|
||||
|
||||
default void sendMessagePrefixed(String key, Object... args) {
|
||||
sendMessage(key, true, 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,9 +14,13 @@ public interface Server {
|
||||
|
||||
List<World> getWorlds();
|
||||
|
||||
Sender getConsoleSender();
|
||||
int getMaxWorldSize();
|
||||
|
||||
Scheduler getScheduler();
|
||||
Sender getConsole();
|
||||
|
||||
Collection<Player> getPlayers();
|
||||
|
||||
Optional<Player> getPlayer(String name);
|
||||
|
||||
Config getConfig();
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
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;
|
||||
import java.util.UUID;
|
||||
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);
|
||||
|
||||
@@ -16,7 +20,29 @@ public interface World {
|
||||
|
||||
int getSeaLevel();
|
||||
|
||||
Coordinate getSpawnCoordinate();
|
||||
Location getSpawn();
|
||||
|
||||
Border getWorldBorder();
|
||||
|
||||
int getElevation(int x, int z);
|
||||
|
||||
int getMaxElevation();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package org.popcraft.chunky.platform.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.popcraft.chunky.platform.Config;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
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.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class GsonConfig implements Config {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private final Path savePath;
|
||||
private ConfigModel configModel = new ConfigModel();
|
||||
|
||||
public GsonConfig(final Path savePath) {
|
||||
this.savePath = savePath;
|
||||
if (Files.exists(this.savePath)) {
|
||||
reload();
|
||||
} else {
|
||||
saveConfig();
|
||||
}
|
||||
Translator.setLanguage(getLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDirectory() {
|
||||
return savePath.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return Optional.ofNullable(configModel.version).orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguage() {
|
||||
return Optional.ofNullable(configModel.language).map(Input::checkLanguage).orElse("en");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getContinueOnRestart() {
|
||||
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() {
|
||||
try (final Reader reader = Files.newBufferedReader(savePath)) {
|
||||
configModel = GSON.fromJson(reader, ConfigModel.class);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
public 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 {
|
||||
public 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,36 +1,32 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public abstract class AbstractShape implements Shape {
|
||||
protected int centerX, centerZ;
|
||||
protected int diameterX, diameterZ;
|
||||
protected int radiusX, radiusZ;
|
||||
protected int x1, x2, z1, z2;
|
||||
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 = (selection.getChunkX() << 4) + 8;
|
||||
this.centerZ = (selection.getChunkZ() << 4) + 8;
|
||||
this.diameterX = selection.getDiameterChunksX() << 4;
|
||||
this.diameterZ = selection.getDiameterChunksZ() << 4;
|
||||
this.centerX = (double) (selection.centerChunkX() << 4) + 8;
|
||||
this.centerZ = (double) (selection.centerChunkZ() << 4) + 8;
|
||||
this.diameterX = selection.diameterChunksX() << 4;
|
||||
this.diameterZ = selection.diameterChunksZ() << 4;
|
||||
this.radiusX = diameterX / 2;
|
||||
this.radiusZ = diameterZ / 2;
|
||||
} else {
|
||||
this.centerX = selection.centerX;
|
||||
this.centerZ = selection.centerZ;
|
||||
this.radiusX = selection.radiusX;
|
||||
this.radiusZ = selection.radiusZ;
|
||||
this.centerX = selection.centerX();
|
||||
this.centerZ = selection.centerZ();
|
||||
this.radiusX = selection.radiusX();
|
||||
this.radiusZ = selection.radiusZ();
|
||||
this.diameterX = 2 * radiusX;
|
||||
this.diameterZ = 2 * radiusZ;
|
||||
}
|
||||
this.x1 = centerX - radiusX;
|
||||
this.x2 = centerX + radiusX;
|
||||
this.z1 = centerZ - radiusZ;
|
||||
this.z2 = centerZ + radiusZ;
|
||||
}
|
||||
|
||||
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) {
|
||||
return Math.hypot(centerX - x, centerZ - z) < radiusX;
|
||||
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 {
|
||||
int p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 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,19 +0,0 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
|
||||
public class Oval extends AbstractEllipse {
|
||||
public Oval(Selection selection, boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(double x, 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 "oval";
|
||||
}
|
||||
}
|
||||
@@ -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 double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z;
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 214 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user