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 | ||
|
|
fec471848e | ||
|
|
b502eed50d | ||
|
|
969f87e72d | ||
|
|
f72425036e | ||
|
|
2555022a99 | ||
|
|
687ba0672e | ||
|
|
23639cc769 | ||
|
|
304e11162c | ||
|
|
c543ab9b9c | ||
|
|
6281a074a7 | ||
|
|
d0ed0b244c | ||
|
|
46f89d0ab5 | ||
|
|
86d1350dfd | ||
|
|
3c8e8a8090 | ||
|
|
984dd5d9db | ||
|
|
cc92958597 | ||
|
|
83f0e0affb | ||
|
|
1ea2855e0f | ||
|
|
110b0986f6 | ||
|
|
89da2e63b4 | ||
|
|
5a181d8347 | ||
|
|
8c96948793 | ||
|
|
1576da7e0e | ||
|
|
309d0a3d40 | ||
|
|
f2f078d1e3 | ||
|
|
fc86380b61 | ||
|
|
1d13e2c12e | ||
|
|
bc1f703021 | ||
|
|
dbe7ce23bb | ||
|
|
f936a6d105 | ||
|
|
74e626207f | ||
|
|
4a301f6a9f | ||
|
|
33a03c2b7b | ||
|
|
3f8d2dff7b | ||
|
|
111f268bfb | ||
|
|
287b76f59a | ||
|
|
7263c1b1b9 | ||
|
|
b568ad04ea | ||
|
|
d6832bc242 | ||
|
|
fa33e6acf3 | ||
|
|
1d704f6eaf | ||
|
|
10a9974def | ||
|
|
ff1bc37845 | ||
|
|
ddedd9e62c | ||
|
|
b4e7bd20e3 | ||
|
|
4328673ecc | ||
|
|
a4912c75ed | ||
|
|
212d63c56e | ||
|
|
080c7176cf | ||
|
|
c742151997 | ||
|
|
d8c1de0f1a | ||
|
|
3fb43d4462 | ||
|
|
19c10c781d | ||
|
|
118184c2f8 | ||
|
|
02713d65e4 | ||
|
|
ff9e0452e9 | ||
|
|
ced751f5f0 | ||
|
|
e4b2ac40fd | ||
|
|
9216f49897 | ||
|
|
a3bd27fc9f | ||
|
|
506dba87bf | ||
|
|
b21a214535 | ||
|
|
8a52854fb9 | ||
|
|
284cf0edd3 | ||
|
|
9ceeb7b753 | ||
|
|
015f9fe486 | ||
|
|
41093c39eb | ||
|
|
5e709ac872 | ||
|
|
e6c773de1c | ||
|
|
428e970a2a | ||
|
|
3b01fef88f | ||
|
|
98e7cc7793 | ||
|
|
0a88ead2ab | ||
|
|
d0e7c09ac6 | ||
|
|
49ab982c0f | ||
|
|
f433c4b9f1 | ||
|
|
4f98a5085f | ||
|
|
f01a25919d | ||
|
|
5fa68e0537 | ||
|
|
dba5f4301e |
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
|
||||
+202
-17
@@ -1,13 +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
|
||||
|
||||
@@ -17,6 +170,9 @@ out/
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
@@ -29,6 +185,7 @@ out/
|
||||
# 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
|
||||
@@ -43,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
|
||||
@@ -51,6 +209,7 @@ hs_err_pid*
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
@@ -70,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
|
||||
@@ -95,19 +277,22 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
target/
|
||||
### Gradle ###
|
||||
.gradle
|
||||
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
.flattened-pom.xml
|
||||
# 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.
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.WorldInitEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.api.ChunkyAPI;
|
||||
import org.popcraft.chunky.command.ChunkyCommand;
|
||||
import org.popcraft.chunky.command.CommandArguments;
|
||||
import org.popcraft.chunky.command.CommandLiteral;
|
||||
import org.popcraft.chunky.integration.WorldBorderIntegration;
|
||||
import org.popcraft.chunky.platform.BukkitConfig;
|
||||
import org.popcraft.chunky.platform.BukkitPlayer;
|
||||
import org.popcraft.chunky.platform.BukkitSender;
|
||||
import org.popcraft.chunky.platform.BukkitServer;
|
||||
import org.popcraft.chunky.platform.Folia;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
import org.popcraft.chunky.util.Version;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public final class ChunkyBukkit extends JavaPlugin implements Listener {
|
||||
private static final String COMMAND_PERMISSION_KEY = "chunky.command.";
|
||||
private Chunky chunky;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.chunky = new Chunky(new BukkitServer(this), new BukkitConfig(this));
|
||||
final Version currentVersion = new Version(Bukkit.getBukkitVersion(), true);
|
||||
if (currentVersion.isValid() && Version.MINECRAFT_1_13_2.isHigherThan(currentVersion)) {
|
||||
getLogger().severe(() -> translate(TranslationKey.ERROR_VERSION));
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
getServer().getServicesManager().register(Chunky.class, chunky, this, ServicePriority.Normal);
|
||||
getServer().getServicesManager().register(ChunkyAPI.class, chunky.getApi(), this, ServicePriority.Normal);
|
||||
if (chunky.getConfig().getContinueOnRestart()) {
|
||||
final Runnable continueTask = () -> chunky.getCommands().get(CommandLiteral.CONTINUE).execute(chunky.getServer().getConsole(), CommandArguments.empty());
|
||||
if (Folia.isFolia()) {
|
||||
Folia.onServerInit(this, continueTask);
|
||||
} else {
|
||||
getServer().getScheduler().scheduleSyncDelayedTask(this, continueTask);
|
||||
}
|
||||
}
|
||||
if (getServer().getPluginManager().getPlugin("WorldBorder") != null) {
|
||||
chunky.getServer().getIntegrations().put("border", new WorldBorderIntegration());
|
||||
}
|
||||
final Metrics metrics = new Metrics(this, 8211);
|
||||
metrics.addCustomChart(new SimplePie("language", () -> chunky.getConfig().getLanguage()));
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
HandlerList.unregisterAll((Plugin) this);
|
||||
if (chunky != null) {
|
||||
chunky.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) {
|
||||
final Sender bukkitSender = sender instanceof final Player player ? new BukkitPlayer(player) : new BukkitSender(sender);
|
||||
final Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
final CommandArguments arguments = CommandArguments.of(Arrays.copyOfRange(args, Math.min(1, args.length), args.length));
|
||||
if (args.length > 0 && commands.containsKey(args[0].toLowerCase())) {
|
||||
if (sender.hasPermission(COMMAND_PERMISSION_KEY + args[0].toLowerCase())) {
|
||||
commands.get(args[0].toLowerCase()).execute(bukkitSender, arguments);
|
||||
} else {
|
||||
bukkitSender.sendMessage(TranslationKey.COMMAND_NO_PERMISSION);
|
||||
}
|
||||
} else {
|
||||
commands.get(CommandLiteral.HELP).execute(bukkitSender, arguments);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public List<String> onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) {
|
||||
if (args.length < 1) {
|
||||
return List.of();
|
||||
}
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
final Map<String, ChunkyCommand> commands = chunky.getCommands();
|
||||
if (args.length == 1) {
|
||||
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()))
|
||||
.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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.bukkit.WorldBorder;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public class BukkitBorder implements Border {
|
||||
final WorldBorder worldBorder;
|
||||
|
||||
public BukkitBorder(final WorldBorder worldBorder) {
|
||||
this.worldBorder = worldBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 getCenter() {
|
||||
return Vector2.of(worldBorder.getCenter().getX(), worldBorder.getCenter().getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadiusX() {
|
||||
return worldBorder.getSize() / 2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadiusZ() {
|
||||
return worldBorder.getSize() / 2d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShape() {
|
||||
return ShapeType.SQUARE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
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.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class BukkitConfig implements Config {
|
||||
private static final List<String> HEADER = Arrays.asList("Chunky Configuration", "https://github.com/pop4959/Chunky/wiki/Configuration");
|
||||
private final ChunkyBukkit plugin;
|
||||
|
||||
public BukkitConfig(final ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
final FileConfigurationOptions options = plugin.getConfig().options();
|
||||
options.copyDefaults(true);
|
||||
try {
|
||||
FileConfigurationOptions.class.getMethod("header", String.class).invoke(options, String.join("\n", HEADER));
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
options.setHeader(HEADER);
|
||||
}
|
||||
plugin.saveConfig();
|
||||
Translator.setLanguage(getLanguage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDirectory() {
|
||||
return plugin.getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void reload() {
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.popcraft.chunky.ChunkyBukkit;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translateKey;
|
||||
|
||||
public class BukkitPlayer extends BukkitSender implements Player {
|
||||
private static final boolean ACTION_BAR_SUPPORTED;
|
||||
private final JavaPlugin plugin = JavaPlugin.getPlugin(ChunkyBukkit.class);
|
||||
|
||||
static {
|
||||
boolean barSupported;
|
||||
try {
|
||||
org.bukkit.entity.Player.class.getMethod("spigot");
|
||||
barSupported = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
barSupported = false;
|
||||
}
|
||||
ACTION_BAR_SUPPORTED = barSupported;
|
||||
}
|
||||
|
||||
final org.bukkit.entity.Player player;
|
||||
|
||||
public BukkitPlayer(final org.bukkit.entity.Player player) {
|
||||
super(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return new BukkitWorld(player.getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
final org.bukkit.Location location = player.getLocation();
|
||||
return new Location(getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Override
|
||||
public void teleport(final Location location) {
|
||||
final org.bukkit.World world = Bukkit.getWorld(location.getWorld().getName());
|
||||
final org.bukkit.Location loc = new org.bukkit.Location(world, location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
||||
final Entity vehicle = player.getVehicle();
|
||||
if (vehicle == null) {
|
||||
teleportAsync(player, loc);
|
||||
} else {
|
||||
final List<Entity> passengers = new ArrayList<>(vehicle.getPassengers());
|
||||
vehicle.eject();
|
||||
teleportAsync(player, loc).thenAcceptAsync(ignored -> {
|
||||
vehicle.teleport(player);
|
||||
for (final Entity passenger : passengers) {
|
||||
passenger.teleport(player);
|
||||
if (passenger instanceof final org.bukkit.entity.Player playerPassenger) {
|
||||
playerPassenger.hideEntity(plugin, vehicle);
|
||||
playerPassenger.showEntity(plugin, vehicle);
|
||||
}
|
||||
vehicle.addPassenger(passenger);
|
||||
}
|
||||
}, command -> plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, command, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> teleportAsync(final Entity entity, final org.bukkit.Location location) {
|
||||
if (Paper.isPaper()) {
|
||||
return Paper.teleportAsync(entity, location);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(entity.teleport(location));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendActionBar(final String key) {
|
||||
if (ACTION_BAR_SUPPORTED) {
|
||||
final String message = formatColored(translateKey(key, false));
|
||||
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
|
||||
} else {
|
||||
this.sendMessage(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
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 java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translateKey;
|
||||
|
||||
public class BukkitSender implements Sender {
|
||||
private static final Pattern RGB_PATTERN = Pattern.compile("&#[0-9a-fA-F]{6}");
|
||||
private static final boolean RGB_COLORS_SUPPORTED;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
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 final ChunkyBukkit plugin;
|
||||
private final Map<String, Integration> integrations;
|
||||
|
||||
public BukkitServer(final ChunkyBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
this.integrations = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integration> getIntegrations() {
|
||||
return integrations;
|
||||
}
|
||||
|
||||
@Override
|
||||
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() {
|
||||
final List<World> worlds = new ArrayList<>();
|
||||
plugin.getServer().getWorlds().forEach(world -> worlds.add(new BukkitWorld(world)));
|
||||
return worlds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxWorldSize() {
|
||||
return plugin.getServer().getMaxWorldSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getConsole() {
|
||||
return new BukkitSender(plugin.getServer().getConsoleSender());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Player> getPlayers() {
|
||||
final Collection<Player> players = new ArrayList<>();
|
||||
plugin.getServer().getOnlinePlayers().forEach(player -> players.add(new BukkitPlayer(player)));
|
||||
return players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Player> getPlayer(final String name) {
|
||||
return Optional.ofNullable(plugin.getServer().getPlayer(name)).map(BukkitPlayer::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config getConfig() {
|
||||
return plugin.getChunky().getConfig();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
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 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;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return world.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return world.getKey().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public UUID getUUID() {
|
||||
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 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 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Chunky Configuration
|
||||
# https://github.com/pop4959/Chunky/wiki/Configuration
|
||||
|
||||
version: 2
|
||||
language: en
|
||||
continue-on-restart: true
|
||||
force-load-existing-chunks: false
|
||||
silent: false
|
||||
update-interval: 1
|
||||
@@ -0,0 +1,105 @@
|
||||
name: ${name}
|
||||
version: ${version}
|
||||
main: ${group}.chunky.ChunkyBukkit
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
softdepend: [dynmap, BlueMap, squaremap, WorldBorder]
|
||||
load: POSTWORLD
|
||||
authors: [${author}]
|
||||
description: ${description}
|
||||
website: https://www.spigotmc.org/resources/chunky.81534/
|
||||
commands:
|
||||
chunky:
|
||||
description: Generates chunks
|
||||
aliases: [cy]
|
||||
permission: chunky.command
|
||||
usage: /chunky
|
||||
permissions:
|
||||
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
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.api.ChunkyAPI;
|
||||
import org.popcraft.chunky.api.ChunkyAPIImpl;
|
||||
import org.popcraft.chunky.command.CancelCommand;
|
||||
import org.popcraft.chunky.command.CenterCommand;
|
||||
import org.popcraft.chunky.command.ChunkyCommand;
|
||||
import org.popcraft.chunky.command.CommandLiteral;
|
||||
import org.popcraft.chunky.command.ConfirmCommand;
|
||||
import org.popcraft.chunky.command.ContinueCommand;
|
||||
import org.popcraft.chunky.command.CornersCommand;
|
||||
import org.popcraft.chunky.command.HelpCommand;
|
||||
import org.popcraft.chunky.command.PatternCommand;
|
||||
import org.popcraft.chunky.command.PauseCommand;
|
||||
import org.popcraft.chunky.command.ProgressCommand;
|
||||
import org.popcraft.chunky.command.QuietCommand;
|
||||
import org.popcraft.chunky.command.RadiusCommand;
|
||||
import org.popcraft.chunky.command.ReloadCommand;
|
||||
import org.popcraft.chunky.command.SelectionCommand;
|
||||
import org.popcraft.chunky.command.ShapeCommand;
|
||||
import org.popcraft.chunky.command.SilentCommand;
|
||||
import org.popcraft.chunky.command.SpawnCommand;
|
||||
import org.popcraft.chunky.command.StartCommand;
|
||||
import org.popcraft.chunky.command.TrimCommand;
|
||||
import org.popcraft.chunky.command.WorldBorderCommand;
|
||||
import org.popcraft.chunky.command.WorldCommand;
|
||||
import org.popcraft.chunky.event.EventBus;
|
||||
import org.popcraft.chunky.platform.Config;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.Server;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.PendingAction;
|
||||
import org.popcraft.chunky.util.RegionCache;
|
||||
import org.popcraft.chunky.util.TaskLoader;
|
||||
import org.popcraft.chunky.util.TaskScheduler;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
import org.popcraft.chunky.util.Version;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
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 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(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 disable() {
|
||||
taskLoader.saveTasks();
|
||||
getGenerationTasks().values().forEach(generationTask -> generationTask.stop(false));
|
||||
getScheduler().cancelTasks();
|
||||
ChunkyProvider.unregister();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private Version loadVersion() {
|
||||
try (final InputStream input = getClass().getClassLoader().getResourceAsStream("version.properties")) {
|
||||
final Properties properties = new Properties();
|
||||
properties.load(input);
|
||||
return new Version(properties.getProperty("version"));
|
||||
} catch (IOException e) {
|
||||
return Version.INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ChunkyCommand> loadCommands() {
|
||||
final Map<String, ChunkyCommand> commandMap = new HashMap<>();
|
||||
commandMap.put(CommandLiteral.CANCEL, new CancelCommand(this));
|
||||
commandMap.put(CommandLiteral.CENTER, new CenterCommand(this));
|
||||
commandMap.put(CommandLiteral.CONFIRM, new ConfirmCommand(this));
|
||||
commandMap.put(CommandLiteral.CONTINUE, new ContinueCommand(this));
|
||||
commandMap.put(CommandLiteral.CORNERS, new CornersCommand(this));
|
||||
commandMap.put(CommandLiteral.HELP, new HelpCommand(this));
|
||||
commandMap.put(CommandLiteral.PATTERN, new PatternCommand(this));
|
||||
commandMap.put(CommandLiteral.PAUSE, new PauseCommand(this));
|
||||
commandMap.put(CommandLiteral.PROGRESS, new ProgressCommand(this));
|
||||
commandMap.put(CommandLiteral.QUIET, new QuietCommand(this));
|
||||
commandMap.put(CommandLiteral.RADIUS, new RadiusCommand(this));
|
||||
commandMap.put(CommandLiteral.RELOAD, new ReloadCommand(this));
|
||||
commandMap.put(CommandLiteral.SELECTION, new SelectionCommand(this));
|
||||
commandMap.put(CommandLiteral.SHAPE, new ShapeCommand(this));
|
||||
commandMap.put(CommandLiteral.SILENT, new SilentCommand(this));
|
||||
commandMap.put(CommandLiteral.SPAWN, new SpawnCommand(this));
|
||||
commandMap.put(CommandLiteral.START, new StartCommand(this));
|
||||
commandMap.put(CommandLiteral.TRIM, new TrimCommand(this));
|
||||
commandMap.put(CommandLiteral.WORLDBORDER, new WorldBorderCommand(this));
|
||||
commandMap.put(CommandLiteral.WORLD, new WorldCommand(this));
|
||||
return commandMap;
|
||||
}
|
||||
|
||||
public TaskScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public TaskLoader getTaskLoader() {
|
||||
return taskLoader;
|
||||
}
|
||||
|
||||
public EventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
public Map<String, GenerationTask> getGenerationTasks() {
|
||||
return generationTasks;
|
||||
}
|
||||
|
||||
public Map<String, TrimCommand.Task> getTrimTasks() {
|
||||
return trimTasks;
|
||||
}
|
||||
|
||||
public Map<String, ChunkyCommand> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
public Selection.Builder getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package org.popcraft.chunky;
|
||||
|
||||
import org.popcraft.chunky.api.event.task.GenerationCompleteEvent;
|
||||
import org.popcraft.chunky.api.event.task.GenerationProgressEvent;
|
||||
import org.popcraft.chunky.event.task.GenerationTaskFinishEvent;
|
||||
import org.popcraft.chunky.event.task.GenerationTaskUpdateEvent;
|
||||
import org.popcraft.chunky.iterator.ChunkIterator;
|
||||
import org.popcraft.chunky.iterator.ChunkIteratorFactory;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.shape.Shape;
|
||||
import org.popcraft.chunky.shape.ShapeFactory;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.Pair;
|
||||
import org.popcraft.chunky.util.RegionCache;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class GenerationTask implements Runnable {
|
||||
private static final int MAX_WORKING_COUNT = Input.tryInteger(System.getProperty("chunky.maxWorkingCount")).orElse(50);
|
||||
private static final double SAMPLE_INTERVAL = 1000d * Math.max(Input.tryInteger(System.getProperty("chunky.sampleInterval")).orElse(30), 30);
|
||||
private static final double SAMPLE_SUB_INTERVAL = SAMPLE_INTERVAL / 30;
|
||||
private final Chunky chunky;
|
||||
private final Selection selection;
|
||||
private final Shape shape;
|
||||
private final AtomicLong startTime = new AtomicLong();
|
||||
private final AtomicLong 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 boolean stopped, cancelled;
|
||||
private long prevTime;
|
||||
|
||||
public GenerationTask(final Chunky chunky, final Selection selection, final long count, final long time, final boolean cancelled) {
|
||||
this(chunky, selection);
|
||||
this.chunkIterator = ChunkIteratorFactory.getChunkIterator(selection, count);
|
||||
this.finishedChunks.set(count);
|
||||
this.cancelled = cancelled;
|
||||
this.prevTime = time;
|
||||
}
|
||||
|
||||
public GenerationTask(final Chunky chunky, final Selection selection) {
|
||||
this.chunky = chunky;
|
||||
this.selection = selection;
|
||||
this.chunkIterator = ChunkIteratorFactory.getChunkIterator(selection);
|
||||
this.shape = ShapeFactory.getShape(selection);
|
||||
this.progress = new Progress(selection.world().getName());
|
||||
this.worldState = chunky.getRegionCache().getWorld(selection.world().getName());
|
||||
}
|
||||
|
||||
private synchronized void update(final int chunkX, final int chunkZ, final boolean loaded) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
progress.chunkCount = finishedChunks.addAndGet(1);
|
||||
progress.percentComplete = 100f * progress.chunkCount / 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
time = (prevTime + (currentTime - startTime.get())) / 1000;
|
||||
progress.complete = true;
|
||||
} else {
|
||||
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", 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 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;
|
||||
}
|
||||
final CompletableFuture<Boolean> isChunkGenerated = forceLoadExistingChunks ?
|
||||
CompletableFuture.completedFuture(false) :
|
||||
selection.world().isChunkGenerated(chunk.x(), chunk.z());
|
||||
isChunkGenerated
|
||||
.thenCompose(generated -> {
|
||||
if (Boolean.TRUE.equals(generated)) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return selection.world().getChunkAtAsync(chunk.x(), chunk.z());
|
||||
}
|
||||
}).whenComplete((ignored, throwable) -> {
|
||||
working.release();
|
||||
update(chunk.x(), chunk.z(), true);
|
||||
});
|
||||
}
|
||||
if (stopped) {
|
||||
chunky.getServer().getConsole().sendMessagePrefixed(TranslationKey.TASK_STOPPED, selection.world().getName());
|
||||
} else {
|
||||
cancelled = true;
|
||||
}
|
||||
chunky.getTaskLoader().saveTask(this);
|
||||
chunky.getGenerationTasks().remove(selection.world().getName());
|
||||
Thread.currentThread().setName(poolThreadName);
|
||||
chunky.getEventBus().call(new GenerationTaskFinishEvent(this));
|
||||
chunky.getEventBus().call(new GenerationCompleteEvent(selection.world().getName()));
|
||||
}
|
||||
|
||||
public void stop(final boolean cancelled) {
|
||||
this.stopped = true;
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public Chunky getChunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public Selection getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return finishedChunks.get();
|
||||
}
|
||||
|
||||
public ChunkIterator getChunkIterator() {
|
||||
return chunkIterator;
|
||||
}
|
||||
|
||||
public Shape getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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;
|
||||
|
||||
@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;
|
||||
|
||||
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 static Builder builder(final Chunky chunky, final World world) {
|
||||
return new Builder(chunky, world);
|
||||
}
|
||||
|
||||
public Chunky chunky() {
|
||||
return chunky;
|
||||
}
|
||||
|
||||
public World world() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
public double centerX() {
|
||||
return this.centerX;
|
||||
}
|
||||
|
||||
public double centerZ() {
|
||||
return this.centerZ;
|
||||
}
|
||||
|
||||
public double radiusX() {
|
||||
return this.radiusX;
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
@@ -0,0 +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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CancelCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CancelCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Map<String, GenerationTask> generationTasks = chunky.getGenerationTasks();
|
||||
final Map<String, TrimCommand.Task> trimTasks = chunky.getTrimTasks();
|
||||
if (generationTasks.isEmpty()
|
||||
&& chunky.getTaskLoader().loadTasks().stream().allMatch(GenerationTask::isCancelled)
|
||||
&& trimTasks.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL_NO_TASKS);
|
||||
return;
|
||||
}
|
||||
final Runnable cancelAction;
|
||||
if (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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
import org.popcraft.chunky.util.Formatting;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CenterCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public CenterCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@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 (Input.isPastWorldLimit(centerX) || Input.isPastWorldLimit(centerZ)) {
|
||||
sender.sendMessage(TranslationKey.HELP_CENTER);
|
||||
return;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ChunkyCommand {
|
||||
void execute(Sender sender, CommandArguments arguments);
|
||||
|
||||
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() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfirmCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ConfirmCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<Runnable> pendingAction = chunky.getPendingAction(sender);
|
||||
if (pendingAction.isEmpty()) {
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_CONFIRM);
|
||||
return;
|
||||
}
|
||||
pendingAction.get().run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ContinueCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ContinueCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@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<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.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class HelpCommand 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(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class PatternCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public PatternCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PauseCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public PauseCommand(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_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.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class QuietCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public QuietCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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;
|
||||
|
||||
public class RadiusCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public RadiusCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Sender sender, final CommandArguments arguments) {
|
||||
final Optional<String> newX = arguments.next();
|
||||
final Optional<Integer> signX = newX.flatMap(Input::trySign);
|
||||
final Optional<Double> newRadiusX = newX.map(x -> signX.isPresent() ? x.substring(1) : x).flatMap(Input::tryDoubleSuffixed);
|
||||
if (newRadiusX.isEmpty() || newRadiusX.get() < 0 || Input.isPastWorldLimit(newRadiusX.get())) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
final Selection current = chunky.getSelection().build();
|
||||
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;
|
||||
}
|
||||
final Optional<String> newZ = arguments.next();
|
||||
if (newZ.isPresent()) {
|
||||
final Optional<Integer> signZ = newZ.flatMap(Input::trySign);
|
||||
final Optional<Double> newRadiusZ = newZ.map(z -> signZ.isPresent() ? z.substring(1) : z).flatMap(Input::tryDoubleSuffixed);
|
||||
if (newRadiusZ.isEmpty() || newRadiusZ.get() < 0 || Input.isPastWorldLimit(newRadiusZ.get())) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
final double radiusZ = signZ.map(sign -> current.radiusZ() + sign * newRadiusZ.get()).orElseGet(newRadiusZ::get);
|
||||
if (radiusZ < 0 || Input.isPastWorldLimit(radiusZ)) {
|
||||
sender.sendMessage(TranslationKey.HELP_RADIUS);
|
||||
return;
|
||||
}
|
||||
chunky.getSelection().radiusX(radiusX).radiusZ(radiusZ);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADII, Formatting.number(radiusX), Formatting.number(radiusZ));
|
||||
} else {
|
||||
chunky.getSelection().radius(radiusX);
|
||||
sender.sendMessagePrefixed(TranslationKey.FORMAT_RADIUS, Formatting.number(radiusX));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggestions(final CommandArguments arguments) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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 java.util.List;
|
||||
|
||||
public class ReloadCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ReloadCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class ShapeCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public ShapeCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.util.Translator.translate;
|
||||
|
||||
public class SilentCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SilentCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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.Formatting;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SpawnCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public SpawnCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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 = () -> {
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
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.platform.Border;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
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;
|
||||
|
||||
public class WorldBorderCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public WorldBorderCommand(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_WORLDBORDER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.popcraft.chunky.command;
|
||||
|
||||
import org.popcraft.chunky.Chunky;
|
||||
import org.popcraft.chunky.platform.Player;
|
||||
import org.popcraft.chunky.platform.Sender;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
import org.popcraft.chunky.util.Input;
|
||||
import org.popcraft.chunky.util.TranslationKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WorldCommand implements ChunkyCommand {
|
||||
private final Chunky chunky;
|
||||
|
||||
public WorldCommand(final Chunky chunky) {
|
||||
this.chunky = chunky;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
import org.popcraft.chunky.platform.Border;
|
||||
|
||||
public interface BorderIntegration extends Integration {
|
||||
boolean hasBorder(String world);
|
||||
|
||||
Border getBorder(String world);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.popcraft.chunky.integration;
|
||||
|
||||
public interface Integration {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public interface ChunkIterator extends Iterator<ChunkCoordinate> {
|
||||
long total();
|
||||
|
||||
String name();
|
||||
|
||||
default boolean process() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
public final class ChunkIteratorFactory {
|
||||
private ChunkIteratorFactory() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class ConcentricChunkIterator implements ChunkIterator {
|
||||
private final int xCenter, zCenter;
|
||||
private final int radiusChunks;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private int annulus, span;
|
||||
private int down, left, up, right;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public ConcentricChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
int diameterFinished = (int) Math.floor(Math.sqrt(count));
|
||||
if (diameterFinished % 2 == 0) {
|
||||
--diameterFinished;
|
||||
}
|
||||
this.annulus = diameterFinished / 2 + 1;
|
||||
this.x += annulus;
|
||||
this.z += annulus;
|
||||
this.span = 2 * annulus;
|
||||
long perimeterCount = count - ((long) diameterFinished * diameterFinished) + 1;
|
||||
this.down += Math.min(span, perimeterCount);
|
||||
perimeterCount -= Math.min(span, perimeterCount);
|
||||
this.left += Math.min(span, perimeterCount);
|
||||
perimeterCount -= Math.min(span, perimeterCount);
|
||||
this.up += Math.min(span, perimeterCount);
|
||||
perimeterCount -= Math.min(span, perimeterCount);
|
||||
this.right += Math.min(span, perimeterCount);
|
||||
this.x += right - left;
|
||||
this.z += up - down;
|
||||
if (annulus > radiusChunks) {
|
||||
hasNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
public ConcentricChunkIterator(final Selection selection) {
|
||||
this.radiusChunks = selection.radiusChunksX();
|
||||
this.x = selection.centerChunkX();
|
||||
this.z = selection.centerChunkZ();
|
||||
this.xCenter = x;
|
||||
this.zCenter = z;
|
||||
final long diameterChunks = selection.diameterChunksX();
|
||||
this.total = diameterChunks * diameterChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (x == xCenter + annulus && z == zCenter + annulus) {
|
||||
++annulus;
|
||||
++x;
|
||||
++z;
|
||||
if (annulus > radiusChunks) {
|
||||
hasNext = false;
|
||||
}
|
||||
span = 2 * annulus;
|
||||
down = left = up = right = 0;
|
||||
}
|
||||
if (down < span) {
|
||||
--z;
|
||||
++down;
|
||||
} else if (left < span) {
|
||||
--x;
|
||||
++left;
|
||||
} else if (up < span) {
|
||||
++z;
|
||||
++up;
|
||||
} else if (right < span) {
|
||||
++x;
|
||||
++right;
|
||||
}
|
||||
return chunkCoord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class Loop2ChunkIterator implements ChunkIterator {
|
||||
private final int x1, x2, z1, z2;
|
||||
private final long diameterChunksZ;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public Loop2ChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
this.x = x1 + (int) (count / diameterChunksZ);
|
||||
this.z = z1 + (int) (count % diameterChunksZ);
|
||||
if (x > x2) {
|
||||
hasNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
final int diameterChunksX = selection.diameterChunksX();
|
||||
this.diameterChunksZ = selection.diameterChunksZ();
|
||||
this.total = diameterChunksX * diameterChunksZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (++z > z2) {
|
||||
z = z1;
|
||||
if (++x > x2) {
|
||||
hasNext = false;
|
||||
}
|
||||
}
|
||||
return chunkCoord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package org.popcraft.chunky.iterator;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.ChunkCoordinate;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class SpiralChunkIterator implements ChunkIterator {
|
||||
private static final int RIGHT = 0, DOWN = 1, LEFT = 2, UP = 3;
|
||||
private final int stopX, stopZ;
|
||||
private final long total;
|
||||
private int x, z;
|
||||
private int span = 1, spanCount, spanProgress;
|
||||
private int direction;
|
||||
private boolean hasNext = true;
|
||||
|
||||
public SpiralChunkIterator(final Selection selection, final long count) {
|
||||
this(selection);
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
int diameterFinished = (int) Math.floor(Math.sqrt(count));
|
||||
if (diameterFinished % 2 == 0) {
|
||||
--diameterFinished;
|
||||
}
|
||||
this.span = diameterFinished;
|
||||
this.spanCount = 1;
|
||||
this.direction = DOWN;
|
||||
final int radiusFinished = diameterFinished / 2;
|
||||
this.x += radiusFinished + 1;
|
||||
this.z += radiusFinished;
|
||||
long perimeterCount = count - (long) diameterFinished * diameterFinished;
|
||||
long spanned;
|
||||
spanned = Math.min(span, perimeterCount);
|
||||
this.z -= spanned;
|
||||
if (spanned == span) {
|
||||
++span;
|
||||
spanCount = 0;
|
||||
direction = LEFT;
|
||||
} else {
|
||||
spanProgress += spanned;
|
||||
}
|
||||
perimeterCount -= spanned;
|
||||
spanned = Math.min(span, perimeterCount);
|
||||
this.x -= spanned;
|
||||
if (spanned == span) {
|
||||
spanCount = 1;
|
||||
direction = UP;
|
||||
} else {
|
||||
spanProgress += spanned;
|
||||
}
|
||||
perimeterCount -= spanned;
|
||||
spanned = Math.min(span, perimeterCount);
|
||||
this.z += spanned;
|
||||
if (spanned == span) {
|
||||
++span;
|
||||
spanCount = 0;
|
||||
direction = RIGHT;
|
||||
} else {
|
||||
spanProgress += spanned;
|
||||
}
|
||||
perimeterCount -= spanned;
|
||||
spanned = Math.min(span, perimeterCount);
|
||||
this.x += spanned;
|
||||
if (spanned == span) {
|
||||
spanCount = 1;
|
||||
direction = DOWN;
|
||||
} else {
|
||||
spanProgress += spanned;
|
||||
}
|
||||
if (x > stopX) {
|
||||
hasNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
final long diameter = selection.diameterChunksX();
|
||||
this.total = diameter * diameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkCoordinate next() {
|
||||
if (!hasNext) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
final ChunkCoordinate chunkCoord = new ChunkCoordinate(x, z);
|
||||
if (x == stopX && z == stopZ) {
|
||||
hasNext = false;
|
||||
}
|
||||
if (spanCount == 2) {
|
||||
spanCount = 0;
|
||||
++span;
|
||||
}
|
||||
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) {
|
||||
spanProgress = 0;
|
||||
++spanCount;
|
||||
direction = direction == UP ? RIGHT : ++direction;
|
||||
}
|
||||
return chunkCoord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long total() {
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public interface Border {
|
||||
Vector2 getCenter();
|
||||
|
||||
double getRadiusX();
|
||||
|
||||
double getRadiusZ();
|
||||
|
||||
String getShape();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface Config {
|
||||
Path getDirectory();
|
||||
|
||||
int getVersion();
|
||||
|
||||
String getLanguage();
|
||||
|
||||
boolean getContinueOnRestart();
|
||||
|
||||
boolean isForceLoadExistingChunks();
|
||||
|
||||
boolean isSilent();
|
||||
|
||||
void setSilent(boolean silent);
|
||||
|
||||
int getUpdateInterval();
|
||||
|
||||
void setUpdateInterval(int updateInterval);
|
||||
|
||||
void reload();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface Player extends Sender {
|
||||
UUID getUUID();
|
||||
|
||||
void teleport(Location location);
|
||||
|
||||
void sendActionBar(String key);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Location;
|
||||
|
||||
public interface Sender {
|
||||
boolean isPlayer();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
|
||||
public interface Server {
|
||||
Map<String, Integration> getIntegrations();
|
||||
|
||||
Optional<World> getWorld(String name);
|
||||
|
||||
List<World> getWorlds();
|
||||
|
||||
int getMaxWorldSize();
|
||||
|
||||
Sender getConsole();
|
||||
|
||||
Collection<Player> getPlayers();
|
||||
|
||||
Optional<Player> getPlayer(String name);
|
||||
|
||||
Config getConfig();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.popcraft.chunky.platform;
|
||||
|
||||
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();
|
||||
|
||||
String getKey();
|
||||
|
||||
CompletableFuture<Boolean> isChunkGenerated(int x, int z);
|
||||
|
||||
CompletableFuture<Void> getChunkAtAsync(int x, int z);
|
||||
|
||||
UUID getUUID();
|
||||
|
||||
int getSeaLevel();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
public abstract class AbstractEllipse extends AbstractShape {
|
||||
protected AbstractEllipse(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
public abstract Vector2 radii();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractPolygon extends AbstractShape {
|
||||
protected AbstractPolygon(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
public abstract List<Vector2> points();
|
||||
}
|
||||
@@ -0,0 +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 final double centerX, centerZ;
|
||||
protected final double diameterX, diameterZ;
|
||||
protected final double radiusX, radiusZ;
|
||||
|
||||
protected AbstractShape(final Selection selection, final boolean chunkAligned) {
|
||||
if (chunkAligned) {
|
||||
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.diameterX = 2 * radiusX;
|
||||
this.diameterZ = 2 * radiusZ;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 center() {
|
||||
return Vector2.of(centerX, centerZ);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
return Math.hypot(centerX - x, centerZ - z) <= radiusX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.CIRCLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2 radii() {
|
||||
return Vector2.of(radiusX, radiusX);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Diamond extends AbstractPolygon {
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
public Diamond(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX;
|
||||
this.p1z = centerZ + radiusX;
|
||||
this.p2x = centerX - radiusX;
|
||||
this.p2z = centerZ;
|
||||
this.p3x = centerX;
|
||||
this.p3z = centerZ - radiusX;
|
||||
this.p4x = centerX + radiusX;
|
||||
this.p4z = centerZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 boolean isBounding(final double x, final double z) {
|
||||
if (!insideLine(p1x, p1z, p2x, p2z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
if (!insideLine(p2x, p2z, p3x, p3z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
if (!insideLine(p3x, p3z, p4x, p4z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
return insideLine(p4x, p4z, p1x, p1z, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Pentagon extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z;
|
||||
|
||||
public Pentagon(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));
|
||||
this.p2x = centerX + radiusX * Math.cos(Math.toRadians(126));
|
||||
this.p2z = centerZ + radiusX * Math.sin(Math.toRadians(126));
|
||||
this.p3x = centerX + radiusX * Math.cos(Math.toRadians(198));
|
||||
this.p3z = centerZ + radiusX * Math.sin(Math.toRadians(198));
|
||||
this.p4x = centerX + radiusX * Math.cos(Math.toRadians(270));
|
||||
this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(270));
|
||||
this.p5x = centerX + radiusX * Math.cos(Math.toRadians(342));
|
||||
this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(342));
|
||||
}
|
||||
|
||||
@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)
|
||||
);
|
||||
}
|
||||
|
||||
@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 inside51 = insideLine(p5x, p5z, p1x, p1z, x, z);
|
||||
return inside12 && inside23 && inside34 && inside45 && inside51;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.PENTAGON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Rectangle extends AbstractPolygon {
|
||||
final double b1x, b1z, b2x, b2z;
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
protected Rectangle(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.b1x = centerX - radiusX;
|
||||
this.b1z = centerZ - radiusZ;
|
||||
this.b2x = centerX + radiusX;
|
||||
this.b2z = centerZ + radiusZ;
|
||||
this.p1x = centerX + radiusX;
|
||||
this.p1z = centerZ - radiusZ;
|
||||
this.p2x = centerX - radiusX;
|
||||
this.p2z = centerZ - radiusZ;
|
||||
this.p3x = centerX - radiusX;
|
||||
this.p3z = centerZ + radiusZ;
|
||||
this.p4x = centerX + radiusX;
|
||||
this.p4z = centerZ + radiusZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 boolean isBounding(final double x, final double z) {
|
||||
return x >= b1x && x <= b2x && z >= b1z && z <= b2z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.RECTANGLE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Shape {
|
||||
boolean isBounding(double x, double z);
|
||||
|
||||
default String name() {
|
||||
return "shape";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.util.Translator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public final class ShapeFactory {
|
||||
private static final Map<String, BiFunction<Selection, Boolean, Shape>> custom = new HashMap<>();
|
||||
|
||||
private ShapeFactory() {
|
||||
}
|
||||
|
||||
public static Shape getShape(final Selection selection) {
|
||||
return getShape(selection, true);
|
||||
}
|
||||
|
||||
public static Shape getShape(final Selection selection, final boolean chunkAligned) {
|
||||
return switch (selection.shape()) {
|
||||
case ShapeType.CIRCLE -> new Circle(selection, chunkAligned);
|
||||
case ShapeType.DIAMOND -> new Diamond(selection, chunkAligned);
|
||||
case ShapeType.ELLIPSE, ShapeType.OVAL -> new Ellipse(selection, chunkAligned);
|
||||
case ShapeType.HEXAGON -> new Hexagon(selection, chunkAligned);
|
||||
case ShapeType.PENTAGON -> new Pentagon(selection, chunkAligned);
|
||||
case ShapeType.RECTANGLE -> new Rectangle(selection, chunkAligned);
|
||||
case ShapeType.STAR -> new Star(selection, chunkAligned);
|
||||
case ShapeType.TRIANGLE -> new Triangle(selection, chunkAligned);
|
||||
default -> custom.getOrDefault(selection.shape(), Square::new).apply(selection, chunkAligned);
|
||||
};
|
||||
}
|
||||
|
||||
public static void registerCustom(final String name, final BiFunction<Selection, Boolean, Shape> shapeFunction) {
|
||||
custom.put(name, shapeFunction);
|
||||
Translator.addCustomTranslation("shape_%s".formatted(name), name);
|
||||
}
|
||||
|
||||
public static Set<String> getCustomTypes() {
|
||||
return custom.keySet();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ShapeType {
|
||||
public static final String CIRCLE = "circle";
|
||||
public static final String DIAMOND = "diamond";
|
||||
public static final String ELLIPSE = "ellipse";
|
||||
public static final String HEXAGON = "hexagon";
|
||||
public static final String OVAL = "oval";
|
||||
public static final String PENTAGON = "pentagon";
|
||||
public static final String RECTANGLE = "rectangle";
|
||||
public static final String SQUARE = "square";
|
||||
public static final String STAR = "star";
|
||||
public static final String TRIANGLE = "triangle";
|
||||
|
||||
private static final List<String> DEFAULTS = List.of(CIRCLE, DIAMOND, ELLIPSE, HEXAGON, PENTAGON, RECTANGLE, SQUARE, STAR, TRIANGLE);
|
||||
|
||||
private ShapeType() {
|
||||
}
|
||||
|
||||
public static List<String> all() {
|
||||
final Set<String> customTypes = ShapeFactory.getCustomTypes();
|
||||
if (customTypes.isEmpty()) {
|
||||
return DEFAULTS;
|
||||
}
|
||||
final List<String> allTypes = new ArrayList<>(DEFAULTS);
|
||||
allTypes.addAll(customTypes);
|
||||
return allTypes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class ShapeUtil {
|
||||
private ShapeUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a point C is inside (to the left of) the line defined by point A and B.
|
||||
*
|
||||
* @param ax Point A x
|
||||
* @param az Point A z
|
||||
* @param bx Point B x
|
||||
* @param bz Point B z
|
||||
* @param cx Point C x
|
||||
* @param cz Point C z
|
||||
* @return Whether point C can be considered inside of line AB.
|
||||
*/
|
||||
public static boolean insideLine(final double ax, final double az, final double bx, final double bz, final double cx, final double cz) {
|
||||
// Compute whether the point is inside the line using a cross product
|
||||
return (bx - ax) * (cz - az) > (bz - az) * (cx - ax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two lines defined by two points each respectively, return the intersection point, if any.
|
||||
*
|
||||
* @param l1x1 Line 1 point 1 x
|
||||
* @param l1z1 Line 1 point 1 z
|
||||
* @param l1x2 Line 1 point 2 x
|
||||
* @param l1z2 Line 1 point 2 z
|
||||
* @param l2x1 Line 2 point 1 x
|
||||
* @param l2z1 Line 2 point 1 z
|
||||
* @param l2x2 Line 2 point 2 x
|
||||
* @param l2z2 Line 2 point 2 z
|
||||
* @return An optional containing the intersection point, or empty if no intersection.
|
||||
*/
|
||||
public static Optional<Vector2> intersection(final double l1x1, final double l1z1, final double l1x2, final double l1z2, final double l2x1, final double l2z1, final double l2x2, final double l2z2) {
|
||||
final double a1 = l1z2 - l1z1;
|
||||
final double a2 = l2z2 - l2z1;
|
||||
final double b1 = l1x1 - l1x2;
|
||||
final double b2 = l2x1 - l2x2;
|
||||
final double determinant = a1 * b2 - a2 * b1;
|
||||
if (determinant == 0) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
final double c1 = a1 * l1x1 + b1 * l1z1;
|
||||
final double c2 = a2 * l2x1 + b2 * l2z1;
|
||||
final double x = (b2 * c1 - b1 * c2) / determinant;
|
||||
final double z = (a1 * c2 - a2 * c1) / determinant;
|
||||
return Optional.of(Vector2.of(x, z));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an ellipse defined by the center and radii, find a point on the perimeter corresponding to a specific
|
||||
* angle.
|
||||
*
|
||||
* @param centerX Ellipse center x
|
||||
* @param centerZ Ellipse center z
|
||||
* @param radiusX Ellipse radius x
|
||||
* @param radiusZ Ellipse radius z
|
||||
* @param angle Angle in radians
|
||||
* @return The point on the ellipse.
|
||||
*/
|
||||
public static Vector2 pointOnEllipse(final double centerX, final double centerZ, final double radiusX, final double radiusZ, final double angle) {
|
||||
final double x = centerX + radiusX * Math.cos(angle);
|
||||
final double z = centerZ + radiusZ * Math.sin(angle);
|
||||
return Vector2.of(x, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the closest point on a line from a given position.
|
||||
*
|
||||
* @param posX Position x
|
||||
* @param posZ Position z
|
||||
* @param p1x Line point 1 x
|
||||
* @param p1z Line point 1 z
|
||||
* @param p2x Line point 2 x
|
||||
* @param p2z Line point 2 z
|
||||
* @return The closest point to the position on the line.
|
||||
*/
|
||||
public static Vector2 closestPointOnLine(final double posX, final double posZ, final double p1x, final double p1z, final double p2x, final double p2z) {
|
||||
final double dx = p2x - p1x;
|
||||
final double dz = p2z - p1z;
|
||||
final double perpendicularSlope = -dx / dz;
|
||||
final double p3x, p3z;
|
||||
if (Double.isInfinite(perpendicularSlope)) {
|
||||
p3x = posX;
|
||||
p3z = posZ + 1;
|
||||
} else {
|
||||
p3x = posX + 1;
|
||||
p3z = posZ + perpendicularSlope;
|
||||
}
|
||||
return ShapeUtil.intersection(p1x, p1z, p2x, p2z, posX, posZ, p3x, p3z).orElseThrow(IllegalStateException::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the distance between 2 points.
|
||||
*
|
||||
* @param p1x Point 1 x
|
||||
* @param p1z Point 1 z
|
||||
* @param p2x Point 2 x
|
||||
* @param p2z Point 2 z
|
||||
* @return The distance between the 2 points.
|
||||
*/
|
||||
public static double distanceBetweenPoints(final double p1x, final double p1z, final double p2x, final double p2z) {
|
||||
return Math.sqrt(Math.pow(p1x - p2x, 2) + Math.pow(p1z - p2z, 2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Square extends AbstractPolygon {
|
||||
final double b1x, b1z, b2x, b2z;
|
||||
final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z;
|
||||
|
||||
protected Square(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.b1x = centerX - radiusX;
|
||||
this.b1z = centerZ - radiusX;
|
||||
this.b2x = centerX + radiusX;
|
||||
this.b2z = centerZ + radiusX;
|
||||
this.p1x = centerX + radiusX;
|
||||
this.p1z = centerZ - radiusX;
|
||||
this.p2x = centerX - radiusX;
|
||||
this.p2z = centerZ - radiusX;
|
||||
this.p3x = centerX - radiusX;
|
||||
this.p3z = centerZ + radiusX;
|
||||
this.p4x = centerX + radiusX;
|
||||
this.p4z = centerZ + radiusX;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 boolean isBounding(final double x, final double z) {
|
||||
return x >= b1x && x <= b2x && z >= b1z && z <= b2z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.SQUARE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.intersection;
|
||||
|
||||
public class Star extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z, p4x, p4z, p5x, p5z;
|
||||
private final double i1x, i1z, i2x, i2z, i3x, i3z, i4x, i4z, i5x, i5z;
|
||||
|
||||
public Star(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));
|
||||
this.p2x = centerX + radiusX * Math.cos(Math.toRadians(126));
|
||||
this.p2z = centerZ + radiusX * Math.sin(Math.toRadians(126));
|
||||
this.p3x = centerX + radiusX * Math.cos(Math.toRadians(198));
|
||||
this.p3z = centerZ + radiusX * Math.sin(Math.toRadians(198));
|
||||
this.p4x = centerX + radiusX * Math.cos(Math.toRadians(270));
|
||||
this.p4z = centerZ + radiusX * Math.sin(Math.toRadians(270));
|
||||
this.p5x = centerX + radiusX * Math.cos(Math.toRadians(342));
|
||||
this.p5z = centerZ + radiusX * Math.sin(Math.toRadians(342));
|
||||
final Vector2 i1 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p5x, p5z).orElse(Vector2.of(p1x, p1z));
|
||||
final Vector2 i2 = intersection(p1x, p1z, p3x, p3z, p2x, p2z, p4x, p4z).orElse(Vector2.of(p2x, p2z));
|
||||
final Vector2 i3 = intersection(p2x, p2z, p4x, p4z, p3x, p3z, p5x, p5z).orElse(Vector2.of(p3x, p3z));
|
||||
final Vector2 i4 = intersection(p3x, p3z, p5x, p5z, p1x, p1z, p4x, p4z).orElse(Vector2.of(p4x, p4z));
|
||||
final Vector2 i5 = intersection(p1x, p1z, p4x, p4z, p2x, p2z, p5x, p5z).orElse(Vector2.of(p5x, p5z));
|
||||
this.i1x = i1.getX();
|
||||
this.i1z = i1.getZ();
|
||||
this.i2x = i2.getX();
|
||||
this.i2z = i2.getZ();
|
||||
this.i3x = i3.getX();
|
||||
this.i3z = i3.getZ();
|
||||
this.i4x = i4.getX();
|
||||
this.i4z = i4.getZ();
|
||||
this.i5x = i5.getX();
|
||||
this.i5z = i5.getZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(i1x, i1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(i2x, i2z),
|
||||
Vector2.of(p3x, p3z),
|
||||
Vector2.of(i3x, i3z),
|
||||
Vector2.of(p4x, p4z),
|
||||
Vector2.of(i4x, i4z),
|
||||
Vector2.of(p5x, p5z),
|
||||
Vector2.of(i5x, i5z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
final boolean inside13 = insideLine(p1x, p1z, p3x, p3z, x, z);
|
||||
final boolean inside24 = insideLine(p2x, p2z, p4x, p4z, x, z);
|
||||
final boolean inside35 = insideLine(p3x, p3z, p5x, p5z, x, z);
|
||||
final boolean inside41 = insideLine(p4x, p4z, p1x, p1z, x, z);
|
||||
if (inside13 && inside24 && inside35 && inside41) {
|
||||
return true;
|
||||
}
|
||||
final boolean inside52 = insideLine(p5x, p5z, p2x, p2z, x, z);
|
||||
if (inside24 && inside35 && inside41 && inside52) {
|
||||
return true;
|
||||
}
|
||||
if (inside35 && inside41 && inside52 && inside13) {
|
||||
return true;
|
||||
}
|
||||
if (inside41 && inside52 && inside13 && inside24) {
|
||||
return true;
|
||||
}
|
||||
return inside52 && inside13 && inside24 && inside35;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.STAR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.popcraft.chunky.shape;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.util.Vector2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.popcraft.chunky.shape.ShapeUtil.insideLine;
|
||||
|
||||
public class Triangle extends AbstractPolygon {
|
||||
private final double p1x, p1z, p2x, p2z, p3x, p3z;
|
||||
|
||||
public Triangle(final Selection selection, final boolean chunkAligned) {
|
||||
super(selection, chunkAligned);
|
||||
this.p1x = centerX + radiusX;
|
||||
this.p1z = centerZ + radiusX;
|
||||
this.p2x = centerX - radiusX;
|
||||
this.p2z = centerZ + radiusX;
|
||||
this.p3x = centerX;
|
||||
this.p3z = centerZ - radiusX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector2> points() {
|
||||
return Arrays.asList(
|
||||
Vector2.of(p1x, p1z),
|
||||
Vector2.of(p2x, p2z),
|
||||
Vector2.of(p3x, p3z)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBounding(final double x, final double z) {
|
||||
if (!insideLine(p1x, p1z, p2x, p2z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
if (!insideLine(p2x, p2z, p3x, p3z, x, z)) {
|
||||
return false;
|
||||
}
|
||||
return insideLine(p3x, p3z, p1x, p1z, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ShapeType.TRIANGLE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record ChunkCoordinate(int x, int z) implements Comparable<ChunkCoordinate> {
|
||||
public static Optional<ChunkCoordinate> fromRegionFile(final String regionFileName) {
|
||||
if (!regionFileName.startsWith("r.")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final int extension = regionFileName.indexOf(".mca");
|
||||
if (extension < 2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
final String regionCoordinates = regionFileName.substring(2, extension);
|
||||
final int separator = regionCoordinates.indexOf('.');
|
||||
final Optional<Integer> regionX = Input.tryInteger(regionCoordinates.substring(0, separator));
|
||||
final Optional<Integer> regionZ = Input.tryInteger(regionCoordinates.substring(separator + 1));
|
||||
if (regionX.isPresent() && regionZ.isPresent()) {
|
||||
return Optional.of(new ChunkCoordinate(regionX.get(), regionZ.get()));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ChunkCoordinate o) {
|
||||
return this.x == o.x ? Integer.compare(this.z, o.z) : Integer.compare(this.x, o.x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final ChunkCoordinate that = (ChunkCoordinate) o;
|
||||
return x == that.x && z == that.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d, %d", x, z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
public final class ChunkMath {
|
||||
private ChunkMath() {
|
||||
}
|
||||
|
||||
public static long pack(final int x, final int z) {
|
||||
final long lx = x & 0xFFFFFFFFL;
|
||||
final long lz = z & 0xFFFFFFFFL;
|
||||
return lx << 32 | lz;
|
||||
}
|
||||
|
||||
public static int regionIndex(final int x, final int z) {
|
||||
return (x & 0x1F) * 32 + (z & 0x1F);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.platform.World;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Disk {
|
||||
private static final long ESTIMATED_SPACE_PER_CHUNK = 7031L;
|
||||
private static final double PERCENT_OVERESTIMATE = 1.05d;
|
||||
|
||||
private Disk() {
|
||||
}
|
||||
|
||||
public static long estimatedSpace(final Selection selection) {
|
||||
final long diameterChunksX = selection.diameterChunksX();
|
||||
final long diameterChunksZ = selection.diameterChunksZ();
|
||||
return (long) (PERCENT_OVERESTIMATE * (diameterChunksX * diameterChunksZ * ESTIMATED_SPACE_PER_CHUNK));
|
||||
}
|
||||
|
||||
public static long remainingSpace(final World world) {
|
||||
final Optional<Path> regionDirectory = world.getRegionDirectory();
|
||||
try {
|
||||
if (regionDirectory.isPresent()) {
|
||||
return regionDirectory.get().toFile().getUsableSpace();
|
||||
}
|
||||
final Path currentWorkingDirectory = Paths.get("");
|
||||
if (Files.exists(currentWorkingDirectory)) {
|
||||
return currentWorkingDirectory.toFile().getUsableSpace();
|
||||
}
|
||||
} catch (UnsupportedOperationException | InvalidPathException | SecurityException ignored) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import org.popcraft.chunky.Selection;
|
||||
import org.popcraft.chunky.shape.ShapeType;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public final class Formatting {
|
||||
private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("#.##");
|
||||
private static final char[] BINARY_PREFIXES = new char[]{'K', 'M', 'G', 'T', 'P'};
|
||||
|
||||
private Formatting() {
|
||||
}
|
||||
|
||||
public static String bytes(final long bytes) {
|
||||
final long value = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
|
||||
if (value < 1024) {
|
||||
return String.format("%d B", bytes);
|
||||
}
|
||||
int i = BINARY_PREFIXES.length - 1;
|
||||
long prefixValue = 1L << (BINARY_PREFIXES.length * 10);
|
||||
for (; i > 0; --i) {
|
||||
if (value >= prefixValue) {
|
||||
break;
|
||||
}
|
||||
prefixValue >>= 10;
|
||||
}
|
||||
return String.format("%.1f %cB", bytes / (double) prefixValue, BINARY_PREFIXES[i]);
|
||||
}
|
||||
|
||||
public static String radius(final Selection selection) {
|
||||
if (ShapeType.RECTANGLE.equals(selection.shape()) || ShapeType.ELLIPSE.equals(selection.shape())) {
|
||||
return String.format("%s, %s", number(selection.radiusX()), number(selection.radiusZ()));
|
||||
} else {
|
||||
return String.format("%s", number(selection.radiusX()));
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized String number(final double number) {
|
||||
return NUMBER_FORMAT.format(number);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package org.popcraft.chunky.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
public final class Hilbert {
|
||||
private static final ChunkCoordinate[] regionChunkCoordinateOffsets = {
|
||||
new ChunkCoordinate(0, 0), new ChunkCoordinate(0, 1), new ChunkCoordinate(1, 1),
|
||||
new ChunkCoordinate(1, 0), new ChunkCoordinate(2, 0), new ChunkCoordinate(3, 0),
|
||||
new ChunkCoordinate(3, 1), new ChunkCoordinate(2, 1), new ChunkCoordinate(2, 2),
|
||||
new ChunkCoordinate(3, 2), new ChunkCoordinate(3, 3), new ChunkCoordinate(2, 3),
|
||||
new ChunkCoordinate(1, 3), new ChunkCoordinate(1, 2), new ChunkCoordinate(0, 2),
|
||||
new ChunkCoordinate(0, 3), new ChunkCoordinate(0, 4), new ChunkCoordinate(1, 4),
|
||||
new ChunkCoordinate(1, 5), new ChunkCoordinate(0, 5), new ChunkCoordinate(0, 6),
|
||||
new ChunkCoordinate(0, 7), new ChunkCoordinate(1, 7), new ChunkCoordinate(1, 6),
|
||||
new ChunkCoordinate(2, 6), new ChunkCoordinate(2, 7), new ChunkCoordinate(3, 7),
|
||||
new ChunkCoordinate(3, 6), new ChunkCoordinate(3, 5), new ChunkCoordinate(2, 5),
|
||||
new ChunkCoordinate(2, 4), new ChunkCoordinate(3, 4), new ChunkCoordinate(4, 4),
|
||||
new ChunkCoordinate(5, 4), new ChunkCoordinate(5, 5), new ChunkCoordinate(4, 5),
|
||||
new ChunkCoordinate(4, 6), new ChunkCoordinate(4, 7), new ChunkCoordinate(5, 7),
|
||||
new ChunkCoordinate(5, 6), new ChunkCoordinate(6, 6), new ChunkCoordinate(6, 7),
|
||||
new ChunkCoordinate(7, 7), new ChunkCoordinate(7, 6), new ChunkCoordinate(7, 5),
|
||||
new ChunkCoordinate(6, 5), new ChunkCoordinate(6, 4), new ChunkCoordinate(7, 4),
|
||||
new ChunkCoordinate(7, 3), new ChunkCoordinate(7, 2), new ChunkCoordinate(6, 2),
|
||||
new ChunkCoordinate(6, 3), new ChunkCoordinate(5, 3), new ChunkCoordinate(4, 3),
|
||||
new ChunkCoordinate(4, 2), new ChunkCoordinate(5, 2), new ChunkCoordinate(5, 1),
|
||||
new ChunkCoordinate(4, 1), new ChunkCoordinate(4, 0), new ChunkCoordinate(5, 0),
|
||||
new ChunkCoordinate(6, 0), new ChunkCoordinate(6, 1), new ChunkCoordinate(7, 1),
|
||||
new ChunkCoordinate(7, 0), new ChunkCoordinate(8, 0), new ChunkCoordinate(9, 0),
|
||||
new ChunkCoordinate(9, 1), new ChunkCoordinate(8, 1), new ChunkCoordinate(8, 2),
|
||||
new ChunkCoordinate(8, 3), new ChunkCoordinate(9, 3), new ChunkCoordinate(9, 2),
|
||||
new ChunkCoordinate(10, 2), new ChunkCoordinate(10, 3), new ChunkCoordinate(11, 3),
|
||||
new ChunkCoordinate(11, 2), new ChunkCoordinate(11, 1), new ChunkCoordinate(10, 1),
|
||||
new ChunkCoordinate(10, 0), new ChunkCoordinate(11, 0), new ChunkCoordinate(12, 0),
|
||||
new ChunkCoordinate(12, 1), new ChunkCoordinate(13, 1), new ChunkCoordinate(13, 0),
|
||||
new ChunkCoordinate(14, 0), new ChunkCoordinate(15, 0), new ChunkCoordinate(15, 1),
|
||||
new ChunkCoordinate(14, 1), new ChunkCoordinate(14, 2), new ChunkCoordinate(15, 2),
|
||||
new ChunkCoordinate(15, 3), new ChunkCoordinate(14, 3), new ChunkCoordinate(13, 3),
|
||||
new ChunkCoordinate(13, 2), new ChunkCoordinate(12, 2), new ChunkCoordinate(12, 3),
|
||||
new ChunkCoordinate(12, 4), new ChunkCoordinate(12, 5), new ChunkCoordinate(13, 5),
|
||||
new ChunkCoordinate(13, 4), new ChunkCoordinate(14, 4), new ChunkCoordinate(15, 4),
|
||||
new ChunkCoordinate(15, 5), new ChunkCoordinate(14, 5), new ChunkCoordinate(14, 6),
|
||||
new ChunkCoordinate(15, 6), new ChunkCoordinate(15, 7), new ChunkCoordinate(14, 7),
|
||||
new ChunkCoordinate(13, 7), new ChunkCoordinate(13, 6), new ChunkCoordinate(12, 6),
|
||||
new ChunkCoordinate(12, 7), new ChunkCoordinate(11, 7), new ChunkCoordinate(10, 7),
|
||||
new ChunkCoordinate(10, 6), new ChunkCoordinate(11, 6), new ChunkCoordinate(11, 5),
|
||||
new ChunkCoordinate(11, 4), new ChunkCoordinate(10, 4), new ChunkCoordinate(10, 5),
|
||||
new ChunkCoordinate(9, 5), new ChunkCoordinate(9, 4), new ChunkCoordinate(8, 4),
|
||||
new ChunkCoordinate(8, 5), new ChunkCoordinate(8, 6), new ChunkCoordinate(9, 6),
|
||||
new ChunkCoordinate(9, 7), new ChunkCoordinate(8, 7), new ChunkCoordinate(8, 8),
|
||||
new ChunkCoordinate(9, 8), new ChunkCoordinate(9, 9), new ChunkCoordinate(8, 9),
|
||||
new ChunkCoordinate(8, 10), new ChunkCoordinate(8, 11), new ChunkCoordinate(9, 11),
|
||||
new ChunkCoordinate(9, 10), new ChunkCoordinate(10, 10), new ChunkCoordinate(10, 11),
|
||||
new ChunkCoordinate(11, 11), new ChunkCoordinate(11, 10), new ChunkCoordinate(11, 9),
|
||||
new ChunkCoordinate(10, 9), new ChunkCoordinate(10, 8), new ChunkCoordinate(11, 8),
|
||||
new ChunkCoordinate(12, 8), new ChunkCoordinate(12, 9), new ChunkCoordinate(13, 9),
|
||||
new ChunkCoordinate(13, 8), new ChunkCoordinate(14, 8), new ChunkCoordinate(15, 8),
|
||||
new ChunkCoordinate(15, 9), new ChunkCoordinate(14, 9), new ChunkCoordinate(14, 10),
|
||||
new ChunkCoordinate(15, 10), new ChunkCoordinate(15, 11), new ChunkCoordinate(14, 11),
|
||||
new ChunkCoordinate(13, 11), new ChunkCoordinate(13, 10), new ChunkCoordinate(12, 10),
|
||||
new ChunkCoordinate(12, 11), new ChunkCoordinate(12, 12), new ChunkCoordinate(12, 13),
|
||||
new ChunkCoordinate(13, 13), new ChunkCoordinate(13, 12), new ChunkCoordinate(14, 12),
|
||||
new ChunkCoordinate(15, 12), new ChunkCoordinate(15, 13), new ChunkCoordinate(14, 13),
|
||||
new ChunkCoordinate(14, 14), new ChunkCoordinate(15, 14), new ChunkCoordinate(15, 15),
|
||||
new ChunkCoordinate(14, 15), new ChunkCoordinate(13, 15), new ChunkCoordinate(13, 14),
|
||||
new ChunkCoordinate(12, 14), new ChunkCoordinate(12, 15), new ChunkCoordinate(11, 15),
|
||||
new ChunkCoordinate(10, 15), new ChunkCoordinate(10, 14), new ChunkCoordinate(11, 14),
|
||||
new ChunkCoordinate(11, 13), new ChunkCoordinate(11, 12), new ChunkCoordinate(10, 12),
|
||||
new ChunkCoordinate(10, 13), new ChunkCoordinate(9, 13), new ChunkCoordinate(9, 12),
|
||||
new ChunkCoordinate(8, 12), new ChunkCoordinate(8, 13), new ChunkCoordinate(8, 14),
|
||||
new ChunkCoordinate(9, 14), new ChunkCoordinate(9, 15), new ChunkCoordinate(8, 15),
|
||||
new ChunkCoordinate(7, 15), new ChunkCoordinate(7, 14), new ChunkCoordinate(6, 14),
|
||||
new ChunkCoordinate(6, 15), new ChunkCoordinate(5, 15), new ChunkCoordinate(4, 15),
|
||||
new ChunkCoordinate(4, 14), new ChunkCoordinate(5, 14), new ChunkCoordinate(5, 13),
|
||||
new ChunkCoordinate(4, 13), new ChunkCoordinate(4, 12), new ChunkCoordinate(5, 12),
|
||||
new ChunkCoordinate(6, 12), new ChunkCoordinate(6, 13), new ChunkCoordinate(7, 13),
|
||||
new ChunkCoordinate(7, 12), new ChunkCoordinate(7, 11), new ChunkCoordinate(6, 11),
|
||||
new ChunkCoordinate(6, 10), new ChunkCoordinate(7, 10), new ChunkCoordinate(7, 9),
|
||||
new ChunkCoordinate(7, 8), new ChunkCoordinate(6, 8), new ChunkCoordinate(6, 9),
|
||||
new ChunkCoordinate(5, 9), new ChunkCoordinate(5, 8), new ChunkCoordinate(4, 8),
|
||||
new ChunkCoordinate(4, 9), new ChunkCoordinate(4, 10), new ChunkCoordinate(5, 10),
|
||||
new ChunkCoordinate(5, 11), new ChunkCoordinate(4, 11), new ChunkCoordinate(3, 11),
|
||||
new ChunkCoordinate(2, 11), new ChunkCoordinate(2, 10), new ChunkCoordinate(3, 10),
|
||||
new ChunkCoordinate(3, 9), new ChunkCoordinate(3, 8), new ChunkCoordinate(2, 8),
|
||||
new ChunkCoordinate(2, 9), new ChunkCoordinate(1, 9), new ChunkCoordinate(1, 8),
|
||||
new ChunkCoordinate(0, 8), new ChunkCoordinate(0, 9), new ChunkCoordinate(0, 10),
|
||||
new ChunkCoordinate(1, 10), new ChunkCoordinate(1, 11), new ChunkCoordinate(0, 11),
|
||||
new ChunkCoordinate(0, 12), new ChunkCoordinate(0, 13), new ChunkCoordinate(1, 13),
|
||||
new ChunkCoordinate(1, 12), new ChunkCoordinate(2, 12), new ChunkCoordinate(3, 12),
|
||||
new ChunkCoordinate(3, 13), new ChunkCoordinate(2, 13), new ChunkCoordinate(2, 14),
|
||||
new ChunkCoordinate(3, 14), new ChunkCoordinate(3, 15), new ChunkCoordinate(2, 15),
|
||||
new ChunkCoordinate(1, 15), new ChunkCoordinate(1, 14), new ChunkCoordinate(0, 14),
|
||||
new ChunkCoordinate(0, 15), new ChunkCoordinate(0, 16), new ChunkCoordinate(1, 16),
|
||||
new ChunkCoordinate(1, 17), new ChunkCoordinate(0, 17), new ChunkCoordinate(0, 18),
|
||||
new ChunkCoordinate(0, 19), new ChunkCoordinate(1, 19), new ChunkCoordinate(1, 18),
|
||||
new ChunkCoordinate(2, 18), new ChunkCoordinate(2, 19), new ChunkCoordinate(3, 19),
|
||||
new ChunkCoordinate(3, 18), new ChunkCoordinate(3, 17), new ChunkCoordinate(2, 17),
|
||||
new ChunkCoordinate(2, 16), new ChunkCoordinate(3, 16), new ChunkCoordinate(4, 16),
|
||||
new ChunkCoordinate(4, 17), new ChunkCoordinate(5, 17), new ChunkCoordinate(5, 16),
|
||||
new ChunkCoordinate(6, 16), new ChunkCoordinate(7, 16), new ChunkCoordinate(7, 17),
|
||||
new ChunkCoordinate(6, 17), new ChunkCoordinate(6, 18), new ChunkCoordinate(7, 18),
|
||||
new ChunkCoordinate(7, 19), new ChunkCoordinate(6, 19), new ChunkCoordinate(5, 19),
|
||||
new ChunkCoordinate(5, 18), new ChunkCoordinate(4, 18), new ChunkCoordinate(4, 19),
|
||||
new ChunkCoordinate(4, 20), new ChunkCoordinate(4, 21), new ChunkCoordinate(5, 21),
|
||||
new ChunkCoordinate(5, 20), new ChunkCoordinate(6, 20), new ChunkCoordinate(7, 20),
|
||||
new ChunkCoordinate(7, 21), new ChunkCoordinate(6, 21), new ChunkCoordinate(6, 22),
|
||||
new ChunkCoordinate(7, 22), new ChunkCoordinate(7, 23), new ChunkCoordinate(6, 23),
|
||||
new ChunkCoordinate(5, 23), new ChunkCoordinate(5, 22), new ChunkCoordinate(4, 22),
|
||||
new ChunkCoordinate(4, 23), new ChunkCoordinate(3, 23), new ChunkCoordinate(2, 23),
|
||||
new ChunkCoordinate(2, 22), new ChunkCoordinate(3, 22), new ChunkCoordinate(3, 21),
|
||||
new ChunkCoordinate(3, 20), new ChunkCoordinate(2, 20), new ChunkCoordinate(2, 21),
|
||||
new ChunkCoordinate(1, 21), new ChunkCoordinate(1, 20), new ChunkCoordinate(0, 20),
|
||||
new ChunkCoordinate(0, 21), new ChunkCoordinate(0, 22), new ChunkCoordinate(1, 22),
|
||||
new ChunkCoordinate(1, 23), new ChunkCoordinate(0, 23), new ChunkCoordinate(0, 24),
|
||||
new ChunkCoordinate(0, 25), new ChunkCoordinate(1, 25), new ChunkCoordinate(1, 24),
|
||||
new ChunkCoordinate(2, 24), new ChunkCoordinate(3, 24), new ChunkCoordinate(3, 25),
|
||||
new ChunkCoordinate(2, 25), new ChunkCoordinate(2, 26), new ChunkCoordinate(3, 26),
|
||||
new ChunkCoordinate(3, 27), new ChunkCoordinate(2, 27), new ChunkCoordinate(1, 27),
|
||||
new ChunkCoordinate(1, 26), new ChunkCoordinate(0, 26), new ChunkCoordinate(0, 27),
|
||||
new ChunkCoordinate(0, 28), new ChunkCoordinate(1, 28), new ChunkCoordinate(1, 29),
|
||||
new ChunkCoordinate(0, 29), new ChunkCoordinate(0, 30), new ChunkCoordinate(0, 31),
|
||||
new ChunkCoordinate(1, 31), new ChunkCoordinate(1, 30), new ChunkCoordinate(2, 30),
|
||||
new ChunkCoordinate(2, 31), new ChunkCoordinate(3, 31), new ChunkCoordinate(3, 30),
|
||||
new ChunkCoordinate(3, 29), new ChunkCoordinate(2, 29), new ChunkCoordinate(2, 28),
|
||||
new ChunkCoordinate(3, 28), new ChunkCoordinate(4, 28), new ChunkCoordinate(5, 28),
|
||||
new ChunkCoordinate(5, 29), new ChunkCoordinate(4, 29), new ChunkCoordinate(4, 30),
|
||||
new ChunkCoordinate(4, 31), new ChunkCoordinate(5, 31), new ChunkCoordinate(5, 30),
|
||||
new ChunkCoordinate(6, 30), new ChunkCoordinate(6, 31), new ChunkCoordinate(7, 31),
|
||||
new ChunkCoordinate(7, 30), new ChunkCoordinate(7, 29), new ChunkCoordinate(6, 29),
|
||||
new ChunkCoordinate(6, 28), new ChunkCoordinate(7, 28), new ChunkCoordinate(7, 27),
|
||||
new ChunkCoordinate(7, 26), new ChunkCoordinate(6, 26), new ChunkCoordinate(6, 27),
|
||||
new ChunkCoordinate(5, 27), new ChunkCoordinate(4, 27), new ChunkCoordinate(4, 26),
|
||||
new ChunkCoordinate(5, 26), new ChunkCoordinate(5, 25), new ChunkCoordinate(4, 25),
|
||||
new ChunkCoordinate(4, 24), new ChunkCoordinate(5, 24), new ChunkCoordinate(6, 24),
|
||||
new ChunkCoordinate(6, 25), new ChunkCoordinate(7, 25), new ChunkCoordinate(7, 24),
|
||||
new ChunkCoordinate(8, 24), new ChunkCoordinate(8, 25), new ChunkCoordinate(9, 25),
|
||||
new ChunkCoordinate(9, 24), new ChunkCoordinate(10, 24), new ChunkCoordinate(11, 24),
|
||||
new ChunkCoordinate(11, 25), new ChunkCoordinate(10, 25), new ChunkCoordinate(10, 26),
|
||||
new ChunkCoordinate(11, 26), new ChunkCoordinate(11, 27), new ChunkCoordinate(10, 27),
|
||||
new ChunkCoordinate(9, 27), new ChunkCoordinate(9, 26), new ChunkCoordinate(8, 26),
|
||||
new ChunkCoordinate(8, 27), new ChunkCoordinate(8, 28), new ChunkCoordinate(9, 28),
|
||||
new ChunkCoordinate(9, 29), new ChunkCoordinate(8, 29), new ChunkCoordinate(8, 30),
|
||||
new ChunkCoordinate(8, 31), new ChunkCoordinate(9, 31), new ChunkCoordinate(9, 30),
|
||||
new ChunkCoordinate(10, 30), new ChunkCoordinate(10, 31), new ChunkCoordinate(11, 31),
|
||||
new ChunkCoordinate(11, 30), new ChunkCoordinate(11, 29), new ChunkCoordinate(10, 29),
|
||||
new ChunkCoordinate(10, 28), new ChunkCoordinate(11, 28), new ChunkCoordinate(12, 28),
|
||||
new ChunkCoordinate(13, 28), new ChunkCoordinate(13, 29), new ChunkCoordinate(12, 29),
|
||||
new ChunkCoordinate(12, 30), new ChunkCoordinate(12, 31), new ChunkCoordinate(13, 31),
|
||||
new ChunkCoordinate(13, 30), new ChunkCoordinate(14, 30), new ChunkCoordinate(14, 31),
|
||||
new ChunkCoordinate(15, 31), new ChunkCoordinate(15, 30), new ChunkCoordinate(15, 29),
|
||||
new ChunkCoordinate(14, 29), new ChunkCoordinate(14, 28), new ChunkCoordinate(15, 28),
|
||||
new ChunkCoordinate(15, 27), new ChunkCoordinate(15, 26), new ChunkCoordinate(14, 26),
|
||||
new ChunkCoordinate(14, 27), new ChunkCoordinate(13, 27), new ChunkCoordinate(12, 27),
|
||||
new ChunkCoordinate(12, 26), new ChunkCoordinate(13, 26), new ChunkCoordinate(13, 25),
|
||||
new ChunkCoordinate(12, 25), new ChunkCoordinate(12, 24), new ChunkCoordinate(13, 24),
|
||||
new ChunkCoordinate(14, 24), new ChunkCoordinate(14, 25), new ChunkCoordinate(15, 25),
|
||||
new ChunkCoordinate(15, 24), new ChunkCoordinate(15, 23), new ChunkCoordinate(14, 23),
|
||||
new ChunkCoordinate(14, 22), new ChunkCoordinate(15, 22), new ChunkCoordinate(15, 21),
|
||||
new ChunkCoordinate(15, 20), new ChunkCoordinate(14, 20), new ChunkCoordinate(14, 21),
|
||||
new ChunkCoordinate(13, 21), new ChunkCoordinate(13, 20), new ChunkCoordinate(12, 20),
|
||||
new ChunkCoordinate(12, 21), new ChunkCoordinate(12, 22), new ChunkCoordinate(13, 22),
|
||||
new ChunkCoordinate(13, 23), new ChunkCoordinate(12, 23), new ChunkCoordinate(11, 23),
|
||||
new ChunkCoordinate(11, 22), new ChunkCoordinate(10, 22), new ChunkCoordinate(10, 23),
|
||||
new ChunkCoordinate(9, 23), new ChunkCoordinate(8, 23), new ChunkCoordinate(8, 22),
|
||||
new ChunkCoordinate(9, 22), new ChunkCoordinate(9, 21), new ChunkCoordinate(8, 21),
|
||||
new ChunkCoordinate(8, 20), new ChunkCoordinate(9, 20), new ChunkCoordinate(10, 20),
|
||||
new ChunkCoordinate(10, 21), new ChunkCoordinate(11, 21), new ChunkCoordinate(11, 20),
|
||||
new ChunkCoordinate(11, 19), new ChunkCoordinate(11, 18), new ChunkCoordinate(10, 18),
|
||||
new ChunkCoordinate(10, 19), new ChunkCoordinate(9, 19), new ChunkCoordinate(8, 19),
|
||||
new ChunkCoordinate(8, 18), new ChunkCoordinate(9, 18), new ChunkCoordinate(9, 17),
|
||||
new ChunkCoordinate(8, 17), new ChunkCoordinate(8, 16), new ChunkCoordinate(9, 16),
|
||||
new ChunkCoordinate(10, 16), new ChunkCoordinate(10, 17), new ChunkCoordinate(11, 17),
|
||||
new ChunkCoordinate(11, 16), new ChunkCoordinate(12, 16), new ChunkCoordinate(13, 16),
|
||||
new ChunkCoordinate(13, 17), new ChunkCoordinate(12, 17), new ChunkCoordinate(12, 18),
|
||||
new ChunkCoordinate(12, 19), new ChunkCoordinate(13, 19), new ChunkCoordinate(13, 18),
|
||||
new ChunkCoordinate(14, 18), new ChunkCoordinate(14, 19), new ChunkCoordinate(15, 19),
|
||||
new ChunkCoordinate(15, 18), new ChunkCoordinate(15, 17), new ChunkCoordinate(14, 17),
|
||||
new ChunkCoordinate(14, 16), new ChunkCoordinate(15, 16), new ChunkCoordinate(16, 16),
|
||||
new ChunkCoordinate(17, 16), new ChunkCoordinate(17, 17), new ChunkCoordinate(16, 17),
|
||||
new ChunkCoordinate(16, 18), new ChunkCoordinate(16, 19), new ChunkCoordinate(17, 19),
|
||||
new ChunkCoordinate(17, 18), new ChunkCoordinate(18, 18), new ChunkCoordinate(18, 19),
|
||||
new ChunkCoordinate(19, 19), new ChunkCoordinate(19, 18), new ChunkCoordinate(19, 17),
|
||||
new ChunkCoordinate(18, 17), new ChunkCoordinate(18, 16), new ChunkCoordinate(19, 16),
|
||||
new ChunkCoordinate(20, 16), new ChunkCoordinate(20, 17), new ChunkCoordinate(21, 17),
|
||||
new ChunkCoordinate(21, 16), new ChunkCoordinate(22, 16), new ChunkCoordinate(23, 16),
|
||||
new ChunkCoordinate(23, 17), new ChunkCoordinate(22, 17), new ChunkCoordinate(22, 18),
|
||||
new ChunkCoordinate(23, 18), new ChunkCoordinate(23, 19), new ChunkCoordinate(22, 19),
|
||||
new ChunkCoordinate(21, 19), new ChunkCoordinate(21, 18), new ChunkCoordinate(20, 18),
|
||||
new ChunkCoordinate(20, 19), new ChunkCoordinate(20, 20), new ChunkCoordinate(20, 21),
|
||||
new ChunkCoordinate(21, 21), new ChunkCoordinate(21, 20), new ChunkCoordinate(22, 20),
|
||||
new ChunkCoordinate(23, 20), new ChunkCoordinate(23, 21), new ChunkCoordinate(22, 21),
|
||||
new ChunkCoordinate(22, 22), new ChunkCoordinate(23, 22), new ChunkCoordinate(23, 23),
|
||||
new ChunkCoordinate(22, 23), new ChunkCoordinate(21, 23), new ChunkCoordinate(21, 22),
|
||||
new ChunkCoordinate(20, 22), new ChunkCoordinate(20, 23), new ChunkCoordinate(19, 23),
|
||||
new ChunkCoordinate(18, 23), new ChunkCoordinate(18, 22), new ChunkCoordinate(19, 22),
|
||||
new ChunkCoordinate(19, 21), new ChunkCoordinate(19, 20), new ChunkCoordinate(18, 20),
|
||||
new ChunkCoordinate(18, 21), new ChunkCoordinate(17, 21), new ChunkCoordinate(17, 20),
|
||||
new ChunkCoordinate(16, 20), new ChunkCoordinate(16, 21), new ChunkCoordinate(16, 22),
|
||||
new ChunkCoordinate(17, 22), new ChunkCoordinate(17, 23), new ChunkCoordinate(16, 23),
|
||||
new ChunkCoordinate(16, 24), new ChunkCoordinate(16, 25), new ChunkCoordinate(17, 25),
|
||||
new ChunkCoordinate(17, 24), new ChunkCoordinate(18, 24), new ChunkCoordinate(19, 24),
|
||||
new ChunkCoordinate(19, 25), new ChunkCoordinate(18, 25), new ChunkCoordinate(18, 26),
|
||||
new ChunkCoordinate(19, 26), new ChunkCoordinate(19, 27), new ChunkCoordinate(18, 27),
|
||||
new ChunkCoordinate(17, 27), new ChunkCoordinate(17, 26), new ChunkCoordinate(16, 26),
|
||||
new ChunkCoordinate(16, 27), new ChunkCoordinate(16, 28), new ChunkCoordinate(17, 28),
|
||||
new ChunkCoordinate(17, 29), new ChunkCoordinate(16, 29), new ChunkCoordinate(16, 30),
|
||||
new ChunkCoordinate(16, 31), new ChunkCoordinate(17, 31), new ChunkCoordinate(17, 30),
|
||||
new ChunkCoordinate(18, 30), new ChunkCoordinate(18, 31), new ChunkCoordinate(19, 31),
|
||||
new ChunkCoordinate(19, 30), new ChunkCoordinate(19, 29), new ChunkCoordinate(18, 29),
|
||||
new ChunkCoordinate(18, 28), new ChunkCoordinate(19, 28), new ChunkCoordinate(20, 28),
|
||||
new ChunkCoordinate(21, 28), new ChunkCoordinate(21, 29), new ChunkCoordinate(20, 29),
|
||||
new ChunkCoordinate(20, 30), new ChunkCoordinate(20, 31), new ChunkCoordinate(21, 31),
|
||||
new ChunkCoordinate(21, 30), new ChunkCoordinate(22, 30), new ChunkCoordinate(22, 31),
|
||||
new ChunkCoordinate(23, 31), new ChunkCoordinate(23, 30), new ChunkCoordinate(23, 29),
|
||||
new ChunkCoordinate(22, 29), new ChunkCoordinate(22, 28), new ChunkCoordinate(23, 28),
|
||||
new ChunkCoordinate(23, 27), new ChunkCoordinate(23, 26), new ChunkCoordinate(22, 26),
|
||||
new ChunkCoordinate(22, 27), new ChunkCoordinate(21, 27), new ChunkCoordinate(20, 27),
|
||||
new ChunkCoordinate(20, 26), new ChunkCoordinate(21, 26), new ChunkCoordinate(21, 25),
|
||||
new ChunkCoordinate(20, 25), new ChunkCoordinate(20, 24), new ChunkCoordinate(21, 24),
|
||||
new ChunkCoordinate(22, 24), new ChunkCoordinate(22, 25), new ChunkCoordinate(23, 25),
|
||||
new ChunkCoordinate(23, 24), new ChunkCoordinate(24, 24), new ChunkCoordinate(24, 25),
|
||||
new ChunkCoordinate(25, 25), new ChunkCoordinate(25, 24), new ChunkCoordinate(26, 24),
|
||||
new ChunkCoordinate(27, 24), new ChunkCoordinate(27, 25), new ChunkCoordinate(26, 25),
|
||||
new ChunkCoordinate(26, 26), new ChunkCoordinate(27, 26), new ChunkCoordinate(27, 27),
|
||||
new ChunkCoordinate(26, 27), new ChunkCoordinate(25, 27), new ChunkCoordinate(25, 26),
|
||||
new ChunkCoordinate(24, 26), new ChunkCoordinate(24, 27), new ChunkCoordinate(24, 28),
|
||||
new ChunkCoordinate(25, 28), new ChunkCoordinate(25, 29), new ChunkCoordinate(24, 29),
|
||||
new ChunkCoordinate(24, 30), new ChunkCoordinate(24, 31), new ChunkCoordinate(25, 31),
|
||||
new ChunkCoordinate(25, 30), new ChunkCoordinate(26, 30), new ChunkCoordinate(26, 31),
|
||||
new ChunkCoordinate(27, 31), new ChunkCoordinate(27, 30), new ChunkCoordinate(27, 29),
|
||||
new ChunkCoordinate(26, 29), new ChunkCoordinate(26, 28), new ChunkCoordinate(27, 28),
|
||||
new ChunkCoordinate(28, 28), new ChunkCoordinate(29, 28), new ChunkCoordinate(29, 29),
|
||||
new ChunkCoordinate(28, 29), new ChunkCoordinate(28, 30), new ChunkCoordinate(28, 31),
|
||||
new ChunkCoordinate(29, 31), new ChunkCoordinate(29, 30), new ChunkCoordinate(30, 30),
|
||||
new ChunkCoordinate(30, 31), new ChunkCoordinate(31, 31), new ChunkCoordinate(31, 30),
|
||||
new ChunkCoordinate(31, 29), new ChunkCoordinate(30, 29), new ChunkCoordinate(30, 28),
|
||||
new ChunkCoordinate(31, 28), new ChunkCoordinate(31, 27), new ChunkCoordinate(31, 26),
|
||||
new ChunkCoordinate(30, 26), new ChunkCoordinate(30, 27), new ChunkCoordinate(29, 27),
|
||||
new ChunkCoordinate(28, 27), new ChunkCoordinate(28, 26), new ChunkCoordinate(29, 26),
|
||||
new ChunkCoordinate(29, 25), new ChunkCoordinate(28, 25), new ChunkCoordinate(28, 24),
|
||||
new ChunkCoordinate(29, 24), new ChunkCoordinate(30, 24), new ChunkCoordinate(30, 25),
|
||||
new ChunkCoordinate(31, 25), new ChunkCoordinate(31, 24), new ChunkCoordinate(31, 23),
|
||||
new ChunkCoordinate(30, 23), new ChunkCoordinate(30, 22), new ChunkCoordinate(31, 22),
|
||||
new ChunkCoordinate(31, 21), new ChunkCoordinate(31, 20), new ChunkCoordinate(30, 20),
|
||||
new ChunkCoordinate(30, 21), new ChunkCoordinate(29, 21), new ChunkCoordinate(29, 20),
|
||||
new ChunkCoordinate(28, 20), new ChunkCoordinate(28, 21), new ChunkCoordinate(28, 22),
|
||||
new ChunkCoordinate(29, 22), new ChunkCoordinate(29, 23), new ChunkCoordinate(28, 23),
|
||||
new ChunkCoordinate(27, 23), new ChunkCoordinate(27, 22), new ChunkCoordinate(26, 22),
|
||||
new ChunkCoordinate(26, 23), new ChunkCoordinate(25, 23), new ChunkCoordinate(24, 23),
|
||||
new ChunkCoordinate(24, 22), new ChunkCoordinate(25, 22), new ChunkCoordinate(25, 21),
|
||||
new ChunkCoordinate(24, 21), new ChunkCoordinate(24, 20), new ChunkCoordinate(25, 20),
|
||||
new ChunkCoordinate(26, 20), new ChunkCoordinate(26, 21), new ChunkCoordinate(27, 21),
|
||||
new ChunkCoordinate(27, 20), new ChunkCoordinate(27, 19), new ChunkCoordinate(27, 18),
|
||||
new ChunkCoordinate(26, 18), new ChunkCoordinate(26, 19), new ChunkCoordinate(25, 19),
|
||||
new ChunkCoordinate(24, 19), new ChunkCoordinate(24, 18), new ChunkCoordinate(25, 18),
|
||||
new ChunkCoordinate(25, 17), new ChunkCoordinate(24, 17), new ChunkCoordinate(24, 16),
|
||||
new ChunkCoordinate(25, 16), new ChunkCoordinate(26, 16), new ChunkCoordinate(26, 17),
|
||||
new ChunkCoordinate(27, 17), new ChunkCoordinate(27, 16), new ChunkCoordinate(28, 16),
|
||||
new ChunkCoordinate(29, 16), new ChunkCoordinate(29, 17), new ChunkCoordinate(28, 17),
|
||||
new ChunkCoordinate(28, 18), new ChunkCoordinate(28, 19), new ChunkCoordinate(29, 19),
|
||||
new ChunkCoordinate(29, 18), new ChunkCoordinate(30, 18), new ChunkCoordinate(30, 19),
|
||||
new ChunkCoordinate(31, 19), new ChunkCoordinate(31, 18), new ChunkCoordinate(31, 17),
|
||||
new ChunkCoordinate(30, 17), new ChunkCoordinate(30, 16), new ChunkCoordinate(31, 16),
|
||||
new ChunkCoordinate(31, 15), new ChunkCoordinate(31, 14), new ChunkCoordinate(30, 14),
|
||||
new ChunkCoordinate(30, 15), new ChunkCoordinate(29, 15), new ChunkCoordinate(28, 15),
|
||||
new ChunkCoordinate(28, 14), new ChunkCoordinate(29, 14), new ChunkCoordinate(29, 13),
|
||||
new ChunkCoordinate(28, 13), new ChunkCoordinate(28, 12), new ChunkCoordinate(29, 12),
|
||||
new ChunkCoordinate(30, 12), new ChunkCoordinate(30, 13), new ChunkCoordinate(31, 13),
|
||||
new ChunkCoordinate(31, 12), new ChunkCoordinate(31, 11), new ChunkCoordinate(30, 11),
|
||||
new ChunkCoordinate(30, 10), new ChunkCoordinate(31, 10), new ChunkCoordinate(31, 9),
|
||||
new ChunkCoordinate(31, 8), new ChunkCoordinate(30, 8), new ChunkCoordinate(30, 9),
|
||||
new ChunkCoordinate(29, 9), new ChunkCoordinate(29, 8), new ChunkCoordinate(28, 8),
|
||||
new ChunkCoordinate(28, 9), new ChunkCoordinate(28, 10), new ChunkCoordinate(29, 10),
|
||||
new ChunkCoordinate(29, 11), new ChunkCoordinate(28, 11), new ChunkCoordinate(27, 11),
|
||||
new ChunkCoordinate(26, 11), new ChunkCoordinate(26, 10), new ChunkCoordinate(27, 10),
|
||||
new ChunkCoordinate(27, 9), new ChunkCoordinate(27, 8), new ChunkCoordinate(26, 8),
|
||||
new ChunkCoordinate(26, 9), new ChunkCoordinate(25, 9), new ChunkCoordinate(25, 8),
|
||||
new ChunkCoordinate(24, 8), new ChunkCoordinate(24, 9), new ChunkCoordinate(24, 10),
|
||||
new ChunkCoordinate(25, 10), new ChunkCoordinate(25, 11), new ChunkCoordinate(24, 11),
|
||||
new ChunkCoordinate(24, 12), new ChunkCoordinate(24, 13), new ChunkCoordinate(25, 13),
|
||||
new ChunkCoordinate(25, 12), new ChunkCoordinate(26, 12), new ChunkCoordinate(27, 12),
|
||||
new ChunkCoordinate(27, 13), new ChunkCoordinate(26, 13), new ChunkCoordinate(26, 14),
|
||||
new ChunkCoordinate(27, 14), new ChunkCoordinate(27, 15), new ChunkCoordinate(26, 15),
|
||||
new ChunkCoordinate(25, 15), new ChunkCoordinate(25, 14), new ChunkCoordinate(24, 14),
|
||||
new ChunkCoordinate(24, 15), new ChunkCoordinate(23, 15), new ChunkCoordinate(22, 15),
|
||||
new ChunkCoordinate(22, 14), new ChunkCoordinate(23, 14), new ChunkCoordinate(23, 13),
|
||||
new ChunkCoordinate(23, 12), new ChunkCoordinate(22, 12), new ChunkCoordinate(22, 13),
|
||||
new ChunkCoordinate(21, 13), new ChunkCoordinate(21, 12), new ChunkCoordinate(20, 12),
|
||||
new ChunkCoordinate(20, 13), new ChunkCoordinate(20, 14), new ChunkCoordinate(21, 14),
|
||||
new ChunkCoordinate(21, 15), new ChunkCoordinate(20, 15), new ChunkCoordinate(19, 15),
|
||||
new ChunkCoordinate(19, 14), new ChunkCoordinate(18, 14), new ChunkCoordinate(18, 15),
|
||||
new ChunkCoordinate(17, 15), new ChunkCoordinate(16, 15), new ChunkCoordinate(16, 14),
|
||||
new ChunkCoordinate(17, 14), new ChunkCoordinate(17, 13), new ChunkCoordinate(16, 13),
|
||||
new ChunkCoordinate(16, 12), new ChunkCoordinate(17, 12), new ChunkCoordinate(18, 12),
|
||||
new ChunkCoordinate(18, 13), new ChunkCoordinate(19, 13), new ChunkCoordinate(19, 12),
|
||||
new ChunkCoordinate(19, 11), new ChunkCoordinate(19, 10), new ChunkCoordinate(18, 10),
|
||||
new ChunkCoordinate(18, 11), new ChunkCoordinate(17, 11), new ChunkCoordinate(16, 11),
|
||||
new ChunkCoordinate(16, 10), new ChunkCoordinate(17, 10), new ChunkCoordinate(17, 9),
|
||||
new ChunkCoordinate(16, 9), new ChunkCoordinate(16, 8), new ChunkCoordinate(17, 8),
|
||||
new ChunkCoordinate(18, 8), new ChunkCoordinate(18, 9), new ChunkCoordinate(19, 9),
|
||||
new ChunkCoordinate(19, 8), new ChunkCoordinate(20, 8), new ChunkCoordinate(21, 8),
|
||||
new ChunkCoordinate(21, 9), new ChunkCoordinate(20, 9), new ChunkCoordinate(20, 10),
|
||||
new ChunkCoordinate(20, 11), new ChunkCoordinate(21, 11), new ChunkCoordinate(21, 10),
|
||||
new ChunkCoordinate(22, 10), new ChunkCoordinate(22, 11), new ChunkCoordinate(23, 11),
|
||||
new ChunkCoordinate(23, 10), new ChunkCoordinate(23, 9), new ChunkCoordinate(22, 9),
|
||||
new ChunkCoordinate(22, 8), new ChunkCoordinate(23, 8), new ChunkCoordinate(23, 7),
|
||||
new ChunkCoordinate(22, 7), new ChunkCoordinate(22, 6), new ChunkCoordinate(23, 6),
|
||||
new ChunkCoordinate(23, 5), new ChunkCoordinate(23, 4), new ChunkCoordinate(22, 4),
|
||||
new ChunkCoordinate(22, 5), new ChunkCoordinate(21, 5), new ChunkCoordinate(21, 4),
|
||||
new ChunkCoordinate(20, 4), new ChunkCoordinate(20, 5), new ChunkCoordinate(20, 6),
|
||||
new ChunkCoordinate(21, 6), new ChunkCoordinate(21, 7), new ChunkCoordinate(20, 7),
|
||||
new ChunkCoordinate(19, 7), new ChunkCoordinate(19, 6), new ChunkCoordinate(18, 6),
|
||||
new ChunkCoordinate(18, 7), new ChunkCoordinate(17, 7), new ChunkCoordinate(16, 7),
|
||||
new ChunkCoordinate(16, 6), new ChunkCoordinate(17, 6), new ChunkCoordinate(17, 5),
|
||||
new ChunkCoordinate(16, 5), new ChunkCoordinate(16, 4), new ChunkCoordinate(17, 4),
|
||||
new ChunkCoordinate(18, 4), new ChunkCoordinate(18, 5), new ChunkCoordinate(19, 5),
|
||||
new ChunkCoordinate(19, 4), new ChunkCoordinate(19, 3), new ChunkCoordinate(19, 2),
|
||||
new ChunkCoordinate(18, 2), new ChunkCoordinate(18, 3), new ChunkCoordinate(17, 3),
|
||||
new ChunkCoordinate(16, 3), new ChunkCoordinate(16, 2), new ChunkCoordinate(17, 2),
|
||||
new ChunkCoordinate(17, 1), new ChunkCoordinate(16, 1), new ChunkCoordinate(16, 0),
|
||||
new ChunkCoordinate(17, 0), new ChunkCoordinate(18, 0), new ChunkCoordinate(18, 1),
|
||||
new ChunkCoordinate(19, 1), new ChunkCoordinate(19, 0), new ChunkCoordinate(20, 0),
|
||||
new ChunkCoordinate(21, 0), new ChunkCoordinate(21, 1), new ChunkCoordinate(20, 1),
|
||||
new ChunkCoordinate(20, 2), new ChunkCoordinate(20, 3), new ChunkCoordinate(21, 3),
|
||||
new ChunkCoordinate(21, 2), new ChunkCoordinate(22, 2), new ChunkCoordinate(22, 3),
|
||||
new ChunkCoordinate(23, 3), new ChunkCoordinate(23, 2), new ChunkCoordinate(23, 1),
|
||||
new ChunkCoordinate(22, 1), new ChunkCoordinate(22, 0), new ChunkCoordinate(23, 0),
|
||||
new ChunkCoordinate(24, 0), new ChunkCoordinate(24, 1), new ChunkCoordinate(25, 1),
|
||||
new ChunkCoordinate(25, 0), new ChunkCoordinate(26, 0), new ChunkCoordinate(27, 0),
|
||||
new ChunkCoordinate(27, 1), new ChunkCoordinate(26, 1), new ChunkCoordinate(26, 2),
|
||||
new ChunkCoordinate(27, 2), new ChunkCoordinate(27, 3), new ChunkCoordinate(26, 3),
|
||||
new ChunkCoordinate(25, 3), new ChunkCoordinate(25, 2), new ChunkCoordinate(24, 2),
|
||||
new ChunkCoordinate(24, 3), new ChunkCoordinate(24, 4), new ChunkCoordinate(25, 4),
|
||||
new ChunkCoordinate(25, 5), new ChunkCoordinate(24, 5), new ChunkCoordinate(24, 6),
|
||||
new ChunkCoordinate(24, 7), new ChunkCoordinate(25, 7), new ChunkCoordinate(25, 6),
|
||||
new ChunkCoordinate(26, 6), new ChunkCoordinate(26, 7), new ChunkCoordinate(27, 7),
|
||||
new ChunkCoordinate(27, 6), new ChunkCoordinate(27, 5), new ChunkCoordinate(26, 5),
|
||||
new ChunkCoordinate(26, 4), new ChunkCoordinate(27, 4), new ChunkCoordinate(28, 4),
|
||||
new ChunkCoordinate(29, 4), new ChunkCoordinate(29, 5), new ChunkCoordinate(28, 5),
|
||||
new ChunkCoordinate(28, 6), new ChunkCoordinate(28, 7), new ChunkCoordinate(29, 7),
|
||||
new ChunkCoordinate(29, 6), new ChunkCoordinate(30, 6), new ChunkCoordinate(30, 7),
|
||||
new ChunkCoordinate(31, 7), new ChunkCoordinate(31, 6), new ChunkCoordinate(31, 5),
|
||||
new ChunkCoordinate(30, 5), new ChunkCoordinate(30, 4), new ChunkCoordinate(31, 4),
|
||||
new ChunkCoordinate(31, 3), new ChunkCoordinate(31, 2), new ChunkCoordinate(30, 2),
|
||||
new ChunkCoordinate(30, 3), new ChunkCoordinate(29, 3), new ChunkCoordinate(28, 3),
|
||||
new ChunkCoordinate(28, 2), new ChunkCoordinate(29, 2), new ChunkCoordinate(29, 1),
|
||||
new ChunkCoordinate(28, 1), new ChunkCoordinate(28, 0), new ChunkCoordinate(29, 0),
|
||||
new ChunkCoordinate(30, 0), new ChunkCoordinate(30, 1), new ChunkCoordinate(31, 1),
|
||||
new ChunkCoordinate(31, 0)
|
||||
};
|
||||
|
||||
private Hilbert() {
|
||||
}
|
||||
|
||||
public static ChunkCoordinate regionDistanceToChunkCoordinateOffset(final int distance) {
|
||||
return regionChunkCoordinateOffsets[distance];
|
||||
}
|
||||
|
||||
public static ChunkCoordinate[] chunkCoordinateOffsets() {
|
||||
return regionChunkCoordinateOffsets;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user