463 Commits
Author SHA1 Message Date
NotAlexNoyle 9f8bf11660 Exclude CODEOWNERS from language statistics 2026-10-29 16:18:54 -04:00
NotAlexNoyle f8dcf33fcb Add CODEOWNERS (one staff approval) 2026-10-29 16:18:54 -04:00
NotAlexNoyle 116976d8e8 Merge branch 'master' of github.com:true-og/Chunky 2026-06-17 06:47:56 -04:00
NotAlexNoyle 800a4fa743 Update config 2026-06-17 06:47:36 -04:00
NotAlexNoyle 3124d52e3b Merge pull request #2 from true-og/codex/fix-runtime-error-in-gradle-jar-build
Fix Bukkit plugin main class relocation in shaded JAR
2026-03-13 13:42:30 -04:00
NotAlexNoyle b973781649 Fix Bukkit shadowJar relocation for plugin main class 2026-03-13 13:42:19 -04:00
NotAlexNoyle 107bb79474 Merge pull request #1 from true-og/codex/fix-jar-build-for-minecraft-plugin
Produce shaded Bukkit plugin jar for Purpur 1.19.4 and bundle/relocate dependencies
2026-03-13 09:42:12 -04:00
NotAlexNoyle f35cb20714 Fix Bukkit plugin packaging and Purpur target 2026-03-13 09:40:54 -04:00
NotAlexNoyle aab04df0b6 Update gradle to 8.14.3 2025-09-17 10:24:14 -04:00
NotAlexNoyle eda3874ba7 Remove unneeded subprojects 2025-06-11 15:31:36 -04:00
NotAlexNoyle 609fea09f6 Fix build 2024-12-08 14:07:26 -05:00
pop4959 d3956d688b New Crowdin updates (#330) 2024-04-13 23:49:49 -07:00
citylion e46c1c3abe Add Hexagon shape (#329) 2024-04-13 23:22:44 -07:00
pop4959 ca668d2434 New Crowdin updates (#328) 2024-04-13 21:37:03 -07:00
pop4959 af621921e9 New Crowdin updates (#326) 2024-03-19 23:34:46 -07:00
Christopher Bohn 9f165e647d Don't wait for server thread when dispatching tasks (Forge) 2024-02-24 11:58:35 -08:00
ishland 4cff7a4711 Don't wait for server thread when dispatching tasks (#322) 2024-02-24 11:00:53 -08:00
Christopher Bohn a60f083e08 Send trim updates to console 2024-02-20 13:07:17 -08:00
Christopher Bohn 74f6ab4866 Improve error handling of trim side 2024-02-20 13:01:05 -08:00
pop4959 ea68abc3cc New Crowdin updates (#318) 2024-02-16 18:14:16 -08:00
Christopher Bohn 73813786c0 Default to op when no permissions are set 2024-02-16 18:11:33 -08:00
pop4959 7b5594c196 New Crowdin updates (#312) 2024-02-12 23:55:32 -08:00
Christopher Bohn 87a13cf729 Add border command to Forge 2024-02-12 23:34:11 -08:00
Christopher Bohn 324f042f77 Update Github Actions 2024-02-07 19:45:26 -08:00
Christopher Bohn 83a9e8b8c3 Bump archloom 2024-02-05 22:02:15 -08:00
Christopher Bohn 2a58fdaf80 Implement chunk filters 2024-02-04 02:31:28 -08:00
Christopher Bohn 62c437dcc8 Support tag search 2024-02-03 22:33:55 -08:00
Christopher Bohn a2e8736c14 Handle interrupts while processing world 2024-02-03 12:10:10 -08:00
Christopher Bohn b93468e9f6 Add world iterator region progress 2024-02-03 11:45:34 -08:00
pop4959 9947f37a26 Implement new world pattern (#315) 2024-02-01 04:17:14 -08:00
Christopher Bohn ce93af7c81 Buffer decompressed chunk data 2024-01-31 02:59:04 -08:00
Christopher Bohn f132397ae6 Default to key when no translation 2024-01-31 02:56:52 -08:00
Christopher Bohn c15c600b45 Fix saving of CSV tasks 2024-01-25 23:10:46 -08:00
Christopher Bohn f59d7c4759 Revert "Adjust max working count default scaling"
This reverts commit 9e447d4a2a.
2024-01-25 23:07:56 -08:00
Christopher Bohn 76b678aec4 Add warning for world border beyond max world size 2024-01-21 18:43:16 -08:00
Christopher Bohn e51f093ce8 Adjust sample interval default values 2024-01-21 02:29:36 -08:00
Christopher Bohn 10f1478474 Make trim cancellable and add progress messages 2024-01-17 23:11:57 -08:00
Christopher Bohn 29c92a8dc8 Scan for generated chunk status 2024-01-17 21:24:54 -08:00
Christopher Bohn 9e447d4a2a Adjust max working count default scaling 2024-01-17 02:35:18 -08:00
Christopher Bohn 1726479648 Don't load region where location table is missing 2024-01-17 01:36:43 -08:00
Christopher Bohn 117c03fb46 Use server boss bars instead of command boss bars 2024-01-17 01:19:50 -08:00
Christopher Bohn 4d562aa62c Optional world argument for worldborder command 2024-01-17 00:11:12 -08:00
Christopher Bohn e398cc2d8e Add system property to await ticket removal 2024-01-16 23:19:51 -08:00
Christopher Bohn e6137925f3 Mention NeoForge in readme 2024-01-15 23:30:35 -08:00
Christopher Bohn f08fb59aa3 Add NeoForge artifact to Github Actions 2024-01-15 23:12:52 -08:00
Christopher Bohn f9429cd717 Apparently the loader version got reset 2024-01-15 23:11:09 -08:00
Christopher Bohn fc7e1b0bae NeoForge implementation 2024-01-15 22:50:51 -08:00
Christopher Bohn c833f27a92 Update platform specific gradle plugins 2024-01-15 20:36:11 -08:00
Christopher Bohn 5975a9f5f3 Find world by namespaced id path as a fallback 2024-01-15 16:37:05 -08:00
Christopher Bohn 0f65dbc19b Don't require permissions in Singleplayer mode 2024-01-14 02:26:42 -08:00
Christopher Bohn 646043cdf0 Replace magic values for tag type 2024-01-14 01:56:34 -08:00
Christopher Bohn caef3640d1 Make Bukkit world getDirectory more strict 2024-01-13 18:20:27 -08:00
Christopher Bohn fe130e8b8e Update Github Actions 2023-12-29 02:37:56 -08:00
Christopher Bohn 586a14e409 Revert Folia to latest published version 2023-12-29 01:25:17 -08:00
Christopher Bohn 213c5ce763 Update build scripts 2023-12-29 01:23:06 -08:00
Christopher Bohn cadf998c32 Add system property for sample interval 2023-11-22 18:13:53 -08:00
Christopher Bohn 886be957cd Support command permission on Fabric 2023-11-22 00:19:03 -08:00
Metriximor 580f0072e3 Improve vehicle teleporting logic (#301) 2023-11-21 23:28:53 -08:00
Emily 5079beb68e Implement isChunkGenerated on Spigot (#299) 2023-11-18 15:27:44 -08:00
Christopher Bohn f609ab4a34 Fix getting elevation on Folia 2023-11-05 20:03:42 -08:00
Christopher Bohn ee02cec5ac Remount vehicle when teleporting 2023-11-05 19:15:42 -08:00
pop4959 e2d8f7b89b New Crowdin updates (#292) 2023-11-05 16:35:26 -08:00
pop4959 f12a5a9258 New Crowdin updates (#291) 2023-09-16 15:40:25 -07:00
pop4959 709f2a5376 New Crowdin updates (#290) 2023-09-03 16:49:09 -07:00
pop4959 329937a7f7 New Crowdin updates (#277) 2023-08-13 18:04:53 -07:00
pop4959 61ca4e7bbf New Crowdin updates (#275) 2023-06-16 13:10:09 -07:00
pop4959 6edaeaa0b0 Handle POIs and entities properly when trimming with inhabited time 2023-06-16 13:06:31 -07:00
Christopher Bohn 176928cff2 Update Forge to 1.20 2023-06-09 22:35:46 -07:00
pop4959 1cdd07b2d5 New Crowdin updates (#273) 2023-06-09 18:37:49 -07:00
Christopher Bohn ebd263954a Update Fabric to 1.20 2023-06-07 18:33:04 -07:00
pop4959 b487702986 New Crowdin updates (#270) 2023-06-07 18:16:38 -07:00
Christopher Bohn 3597ec3465 Update build scripts 2023-05-29 20:51:37 -07:00
Christopher Bohn 33b6080ccd Update bStats 2023-05-29 17:27:53 -07:00
Christopher Bohn bfb30e58cc update test strings 2023-05-29 16:09:56 -07:00
Christopher Bohn 4a329e8029 add notice about inhabited time trim 2023-05-29 16:01:02 -07:00
Christopher Bohn f435293368 Register new trim arguments in Fabric/Forge 2023-05-29 14:01:46 -07:00
Christopher Bohn 3666d58e88 add another folia method 2023-05-28 16:22:54 -07:00
Christopher Bohn 38e99a8703 add some folia methods 2023-05-28 15:55:51 -07:00
Christopher Bohn 2ff6a5dddf Add missing tests 2023-05-21 17:34:44 -07:00
pop4959 b1d088bef6 New Crowdin updates (#268) 2023-05-21 17:31:16 -07:00
pop4959 c5f15db2b8 New Crowdin updates (#266) 2023-05-09 23:23:31 -07:00
Christopher Bohn 98baadfe32 Move NBT to its own module 2023-05-02 22:22:53 -07:00
pop4959 d4c0af18aa New Crowdin updates (#262) 2023-04-29 16:06:46 -07:00
Christopher Bohn 6df5aa1e1e Bump supported version ranges 2023-04-15 01:09:22 -07:00
Christopher Bohn 410ddd106f Fix usage of BlockPos on 1.19.4 2023-04-15 00:59:25 -07:00
pop4959 5d83912c3b New Crowdin updates (#259) 2023-04-14 21:29:33 -07:00
pop4959 953196facb New Crowdin updates (#257) 2023-04-06 22:23:38 -07:00
pop4959 9474b9144c New Crowdin updates (#248) 2023-03-29 19:54:08 -07:00
Christopher Bohn b347572f84 Update default iterator to regionized 2023-03-29 19:32:39 -07:00
pop4959 2a3f089861 Folia support (#256) 2023-03-29 19:18:20 -07:00
Christopher Bohn f8b7d0fba5 Fix reading chunks from non-regions 2023-03-18 01:38:01 -07:00
pop4959 96bb1ea7e2 Implement new region pattern (#253) 2023-03-14 01:26:16 -07:00
Christopher Bohn 9ee34d3983 Ensure tickets placed on main after gen 2023-03-11 21:31:42 -08:00
Christopher Bohn dbc51fbedf Improve generation pipeline 2023-03-08 23:38:52 -08:00
pop4959 0f914e461f New Crowdin updates (#247) 2023-03-04 01:15:15 -08:00
Christopher Bohn 66179d4a45 Extra trim options 2023-03-04 00:50:11 -08:00
Christopher Bohn 75640077b4 Update Paper repo URL 2023-03-02 00:45:29 -08:00
Christopher Bohn b7cbd6775b Register extended border arguments on Fabric 2023-03-01 20:52:34 -08:00
Christopher Bohn f912195a26 Add system property for ticking load duration 2023-02-01 01:15:27 -08:00
pop4959 02923bee53 New Crowdin updates (#236) 2023-02-01 01:11:58 -08:00
pop4959 bda5898a35 New Crowdin updates (#235) 2022-12-31 14:06:26 -08:00
pop4959 5eecf8b671 Use release version of PaperLib 2022-12-31 14:02:28 -08:00
pop4959 e2b76451ab Use RegisterCommandsEvent on Forge 2022-12-28 14:11:20 -08:00
pop4959 51c2dae5f9 New Crowdin updates (#233) 2022-12-28 13:23:39 -08:00
pop4959 c17ad230ca Bump required Minecraft versions 2022-12-15 17:25:53 -08:00
pop4959 b4d2748f94 New Crowdin updates (#225) 2022-12-15 01:34:23 -08:00
pop4959 6922cd49f5 Fabric/Forge updates for 1.19.3 2022-12-09 00:40:18 -08:00
pop4959 469dd57931 New Crowdin updates (#223) 2022-10-27 19:07:21 -07:00
pop4959 447c3daca0 Add Chinese Traditional to crowdin.yml 2022-10-26 22:17:45 -07:00
pop4959 cdd1d92fcb Fix error loading permissions before server start 2022-10-02 18:17:08 -07:00
Dylan Xaldin 2776307e51 Remove unused imports 2022-10-01 01:34:01 -07:00
pop4959 c25ea97301 New Crowdin updates (#217) 2022-09-25 14:08:16 -07:00
pop4959 eeaf3cccac New Crowdin updates (#216) 2022-09-22 08:16:28 -07:00
pop4959 0e0d5395b2 New Crowdin updates (#215) 2022-09-20 02:05:54 -07:00
pop4959 4e70b40e26 New translations en.json (French) (#214) 2022-09-18 18:24:29 -07:00
pop4959 0cc7d22190 Internally support custom shapes 2022-09-18 17:42:23 -07:00
pop4959 8473fee636 Create workflow for javadoc 2022-09-11 22:41:49 -07:00
pop4959 47a228f14a Add API documentation 2022-09-11 21:55:35 -07:00
pop4959 304b29b762 New Crowdin updates (#213) 2022-09-09 20:13:24 -07:00
pop4959 a6ac1dc4f7 New Crowdin updates (#212) 2022-09-06 02:09:49 -07:00
pop4959 b86fb33cbb Preview API 2022-09-05 23:45:17 -07:00
pop4959 c727a8b9f6 New Crowdin updates (#211) 2022-09-05 21:56:03 -07:00
pop4959 9a6ddb3b00 Update wrap metadata 2022-09-05 21:02:18 -07:00
pop4959 57fbe2c3eb Register dimension arguments for border command 2022-09-05 17:27:51 -07:00
pop4959 1ac89047f5 Permission support on Fabric 2022-09-03 21:01:51 -07:00
pop4959 18c3a53db5 Safer air check 2022-09-03 19:52:35 -07:00
pop4959 21a9bd35cc New Crowdin updates (#210) 2022-09-03 03:26:57 -07:00
pop4959 423daa0e84 Improve elevation calculation 2022-09-03 03:10:58 -07:00
pop4959 4dac670207 Register instance with the service manager 2022-08-28 02:50:05 -07:00
pop4959 7c28e1b17c Include border command in help when present 2022-08-27 22:47:33 -07:00
pop4959 142af0c2c8 Update Gradle to 7.5.1 2022-08-27 21:51:03 -07:00
pop4959 c356a9e008 Fix circle returning incorrect radii 2022-08-27 19:46:30 -07:00
pop4959 9ba9e407b0 Update default permissions 2022-08-20 22:33:51 -07:00
pop4959 7464d503e6 Add missing test translations 2022-08-20 22:26:26 -07:00
pop4959 e076408af9 Add Korean to crowdin.yml 2022-08-20 11:32:53 -07:00
pop4959 00478faa4e Fix errors in Hebrew translation 2022-08-14 02:50:52 -07:00
pop4959 0983ca3a8e Add test for Hebrew 2022-08-14 02:41:39 -07:00
pop4959 8f49d24569 New Crowdin updates (#204) 2022-08-14 02:36:59 -07:00
pop4959 0d17535530 Add Hebrew to crowdin.yml 2022-08-12 10:49:59 -07:00
pop4959 a605fa9e1a Refactor methods into ShapeUtil 2022-08-09 21:33:53 -07:00
pop4959 6a010c5c57 Option to force load chunks 2022-08-06 22:31:56 -07:00
pop4959 062a556a70 Fire event when running reload 2022-08-06 21:21:02 -07:00
pop4959 c0c03a16a1 Add command to display selection
Closes #177
2022-08-06 21:03:41 -07:00
pop4959 d813a08b62 Add more specific reload 2022-08-06 19:45:17 -07:00
pop4959 8783e099b1 Bump config version 2022-08-02 01:38:34 -07:00
pop4959 f7669ca67f Create task directories 2022-08-02 01:18:45 -07:00
pop4959 3dce0ff86f Add missing config directories 2022-08-02 01:06:54 -07:00
pop4959 cd4ee39655 Simple NBT 2022-07-29 23:40:28 -07:00
pop4959 309d3e185f Move task loading out of config 2022-07-24 17:49:14 -07:00
pop4959 20c749ed61 Update Gradle to 7.5 2022-07-19 19:30:30 -07:00
pop4959 d85e81c936 Update WorldBorder dependency 2022-07-18 20:17:51 -07:00
pop4959 064a0bbb25 Use player sender on Forge 2022-07-18 20:01:21 -07:00
pop4959 8b643a9f2d Fix no-arg execution 2022-07-18 19:37:03 -07:00
pop4959 a18d68b919 Select player's world by default 2022-07-17 20:14:43 -07:00
pop4959 d77595ce07 Rename suggestion method 2022-07-17 19:51:43 -07:00
pop4959 95825514b8 Wrap command arguments 2022-07-17 19:38:50 -07:00
pop4959 2811dd732b Misc refactor 2022-07-09 19:07:50 -07:00
pop4959 9a61676c4f Use Java 17 everywhere 2022-07-09 16:54:09 -07:00
pop4959 91427b80a7 New Crowdin updates (#200) 2022-07-08 19:28:14 -07:00
pop4959 dda4c845d4 Add test for Hindi 2022-07-07 02:18:49 -07:00
pop4959 ff39433836 Add Hindi translation (#199) 2022-07-07 02:16:01 -07:00
pop4959 ffb35e9059 Add Hindi to crowdin.yml 2022-07-06 22:46:41 -07:00
pop4959 2a4db272ea Minor style fixes 2022-07-03 00:47:34 -07:00
pop4959 a3ad11188c Add test for Finnish 2022-07-02 19:19:55 -07:00
pop4959 a780ed5c8a New Crowdin updates (#196) 2022-07-02 19:14:25 -07:00
pop4959 1feaea45e4 Add Finnish to crowdin.yml 2022-06-26 00:59:32 -07:00
pop4959 0e6a974173 Remove boss bars when paused or cancelled 2022-06-25 17:42:16 -07:00
pop4959 6b0d09bf46 Add tests for Bosnian and Croatian 2022-06-25 16:57:10 -07:00
pop4959 41a7749afc Add Bosnian and Croatian translation (#194) 2022-06-25 16:45:07 -07:00
pop4959 e5e377176c Add Bosnian and Croatian to crowdin.yml 2022-06-22 16:48:58 -07:00
pop4959 1544a30715 New Crowdin updates (#193) 2022-06-22 16:35:40 -07:00
pop4959 79888db1f6 Fix failing translation in Serbian (Latin) (#192) 2022-06-21 22:58:38 -07:00
pop4959 e34a49aee1 Add test for Serbian (Latin) 2022-06-21 22:48:49 -07:00
pop4959 4708dbd47b Add Serbian (Latin) translation (#191) 2022-06-21 22:44:23 -07:00
pop4959 a2951bed7b Add Serbian (Latin) to crowdin.yml 2022-06-21 22:00:06 -07:00
dfsek c84435efc1 Fix Fabric invoker mixin names (#190) 2022-06-18 02:57:13 -07:00
pop4959 ba7d2cb57f Add test for Brazilian Portuguese 2022-06-18 02:53:49 -07:00
pop4959 75e227b7dc Add Brazilian Portuguese translation (#188) 2022-06-18 02:45:25 -07:00
pop4959 b4c361f4f5 Add Brazilian Portuguese to crowdin.yml 2022-06-11 00:32:06 -07:00
pop4959 71e8c11fca New Crowdin updates (#187) 2022-06-11 00:24:52 -07:00
pop4959 b09a9d6e53 Update Forge to 1.19 2022-06-09 02:46:18 -07:00
pop4959 0db4a98c6d Update Fabric to 1.19 2022-06-07 19:26:28 -07:00
pop4959 73c8628d39 Update README.md 2022-06-07 19:26:28 -07:00
pop4959 fe1dac6977 Migrate options to config 2022-06-07 19:26:28 -07:00
pop4959 a986ef17b0 Forgot to remove old Forge boss bar 2022-06-07 19:26:28 -07:00
pop4959 91c9d9d335 Reimplement boss bar progress 2022-06-07 19:26:28 -07:00
pop4959 bcdfe9918e Replace deprecated sound registry method 2022-06-07 19:26:27 -07:00
pop4959 807f24afbc Fix issue with broken header copy 2022-06-07 19:26:27 -07:00
pop4959 41c4df3ca5 Replace deprecated defaultWorld method 2022-06-07 19:26:27 -07:00
pop4959 5af88bb328 Bump sponge API / gradle plugin versions 2022-06-07 19:26:27 -07:00
pop4959 67d4667057 Update Fabric to 1.19-pre 2022-06-07 19:26:27 -07:00
pop4959 7ccb15c834 Ensure both pattern args are terminal 2022-06-07 19:26:27 -07:00
pop4959 9c1ee4601c Add brig args for pattern 2022-06-07 19:26:27 -07:00
pop4959 39b04da630 consolidate directory methods 2022-05-29 00:29:46 -07:00
pop4959 1508169353 Improve csv iterator 2022-05-28 19:00:37 -07:00
pop4959 f73aba5d0d New Crowdin updates (#180) 2022-05-28 03:41:14 -07:00
pop4959 69655a4530 Add CSV pattern 2022-05-28 03:34:37 -07:00
pop4959 9a63393ec5 Update shadow to 7.1.2 2022-05-25 11:58:34 -07:00
pop4959 1fc2e036f5 Bump loom version 2022-05-24 22:31:44 -07:00
pop4959 867e3c2b2c Set mixin compatibility to Java 17 2022-05-24 22:09:06 -07:00
pop4959 7b92f22201 Bump fabric loader version 2022-05-24 21:59:39 -07:00
pop4959 517be4692e No tasks to continue when all cancelled 2022-05-21 00:10:34 -07:00
pop4959 8b38275da5 Load cancelled tasks 2022-05-21 00:02:47 -07:00
pop4959 5abcdc3bbf Refactor for ChunkyBorder Fabric support (#179) 2022-05-19 21:30:45 -07:00
pop4959 150c7f2ee5 Update loom version 2022-04-16 15:33:19 -07:00
pop4959 199d4faa2b New Crowdin updates (#167) 2022-04-10 18:22:22 -07:00
pop4959 4c71c18b12 Update Gradle to 7.4.2 2022-04-10 17:53:48 -07:00
pop4959 c696d61fbe Remove unnecessary dependencies in common 2022-04-10 17:48:38 -07:00
pop4959 7b1254c6b6 Dependency cleanup 2022-04-10 17:17:45 -07:00
pop4959 3c680f4bbe Update SpongeAPI and SpongeGradle 2022-04-10 16:00:15 -07:00
pop4959 b758dc0d16 Add test for Italian 2022-01-03 13:33:18 -08:00
pop4959 e1a0c5c4f3 Add Italian translation (#163) 2022-01-03 13:29:43 -08:00
pop4959 27d8d445ad Fix error for server stopping before initialization 2022-01-03 13:08:12 -08:00
pop4959 93b2ffe082 Improve Brigadier command registration 2021-12-31 21:30:17 -08:00
pop4959 ab592e7dc3 Add Italian to crowdin.yml 2021-12-31 17:48:24 -08:00
pop4959 4b722138dd Add test for Ukrainian 2021-12-31 13:23:43 -08:00
pop4959 59a209f20e Add Ukrainian translation (#162) 2021-12-31 11:06:02 -08:00
pop4959 31b9889e67 Add Ukrainian to crowdin.yml 2021-12-28 09:59:34 -08:00
pop4959 99141b6cfd New Crowdin updates (#160) 2021-12-26 23:55:04 -08:00
pop4959 8a81e62c85 Handle CompletionException from isChunkGenerated 2021-12-24 21:19:09 -08:00
pop4959 5613f94e01 Clear region cache on world init 2021-12-24 16:52:59 -08:00
pop4959 3ee15e5cad Fix regression with squaremap polygon markers 2021-12-20 22:19:19 -08:00
pop4959 6c5f8b59d8 New Crowdin updates (#155) 2021-12-20 22:06:04 -08:00
Dylan Xaldin 10a508495b Add Squaremap Integration (#156) 2021-12-20 21:03:17 -08:00
pop4959 fdc69bab13 Test Czech 2021-12-12 12:43:28 -08:00
pop4959 4391b21153 New Crowdin updates (#146) 2021-12-12 12:38:31 -08:00
pop4959 e73bb16451 Fix RGB support check 2021-12-05 22:11:12 -08:00
pop4959 5b17f2d979 Release permits with exceptional completion 2021-12-05 18:47:03 -08:00
pop4959 650127c068 Add Czech to crowdin.yml 2021-12-02 00:23:21 -08:00
Dylan Xaldin 32072f3161 [Forge] Update to 1.18 (#145) 2021-12-01 23:09:55 -08:00
pop4959 f534c46fb4 Update Fabric to 1.18 2021-11-30 12:10:36 -08:00
pop4959 8e00001d21 New Crowdin updates (#144) 2021-11-30 12:05:29 -08:00
pop4959 a225c1251b Translate pattern in command 2021-11-30 02:42:24 -08:00
pop4959 53181d90e7 Add system property for max working count 2021-11-28 04:16:04 -08:00
pop4959 4b4f6249bf Use Java 17 in Github Actions 2021-11-28 03:46:49 -08:00
pop4959 cb4f905ed9 Update Fabric to 1.18-rc3 2021-11-28 03:41:40 -08:00
pop4959 0191428555 Update Gradle to 7.3 2021-11-28 03:12:04 -08:00
pop4959 83731b0c33 New Crowdin updates (#143) 2021-11-28 02:43:57 -08:00
pop4959 c7f0e12418 Update PaperLib version for release candidate 2021-11-27 11:01:03 -08:00
pop4959 eb9d3413f4 New Crowdin updates (#140) 2021-11-26 13:32:51 -08:00
pop4959 61c3383246 Add missing commands to chunky border help 2021-11-21 21:58:42 -08:00
pop4959 7cdf34bcab Add missing commands to help menu 2021-11-21 21:53:12 -08:00
pop4959 4cd8ac5f12 Add translation strings for patterns 2021-11-21 21:40:41 -08:00
pop4959 3574cdd9bc Update translation tests 2021-10-29 01:46:19 -07:00
pop4959 0ed5de2fb1 New Crowdin updates (#137) 2021-10-29 01:32:26 -07:00
pop4959 e19b46f282 Refactor for ChunkyBorder 2021-10-29 00:25:52 -07:00
pop4959 d8aa40af23 Make Player distinct from Sender 2021-10-29 00:25:36 -07:00
pop4959 0ed37bebea Update crowdin.yml 2021-10-29 00:25:20 -07:00
pop4959 9fbe8ef271 Add method to get server players 2021-10-14 01:36:21 -07:00
pop4959 66b38acce0 Add setter methods to border 2021-10-14 01:15:45 -07:00
pop4959 eb83576310 Move version util to common 2021-10-13 00:18:38 -07:00
pop4959 71da25cc68 Add method to server for player 2021-10-13 00:12:53 -07:00
pop4959 8b60a8e13f Add method to sender for UUID 2021-10-12 23:38:28 -07:00
pop4959 2615e89817 Change border help message 2021-10-12 23:21:42 -07:00
pop4959 76f378de2a New Crowdin updates (#136) 2021-10-02 17:59:29 -07:00
pop4959 bc5bc30b54 Add border addon translations 2021-10-02 17:23:42 -07:00
pop4959 1fcd160666 Update to gradle 7.2 2021-10-02 16:52:07 -07:00
pop4959 e1b41d7005 Code cleanup 2021-10-02 16:44:26 -07:00
pop4959 a9cf9b5fa7 Update to new Sponge metadata format 2021-10-02 16:35:59 -07:00
Dylan Xaldin c55e573e17 [Forge] Update to 1.17.1 (#127) 2021-08-15 20:57:17 -07:00
pop4959 704b4f778a Code cleanup 2021-08-14 23:30:50 -07:00
pop4959 595b2a7ef3 New Crowdin updates (#125) 2021-08-14 19:56:34 -07:00
pop4959 1953302756 Task scheduling changes 2021-08-14 19:47:46 -07:00
Dylan Xaldin 3a27f1675e Code cleanup (#124) 2021-08-07 22:47:52 -07:00
pop4959 3059d23e82 Don't sample chunks that aren't loaded 2021-08-05 00:06:51 -07:00
Dylan Xaldin 34b653c4b4 [Sponge] Fix various configuration issues (#122) 2021-08-04 23:38:00 -07:00
Dylan Xaldin 6f357ff623 [Sponge] Implement "continue-on-restart" feature (#123) 2021-08-04 23:02:39 -07:00
pop4959 52de6a734a Improve language handling 2021-08-04 22:19:22 -07:00
Dylan Xaldin a6df5afee6 [Bukkit] Shade and update to bStats 2.2.1 (#121) 2021-08-04 01:44:40 -07:00
pop4959 7c82c8787a Fix problems with elliptical selections 2021-08-04 01:34:51 -07:00
Dylan Xaldin 8fdd26d576 [Bukkit] Use SimpleScheduler (#118) 2021-08-02 01:16:49 -07:00
Dylan Xaldin 5a85972030 [Common] Fix SimpleScheduler behavior (#117) 2021-08-02 01:14:59 -07:00
Dylan Xaldin 55330fd243 [Fabric/Forge] Use alternative chunk scheduling method (#119) 2021-08-02 01:13:16 -07:00
pop4959 a04a548b67 New Crowdin updates (#116) 2021-08-02 01:08:25 -07:00
pop4959 b6418a7cd0 Add simple region cache and optimize sampling 2021-08-01 23:53:32 -07:00
pop4959 c24fb2cdc0 Update crowdin.yml 2021-07-31 11:13:34 -07:00
Dylan Xaldin 6cca8531b3 [Fabric] Simplify world and sender logic (#115) 2021-07-30 22:07:14 -07:00
pop4959 10a866a77f New Crowdin updates (#102) 2021-07-29 23:09:03 -07:00
Dylan Xaldin fe149c73bb [Sponge] Fix trim functionality (#113) 2021-07-29 22:56:55 -07:00
Dylan Xaldin 710789eaef [Forge] Improve parity with Fabric (#112) 2021-07-29 22:54:46 -07:00
Dylan Xaldin 002ebe3d84 [Fabric] Update Loom to 0.9 (#110) 2021-07-29 02:16:57 -07:00
Dylan Xaldin 0dd8e13bf0 [Sponge] Fix scheduler blocking server (#111) 2021-07-29 02:16:37 -07:00
Dylan Xaldin e2a29198a7 [Forge] Migrate from MCP to Mojang Mappings (#109) 2021-07-29 00:17:14 -07:00
Dylan Xaldin 132023f7e0 Improve build tooling + bring back Forge (#108) 2021-07-27 23:00:05 -07:00
pop4959 e80df2186a Add progress command and bar 2021-07-17 23:00:55 -07:00
pop4959 28f0fd7395 Remove unnecessary platform class 2021-07-10 23:50:10 -07:00
pop4959 536375cdd9 Refactor task progress 2021-07-10 23:36:15 -07:00
pop4959 b8bba0daa0 Improve sponge command parsing 2021-07-10 22:07:42 -07:00
pop4959 b0a116a480 Fix weight being ignored 2021-07-09 22:00:35 -07:00
pop4959 06e400741d Reduce awt usage 2021-07-09 21:38:02 -07:00
pop4959 b6113a648a [Fabric] Update to 1.17.1-pre2 2021-07-06 00:10:19 -07:00
pop4959 250dfe6894 [Fabric] Remove ticket when chunk holder is null
This case never ever happens in vanilla as far as I'm aware, but
it should be handled regardless just in case.
2021-07-04 14:54:28 -07:00
pop4959 becb26e2a8 Upload artifacts 2021-06-26 21:35:48 -07:00
pop4959 7bd8124e95 Leave implementation out of the artifact name 2021-06-26 21:25:54 -07:00
pop4959 90a87428df Don't trim region if location table is missing 2021-06-19 20:26:25 -07:00
pop4959 4853b93991 Better version handling 2021-06-19 19:51:34 -07:00
pop4959 83ba481df0 Update crowdin.yml 2021-06-10 21:43:49 -07:00
pop4959 c8fe6a8d24 New Crowdin updates (#100) 2021-06-08 12:29:58 -07:00
pop4959 4e1ad4f341 Bump required Fabric version to 1.17 2021-06-08 12:09:42 -07:00
pop4959 61186d0b89 New Crowdin updates (#99) 2021-06-07 23:01:42 -07:00
pop4959 95e82dbcd0 Support POI and entity (1.17) removal in trim 2021-06-07 22:50:41 -07:00
pop4959 5e4cfbd942 Fix NPE reading empty json config 2021-06-03 21:34:08 -07:00
pop4959 fd6eab114d Add limit feature 2021-06-03 20:51:53 -07:00
pop4959 21872e1ae1 Config#getDirectory 2021-06-03 19:53:44 -07:00
pop4959 f9010efe0f Implement isChunkGenerated on Fabric 2021-06-02 21:59:04 -07:00
pop4959 167e9c47c6 New Crowdin updates (#95) 2021-05-31 21:12:06 -07:00
Jason 92c75e5ef4 Minor build script cleanup + add GitHub Actions (#97) 2021-05-31 20:21:25 -07:00
pop4959 e2cc0fcccf Update to Fabric 1.17-pre1
Updates Gradle from 6.8.3 to 6.9 for Java 16 support which is
required by loom for building Minecraft 1.17 mods. Also uses
toolchains to target Java 8 for all builds except for Fabric.
Forge builds are temporarily disabled until FG 5 is released
which will be compatible with Java 16.
2021-05-31 16:47:53 -07:00
pop4959 a37ac45396 Sponge API 8 WIP 2021-05-30 18:54:51 -07:00
pop4959 5fcdfdfb61 Prefixed radius to expand/contract selection 2021-05-29 22:56:20 -07:00
pop4959 8508c6da82 Use getDiameter for world border in Forge 2021-05-23 16:11:56 -07:00
pop4959 779c8e2675 Support changing language in reload command 2021-05-20 21:24:39 -07:00
pop4959 73caab174d Default /chunky center to player position 2021-05-14 23:45:56 -07:00
pop4959 0e594b0efe Improve decimal formatting
Don't show decimal unless used
2021-05-14 22:19:22 -07:00
pop4959 751f98d852 Consider the corner still "inside" the shape 2021-05-13 23:30:44 -07:00
pop4959 95eb42f55a Print 2nd radii based on shape instead of equality 2021-05-13 22:56:21 -07:00
pop4959 267167f145 Translate shape in shape command 2021-05-13 01:16:10 -07:00
pop4959 43ac6473c0 Improve command handling in Fabric/Forge 2021-05-13 01:09:45 -07:00
pop4959 4ec9b22599 New Crowdin updates (#91) 2021-05-10 22:18:13 -07:00
pop4959 a244ac1ee8 Allow decimal center and radius 2021-05-08 23:08:41 -07:00
pop4959 6dd318add6 New Crowdin updates (#90) 2021-05-08 19:18:40 -07:00
pop4959 42b914a9db Translate shape in messages 2021-05-08 00:04:07 -07:00
pop4959 4abb47cdd5 Replace delete tl keys with trim 2021-05-07 23:52:23 -07:00
pop4959 7a86586b42 Miscellaneous translation changes 2021-05-07 22:27:02 -07:00
pop4959 5daf5db2ef New Crowdin updates (#83) 2021-05-02 19:53:44 -07:00
pop4959 04facd1044 Add Forge implementation 2021-05-02 19:48:34 -07:00
pop4959 d5fc3795ed Update gradle 2021-04-29 01:12:11 -07:00
pop4959 9b8fcfb502 New Crowdin updates (#82) 2021-04-29 00:53:45 -07:00
pop4959 bf3c024625 New Crowdin updates (#79) 2021-04-29 00:30:36 -07:00
pop4959 bc9137ee1a Line width support in BlueMap integration 2021-03-24 19:42:12 -07:00
pop4959 001de17224 Match extended syntax from start in trim 2021-03-24 19:14:40 -07:00
pop4959 168bf37911 Rename delete to trim 2021-03-24 18:57:59 -07:00
pop4959 34fda3ff7b Translation keys for shapes 2021-03-24 18:10:49 -07:00
pop4959 4881a178d4 Clean up imports 2021-03-24 18:05:55 -07:00
pop4959 e5c8a97b95 Radius formatting 2021-03-24 18:01:18 -07:00
pop4959 d793193052 Improve command confirmations
Confirmations are now per sender which means that if there are
several users of the plugin, they cannot confirm each other's
commands. Confirmations also now expire after one minute.
2021-03-23 22:28:05 -07:00
pop4959 8435db73af Update to BlueMap API 1.3 2021-03-22 21:22:19 -07:00
pop4959 95502cbc69 Delay continue-on-restart task until first tick
Should fix an issue where this option doesn't work when Chunky
loads before world manager plugins like Multiverse and
Hyperverse.
2021-03-21 22:29:00 -07:00
pop4959 e7e8adb28e World implementation not needed for tests 2021-03-19 21:31:59 -07:00
pop4959 f18b49fb46 Explicitly set archivesBaseName to chunky
Fixes #72. Both LuckPerms and Chunky were using "fabric" for the
module name, and Loom was using this as the default refmap name
since no archive base name was declared.
2021-03-19 20:57:09 -07:00
pop4959 34ca13ac9e Temporarily disable disk space estimation 2021-03-19 20:42:58 -07:00
pop4959 ce6c11e6cb Make subperms implicitly grant base command perm 2021-03-18 22:54:07 -07:00
pop4959 2eef87ace1 Improve shape behavior and add tests 2021-03-16 22:14:19 -07:00
pop4959 7ba6178803 Improve selection 2021-03-16 20:02:06 -07:00
pop4959 4ba23043ba Check language to make sure it's valid 2021-03-11 22:56:10 -08:00
pop4959 9cdea6b108 Refactor some config stuff 2021-03-11 22:37:27 -08:00
pop4959 0caba5e6f9 Update to Fabric Loom 0.6 2021-03-11 21:18:50 -08:00
pop4959 1608119c12 New Crowdin updates (#73) 2021-03-11 21:06:14 -08:00
pop4959 e31d1923aa Update crowdin.yml 2021-02-24 00:01:33 -08:00
pop4959 2e1c833d2f Warn on start if low on disk space 2021-02-11 20:44:45 -08:00
pop4959 160bf3f6fc Implement delete command 2021-02-10 00:40:33 -08:00
pop4959 d6b36ced5f Revamp permissions 2021-02-08 23:26:04 -08:00
pop4959 f1e0fbb423 Rename oval to ellipse 2021-02-08 21:56:17 -08:00
pop4959 2ce1c49f57 New Crowdin updates (#71) 2021-02-07 03:11:52 -08:00
pop4959 99672423c5 New Crowdin updates (#70) 2021-02-06 11:59:20 -08:00
pop4959 14cdfd3def Update translation key for cancel 2021-02-06 02:03:57 -08:00
pop4959 4db17075b8 Add world argument to pause, continue, and cancel 2021-02-06 01:56:24 -08:00
pop4959 88dfe88093 Start shouldn't run with invalid params 2021-02-06 00:42:01 -08:00
pop4959 d645eeb119 Add support for Mod Menu in Fabric 2021-02-05 23:42:32 -08:00
pop4959 50e3cf0b0a Extended syntax for start command 2021-02-04 00:18:47 -08:00
pop4959 23b6c4a6f8 Only suggest completions once 2021-02-03 23:35:01 -08:00
pop4959 17a43ecfaa Check world equality by underlying object 2021-02-03 23:25:54 -08:00
pop4959 dce0283f66 Clean up world limit checks 2021-02-02 21:41:12 -08:00
pop4959 00e8102b25 Support suffixed numeric inputs 2021-02-02 21:25:29 -08:00
pop4959 02ae8cabb0 Change plugin ID on Fabric 2021-02-02 20:14:31 -08:00
pop4959 2c25fa97c0 Replace vanilla border markers in Pl3xMap 2021-02-02 20:03:59 -08:00
pop4959 eaaa8a4956 Update Spanish translation (#68) 2021-01-31 21:37:35 -08:00
pop4959 dd6e4b4298 Sponge metrics 2021-01-25 23:32:31 -08:00
pop4959 fb9f8eb1a2 bStats should be loaded last 2021-01-25 22:32:33 -08:00
pop4959 6d991dac64 Refactor selection 2021-01-23 22:48:19 -08:00
pop4959 f707e3b258 Update Pl3xMap integration 2021-01-23 00:33:50 -08:00
pop4959 e7128d08a3 Add Pl3xMap integration 2021-01-22 22:47:40 -08:00
pop4959 ab7dca34dc Bump yarn version 2021-01-22 22:36:38 -08:00
Yey007 5e7e0607e3 Fix Sponge not running shutdown event (#65) 2021-01-21 17:49:59 -08:00
pop4959 e75c99bd58 New Crowdin updates (#63) 2021-01-19 21:53:00 -08:00
pop4959 80f380b085 Build against 1.16.5 for Fabric 2021-01-15 22:02:25 -08:00
pop4959 16f9aa6b32 New Crowdin updates (#61) 2021-01-15 21:40:44 -08:00
pop4959 1a42f99226 Crowdin configuration 2021-01-11 23:22:56 -08:00
pop4959 cf098ceb42 Update bStats 2021-01-11 22:47:38 -08:00
pop4959 cf893e5169 Add corners command 2021-01-11 22:34:14 -08:00
Andre_601 289c484806 Update German translation (#59) 2021-01-03 15:33:48 -08:00
pop4959 072fd2744a Automatic patch versioning 2021-01-03 00:06:16 -08:00
pop4959 5e34edc21b Update publications 2021-01-02 23:09:40 -08:00
pop4959 fec471848e Multi-platform support 2021-01-02 18:26:48 -08:00
ahdg b502eed50d Update Chinese translation (#57) 2020-11-06 20:49:53 -08:00
pop4959 969f87e72d Remove workaround for Tuinity delayed chunk unloading 2020-11-04 20:49:01 -08:00
pop4959 f72425036e Add support for exact shapes vs chunk aligned 2020-10-25 21:57:36 -07:00
Aleksandr 2555022a99 Update Russian translation (#52) 2020-10-24 14:14:06 -07:00
pop4959 687ba0672e Add confirmation for potentially destructive actions 2020-10-24 00:43:03 -07:00
pop4959 23639cc769 Fix incorrect time when cancelling saved task 2020-10-24 00:42:31 -07:00
pop4959 304e11162c Ensure iterators don't continue past maximum chunk count 2020-10-23 22:47:32 -07:00
pop4959 c543ab9b9c Reload command 2020-10-23 22:46:17 -07:00
xNeox 6281a074a7 Added Polish translation (#51) 2020-10-19 12:31:59 -07:00
Aleksandr d0ed0b244c Update Russian translation (#50) 2020-10-17 23:48:45 -07:00
pop4959 46f89d0ab5 Merge branch 'master' of https://github.com/pop4959/Chunky 2020-10-17 18:13:43 -07:00
pop4959 86d1350dfd Add command to select center from spawn 2020-10-17 18:13:29 -07:00
dependabot[bot] 3c8e8a8090 Bump junit from 4.12 to 4.13.1 (#49)
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-13 20:43:54 -07:00
pop4959 984dd5d9db Fix sync save on shutdown causing issues 2020-10-11 23:18:03 -07:00
pop4959 cc92958597 Revert "Improve shutdown by saving tasks sync (#40)"
This reverts commit f2f078d1e3.
2020-10-11 22:46:34 -07:00
pop4959 83f0e0affb Revert "Improve shutdown (#43)"
This reverts commit 1576da7e0e.
2020-10-11 22:45:14 -07:00
Lukas Rieger 1ea2855e0f Fix some typos and grammar for the German translation (#47) 2020-10-09 13:26:12 -07:00
Alexander 110b0986f6 Update Bulgarian translation (#46) 2020-10-09 13:23:31 -07:00
Aleksandr 89da2e63b4 Update Russian translation (#45) 2020-10-09 13:20:50 -07:00
Andre_601 5a181d8347 Update German translation (#44) 2020-10-08 14:56:32 -07:00
pop4959 8c96948793 Bump version 2020-10-08 14:54:24 -07:00
pop4959 1576da7e0e Improve shutdown (#43) 2020-10-07 23:49:09 -07:00
Dylan Xaldin 309d0a3d40 Provide a better message if trying to continue with no tasks (#42)
This changes the continue command to properly inform the user if there were no tasks to continue, rather than returning nothing and leaving the user with no command feedback whatsoever.
2020-10-04 22:56:15 -07:00
pop4959 f2f078d1e3 Improve shutdown by saving tasks sync (#40) 2020-10-03 00:43:34 -07:00
Dylan Xaldin fc86380b61 Provide a better message if trying to pause with no tasks (#41)
This changes the pause command to properly inform the user if there were no tasks to pause, rather than returning nothing and leaving the user with no command feedback whatsoever.
2020-10-03 00:42:31 -07:00
Dylan Xaldin 1d13e2c12e Provide a better message if trying to cancel with no tasks (#39)
This changes the cancel command to properly inform the user if any tasks were actually cancelled, rather than just always saying that all tasks were cancelled.
2020-10-02 23:40:54 -07:00
pop4959 bc1f703021 Show radius in start message properly (#38) 2020-10-02 21:00:30 -07:00
Dylan Xaldin dbe7ce23bb Change "[Chunky]" prefix into a separate translation key (#37)
This makes the [Chunky] prefix that appears before many of the messages become its own translation key, which then gets used as an argument inside all of the translation keys that use it.
2020-10-02 19:42:53 -07:00
pop4959 f936a6d105 Bump version 2020-10-02 19:41:48 -07:00
ahdg 74e626207f Update Chinese translation (#36) 2020-10-02 00:02:51 -07:00
pop4959 4a301f6a9f Bump version 2020-10-02 00:02:01 -07:00
pop4959 33a03c2b7b Make sure markers are loaded when BlueMap is loaded 2020-09-26 23:27:02 -07:00
pop4959 3f8d2dff7b Ensure worldborder command sets both x and z to the same value 2020-09-26 21:48:18 -07:00
pop4959 111f268bfb Use consistent variable naming for x/z 2020-09-26 21:47:11 -07:00
pop4959 287b76f59a Add label to bluemap markers correctly 2020-09-25 23:46:31 -07:00
pop4959 7263c1b1b9 Fix dynmap colors 2020-09-25 22:27:59 -07:00
pop4959 b568ad04ea Fix issue with reloading dynmap markers 2020-09-25 21:37:54 -07:00
pop4959 d6832bc242 Clean up BlueMap implementation some more 2020-09-25 11:11:03 -07:00
pop4959 fa33e6acf3 Add 1.15.0 version constant 2020-09-24 21:28:57 -07:00
pop4959 1d704f6eaf Fix regression introduced in refactor 2020-09-24 21:27:31 -07:00
Andre_601 10a9974def Update de.json (#35) 2020-09-24 20:24:09 -07:00
pop4959 ff1bc37845 Refactor classes and integrations 2020-09-23 22:31:30 -07:00
pop4959 ddedd9e62c Angle should be double 2020-09-23 18:26:54 -07:00
pop4959 b4e7bd20e3 Deploy to CodeMC 2020-09-23 18:17:52 -07:00
pop4959 4328673ecc Add map integration and ellipse calculation 2020-09-23 18:04:38 -07:00
pop4959 a4912c75ed WorldBorder support for worldborder command 2020-09-21 21:41:07 -07:00
pop4959 212d63c56e More shape code refactor 2020-09-21 20:26:41 -07:00
pop4959 080c7176cf Add Spanish translation (#33)
* Add files via upload

* Update es.json

Co-authored-by: pop4959 <pop4959@gmail.com>
2020-09-21 14:06:52 -07:00
pop4959 c742151997 Bump version 2020-09-21 14:06:35 -07:00
pop4959 d8c1de0f1a Update es.json 2020-09-21 14:01:14 -07:00
benjamonSRL 3fb43d4462 Add files via upload 2020-09-21 17:43:20 -03:00
Aleksandr 19c10c781d Update ru.json (#32) 2020-09-20 01:09:32 -07:00
pop4959 118184c2f8 Refactor shape code 2020-09-18 13:56:38 -07:00
pop4959 02713d65e4 Refactor and add support for rectangle/oval shapes 2020-09-12 22:03:47 -07:00
Aleksandr ff9e0452e9 Update Russian translation (#31) 2020-09-11 10:56:07 -07:00
pop4959 ced751f5f0 Update targeted API version to 1.16.3 2020-09-11 00:19:59 -07:00
pop4959 e4b2ac40fd Reword worldborder command help 2020-08-30 15:06:42 -07:00
pop4959 9216f49897 Fix tab completion NPE 2020-08-30 15:01:14 -07:00
pop4959 a3bd27fc9f Refactor commands (#26) 2020-08-30 01:20:24 -07:00
pop4959 506dba87bf Make sure bounds are checked correctly 2020-08-29 12:36:42 -07:00
pop4959 b21a214535 Allow double values in center command 2020-08-29 12:26:57 -07:00
pop4959 8a52854fb9 AbstractShape constructor should be protected 2020-08-26 11:23:54 -07:00
Alexander 284cf0edd3 Update Bulgarian translation (#23) 2020-08-25 11:34:49 -07:00
pop4959 9ceeb7b753 Synchronize access to config to avoid race conditions 2020-08-25 11:31:50 -07:00
pop4959 015f9fe486 Continue on restart option 2020-08-25 11:08:38 -07:00
pop4959 41093c39eb Shape support (#22) 2020-08-24 15:39:10 -07:00
pop4959 5e709ac872 Bump version 2020-08-22 10:14:27 -07:00
montlikadani e6c773de1c Add "cancel" to tab completions, and only add custom charts when bStats is enabled (#19) 2020-08-22 10:06:35 -07:00
Alexander 428e970a2a Add Bulgarian translation (#17) 2020-08-21 10:19:56 -07:00
pop4959 3b01fef88f Bump version 2020-08-20 21:10:59 -07:00
ahdg 98e7cc7793 Update zh_CN.json (#16) 2020-08-20 21:10:10 -07:00
pop4959 0a88ead2ab Make sure center and radius are inside world limit 2020-08-19 18:48:45 -07:00
pop4959 d0e7c09ac6 Fix integer overflow issues 2020-08-19 18:26:40 -07:00
pop4959 49ab982c0f Bump version 2020-08-19 16:34:41 -07:00
pop4959 f433c4b9f1 More descriptive thread name 2020-08-19 16:34:07 -07:00
pop4959 4f98a5085f GenTask -> GenerationTask 2020-08-17 23:49:27 -07:00
pop4959 f01a25919d Add missing tab completion for pattern 2020-08-17 17:58:20 -07:00
pop4959 5fa68e0537 Iterator improvements 2020-08-17 17:51:51 -07:00
pop4959 dba5f4301e Remove unused fields and add language chart to metrics 2020-08-17 09:55:38 -07:00
194 changed files with 12159 additions and 1790 deletions

No files matched your search

+5
View File
@@ -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>
+1
View File
@@ -0,0 +1 @@
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -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
+47
View File
@@ -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"
+39
View File
@@ -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
View File
@@ -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
+17
View File
@@ -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>
+24 -4
View File
@@ -1,6 +1,26 @@
## Chunky
Pre-generates chunks, quickly, efficiently, and safely
# Chunky
[Spigot Resource Page](https://www.spigotmc.org/resources/chunky.81534/)
![chunkylogo](https://user-images.githubusercontent.com/17698576/171119146-122497e4-7527-438b-8f8e-c1c04c2841b9.jpg)
[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
[![yklogo](https://www.yourkit.com/images/yklogo.png)](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.
+102
View File
@@ -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]
}
+45
View File
@@ -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();
}
}
+9
View File
@@ -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
+105
View File
@@ -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
+26
View File
@@ -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